Geant4であるプログラムを書いていたところ、segmentation faultとGDBの出力結果ではまりました。
以下のコードをstepping actionの中で使おうとしたところ、
void SteppingAction::UserSteppingAction(const G4Step* step) { RunAction* action = (RunAction*)G4RunManager::GetRunManager()->GetUserRunAction(); G4Track* track = step->GetTrack(); G4StepPoint* prePoint = step->GetPreStepPoint(); G4StepPoint* postPoint= step->GetPostStepPoint(); G4VPhysicalVolume* prePV = prePoint->GetPhysicalVolume(); G4VPhysicalVolume* postPV= postPoint->GetPhysicalVolume(); G4OpBoundaryProcessStatus boundaryStatus = Undefined; static G4OpBoundaryProcess* boundaryProcess = 0; //find the boundary process only once if(!boundaryProcess){ G4ProcessManager* pm = step->GetTrack()->GetDefinition()->GetProcessManager(); G4int nprocesses = pm->GetProcessListLength(); G4ProcessVector* pv = pm->GetProcessList(); for(G4int i = 0; i < nprocesses; i++){ if((*pv)[i]->GetProcessName() == "OpBoundary"){ boundaryProcess = (G4OpBoundaryProcess*)(*pv)[i]; break; } // if } // i } // if G4double stepLength = step->GetStepLength(); G4String preVolumeName = prePoint->GetPhysicalVolume()->GetName(); G4String postVolumeName = postPoint->GetPhysicalVolume()->GetName(); G4String processName = postPoint->GetProcessDefinedStep()->GetProcessName();
なぜかsegmentation faultでこける場合がありました。GDBを走らせると、seg faultの原因箇所は
if((*pv)[i]->GetProcessName() == "OpBoundary"){
ということだったのでGetProcessName()の結果をG4Stringに詰め直したり、stderrに吐いてみたりと色々試したのですが、どうやらGetProcessName()の結果自体は正しい値の入ったG4Stringを返していました。G4Stringはstd::stringの派生classなので、変なbugが残っているはずもなく、またMacでもLinuxでも同じ死に方をします。
Program received signal EXC_BAD_ACCESS, Could not access memory. Reason: KERN_PROTECTION_FAILURE at address: 0x00000024 0x928c3401 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string () (gdb) bt #0 0x928c3401 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string () #1 0x00008845 in G4VProcess::GetProcessName () at G4VProcess.hh:51 #2 0x00008845 in SteppingAction::UserSteppingAction (this=0x3285700, step=0x324b850) at src/SteppingAction.cc:42 #3 0x001b9bc6 in G4SteppingManager::Stepping () at basic_string.h:227 #4 0x001c0f22 in G4TrackingManager::ProcessOneTrack () at basic_string.h:227 #5 0x0019b6b4 in G4EventManager::DoProcessing () at basic_string.h:227 #6 0x000f0582 in G4RunManager::DoEventLoop () at basic_string.h:227 #7 0x000f17e1 in G4RunManager::BeamOn () at basic_string.h:227 #8 0x000f4155 in G4RunMessenger::SetNewValue () at basic_string.h:227 #9 0x0031d5c1 in G4UIcommand::DoIt () at basic_string.h:227 #10 0x003248a6 in G4UImanager::ApplyCommand () at basic_string.h:227 #11 0x000e1421 in G4UIterminal::ExecuteCommand () at basic_string.h:227 #12 0x000e29f3 in G4UIterminal::SessionStart () at basic_string.h:227 #13 0x00002d30 in main () at MinimumTest1.cc:54
のようなGDBの出力結果です。src/SteppingAction.cc:42の箇所が、
if((*pv)[i]->GetProcessName() == "OpBoundary"){
に該当しています。
半日以上悩んだものの解決方法が見えず、まずは他の場所の修正を先にやろうと他の箇所のNULL checkをちゃんとするよう、以下のように変更にしたところ、
G4double stepLength = step->GetStepLength(); G4String preVolumeName = prePoint->GetPhysicalVolume()->GetName(); G4String postVolumeName = postPoint->GetPhysicalVolume() ? postPoint->GetPhysicalVolume()->GetName() : ""; G4String processName = postPoint->GetProcessDefinedStep() ? postPoint->GetProcessDefinedStep()->GetProcessName() : "";
なぜかseg faultが発生しなくなりました。
実はこけていた場所はGetProcessName()のところではなく、
postPoint->GetProcessDefinedStep()
の返り値がNULLだったものにGetProcessName()でaccessしていたからという落ち。
何行も後ろのbugの結果を、GDBが実際とは異なる行番号で報告してくれる場合があるわけで、これはdebug時に非常に混乱します。ただし、MacでもLinuxでも同じ報告をしてくれたので、もしかしたら自分の間違いがどこかにあるのかもしれません。