I am writing a file I can call to generate a series of plots from a ROOT file generated with GEANT4.
The code consists of a main function addmrdplots() which opens a TTree, and then calls a series of other functions, each of which generates a particular plot. Individually they all compile, generate, and exit without issue. But when two particular functions are both called, the latter will generate a segmentation fault. This only happens when the macros are called in one particular order.
I have tried calling the functions with valgrind, but perhaps am not using the right call syntax. I used:
valgrind --tool=memcheck --leak-check=full --log-file="rooterror.out" -v root -q -b ~/LinuxSystemFiles/WChSandBox/WChSandBox_v1/callcode.cxx
where callcode is a simple macro consisting of just:
{gROOT->ProcessLine(".L ~/LinuxSystemFiles/WChSandBox/WChSandBox_v1/addmrdplots.cxx++g");
gROOT->ProcessLine("addmrdplots();");}
This call opens ROOT, compiles the macro (generating a segfault) and then exits, but the valgrind output seems to be empty.
I have tried stripping down the code to a more simplified version to post, however whenever I do so it always runs without issue. Is there any chance someone can take a look and point out what potential issues there may be? No doubt the code is an absolute Rube Goldberg machine, so I’m open to tips.
Function 1: [code]
void EnergyDepositioninLayers(TTree* mrdtree){
// Produce plot of energy deposition vs depth, with bins corresponding to MRD layers
// =================================================================================
// Define canvas
TCanvas edeposCanv = TCanvas(“edeposCanv”,“Title”);
// Find num entries in tree
Int_t numentries = mrdtree->GetEntries();
// Get branch for num hits this entry
TBranch* numhitsbranch = mrdtree->GetBranch(“hitnum”);
Int_t arraysize = 0;
numhitsbranch->SetAddress(&arraysize);
// Get branch for z positions of all hits this entry
TBranch* zbranch = mrdtree->GetBranch(“mrdhit_z”);
zbranch->SetAutoDelete(kTRUE);
Double_t* hitzvals;
Double_t zval = 0; // one element
// Get branch for energy deposited by hit this entry
TBranch* edepbranch = mrdtree->GetBranch(“mrdhit_edep”);
edepbranch->SetAutoDelete(kTRUE);
Double_t* hitedeps;
Double_t edep = 0;
// Array to store total energy deposited in each layer
Double_t* edeps = new Double_t[mrdnumlayers];
// Array of layer numbers (graphs require both x and y arrays)
Double_t* layercentres = new Double_t[mrdnumlayers];
for(Int_t layer=1;layer<(mrdnumlayers+1);layer++){layercentres[layer-1]=(Double_t)layer;edeps[layer-1]=0.;}
// Loop over entries
for (Int_t i=0;i<numentries-1;i++){
numhitsbranch->GetEntry(i);
// check we have hits
if(arraysize!=0){
// retrieve hit positions
hitzvals = new Double_t[arraysize];
zbranch->SetAddress(hitzvals);
zbranch->GetEntry(i);
// retrieve hit energy depositions
hitedeps = new Double_t[arraysize];
edepbranch->SetAddress(hitedeps);
edepbranch->GetEntry(i);
//scan over hits, accumulating energy deposited in each layer
for(size_t j=0;j<arraysize;j++){
zval=hitzvals[j];
edep=hitedeps[j];
// determine which layer this hit is in
for(Int_t k=1;k<(mrdnumlayers+1);k++){
Double_t layerstart = mrdzstart+((mrdzlen/mrdnumlayers))*(k-1);
Double_t layerend = mrdzstart+((mrdzlen/mrdnumlayers))*k;
if(zval>=layerstart&&zval<layerend){edeps[k-1]+=edep;break;}
}
}
}
} edeposCanv.cd();
edeposCanv.Clear();
TGraph edepvsz = TGraph(mrdnumlayers,layercentres,edeps);
edepvsz.SetFillColor(40);
edepvsz.Draw(“AB”);
edepvsz.SetTitle(“Total Energy Deposited in each MRD Layer”);
edepvsz.GetXaxis()->SetTitle(“MRD Layer”);
edepvsz.GetXaxis()->SetRangeUser(0,mrdnumlayers+0.5);
edepvsz.GetXaxis()->CenterTitle(true);
edepvsz.GetXaxis()->SetNdivisions(15);
edepvsz.GetYaxis()->SetTitle(“Total Energy Deposit (keV)”);
edepvsz.GetYaxis()->CenterTitle(true);
edeposCanv.Modified();
edepvsz.Write();
edeposCanv.SaveAs(“edepvsz.png”);[/code]
You need to run valgrind on root.exe. Please pass --suppressions=$ROOTSYS/etc/valgrind-root.supp to valgrind.
Cheers, Axel.[/quote]
Axel - I gave the command I was using to try to run valgrind on root, but clearly it isn’t outputting anything (even without suppressions). What command should I be using to run valgrind?
[quote=“vvassilev”]Can we see a backtrace and the code of those functions?
–Vassil[/quote]
Sorry, working on formatting. Second function and backtrace will be posted in a moment. Quick responses guys!
function 2:
void EnergyDepositionInPaddles(TTree* mrdtree){
// Distributions of E Deposition in each Panel, bins are Scintillator Paddles
// ============================================================================
// Create canvas and divide into pads for each layer.
TCanvas edeposCanv2 = TCanvas("edeposCanv2","Title",8,59,1266,737);
edeposCanv2.Clear();
edeposCanv2.Divide(4,3);
// create array of total energy depositions in each layer
Double_t* totedepinpanels = new Double_t[mrdnumlayers];
// set up aliases for paddle and panel number based on geant4 copy number
mrdtree->SetAlias("mrdhit_panelnum",Form("TMath::Floor(mrdhit_copynum/%d)",mrdpaddlesperpanel));
mrdtree->SetAlias("mrdhit_paddlenum",Form("mrdhit_copynum-(mrdhit_panelnum*%d)",mrdpaddlesperpanel));
// loop over all panels
for(Int_t panelnum=0;panelnum<mrdnumlayers;panelnum++){
edeposCanv2.cd(panelnum+1);
// draw all hits, using alias to cut hits in a given panel, weighting each by their energy deposition
mrdtree->Draw(Form("mrdhit_paddlenum>>edepinpanel%d(%d,1,%d)",panelnum,mrdpaddlesperpanel,mrdpaddlesperpanel+1),Form("mrdhit_edep*(mrdhit_panelnum==%d)",panelnum));
// retrieve the current pad
TCanvas* currentcanvas = (TCanvas*)edeposCanv2.GetPrimitive(Form("edeposCanv2_%d",panelnum+1));
// retrieve the generated histogram from that pad
TH1F* edepinpanel = (TH1F*)currentcanvas->GetPrimitive(Form("edepinpanel%d",panelnum));
// formatting
edepinpanel->SetTitle(Form("Energy Deposition in MRD Layer %d;Paddle Number;Total Energy Deposited",panelnum));
edepinpanel->GetXaxis()->CenterLabels();
// store to output file
edepinpanel->Write();
// integrate energy and add into total energy deposited in this panel
Double_t totedepinpanel = edepinpanel->Integral();
totedepinpanels[panelnum]=totedepinpanel;
}
// save a picture of all plots to file
edeposCanv2.cd();
edeposCanv2.Modified();
edeposCanv2.SaveAs("deposition in panels.png");
There was a crash.
This is the entire stack trace of all threads:
#0 0x00007f6b0955630c in __libc_waitpid (pid=10112, stat_loc=stat_loc
entry=0x7fffa6860fa0, options=options
entry=0) at …/sysdeps/unix/sysv/linux/waitpid.c:31 #1 0x00007f6b094e018b in do_system (line=) at …/sysdeps/posix/system.c:148 #2 0x00007f6b0a40c239 in TUnixSystem::StackTrace() () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #3 0x00007f6b0a40de5c in TUnixSystem::DispatchSignals(ESignals) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #4 #5 0x00007f6b0a3badab in TObjLink::TObjLink(TObject*, TObjLink*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #6 0x00007f6b0a3badea in TList::NewLink(TObject*, TObjLink*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #7 0x00007f6b0a3ba3f9 in TList::AddLast(TObject*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #8 0x00007f6b0a3b6151 in THashList::AddLast(TObject*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #9 0x00007f6b06f17f71 in TDirectoryFile::AppendKey(TKey*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libRIO.so #10 0x00007f6b06f233f7 in TKey::TKey(TObject const*, char const*, int, TDirectory*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libRIO.so #11 0x00007f6b06efffa5 in TFile::CreateKey(TDirectory*, TObject const*, char const*, int) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libRIO.so #12 0x00007f6b06f1b2f2 in TDirectoryFile::WriteTObject(TObject const*, char const*, char const*, int) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libRIO.so #13 0x00007f6b0a341901 in TObject::Write(char const*, int, int) const () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #14 0x00007f6b0198b6b0 in EnergyDepositionInPaddles (mrdtree=0x259f3e0) at /home/marc/LinuxSystemFiles/WChSandBox/WChSandBox_v1/addmrdplots.cxx:502 #15 0x00007f6b0198957e in addmrdplots (mrdinfile=0x1f742c8 “MRDEvents2.root”, fullinfile=0x1c8d1d8 “FullEvent2.root”) at /home/marc/LinuxSystemFiles/WChSandBox/WChSandBox_v1/addmrdplots.cxx:84 #16 0x00007f6b0198ce20 in G__addmrdplots_cxx_ACLiC_dict__0_2099 (result7=0x7fffa6865190, funcname=0x24123a0 “”, libp=0x7fffa68651d0, hash=0) at /home/marc/LinuxSystemFiles/WChSandBox/WChSandBox_v1/addmrdplots_cxx_ACLiC_dict.cxx:162 #17 0x00007f6b085d276d in Cint::G__ExceptionWrapper(int ()(G__value, char const*, G__param*, int), G__value*, char*, G__param*, int) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCint.so #18 0x00007f6b0850e7a7 in G__execute_call () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCint.so #19 0x00007f6b0850eb65 in G__call_cppfunc () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCint.so #20 0x00007f6b085b2085 in G__interpret_func () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCint.so #21 0x00007f6b086204f5 in G__getfunction () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCint.so #22 0x00007f6b086281a6 in G__getitem () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCint.so #23 0x00007f6b0862d605 in G__getexpr () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCint.so #24 0x00007f6b084fbb5c in G__exec_statement () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCint.so #25 0x00007f6b085ba166 in G__exec_tempfile_core () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCint.so #26 0x00007f6b085bb64e in G__exec_tempfile_fp () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCint.so #27 0x00007f6b084ebc59 in G__process_cmd () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCint.so #28 0x00007f6b0a3dace0 in TCint::ProcessLine(char const*, TInterpreter::EErrorCode*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #29 0x00007f6b0a3b13c2 in TApplication::ProcessLine(char const*, bool, int*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #30 0x00007f6b09fa4549 in TRint::HandleTermInput() () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libRint.so #31 0x00007f6b0a40d645 in TUnixSystem::CheckDescriptors() () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #32 0x00007f6b0a40e14a in TUnixSystem::DispatchOneEvent(bool) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #33 0x00007f6b0a359906 in TSystem::InnerLoop() () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #34 0x00007f6b0a35a4b0 in TSystem::Run() () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #35 0x00007f6b0a3aff1f in TApplication::Run(bool) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #36 0x00007f6b09fa56d7 in TRint::Run(bool) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libRint.so #37 0x0000000000400fac in main ()
The lines below might hint at the cause of the crash.
If they do not help you then please submit a bug report at http://root.cern.ch/bugs. Please post the ENTIRE stack trace
from above as an attachment in addition to anything else
that might help us fixing this issue.
#5 0x00007f6b0a3badab in TObjLink::TObjLink(TObject*, TObjLink*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #6 0x00007f6b0a3badea in TList::NewLink(TObject*, TObjLink*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #7 0x00007f6b0a3ba3f9 in TList::AddLast(TObject*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #8 0x00007f6b0a3b6151 in THashList::AddLast(TObject*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #9 0x00007f6b06f17f71 in TDirectoryFile::AppendKey(TKey*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libRIO.so #10 0x00007f6b06f233f7 in TKey::TKey(TObject const*, char const*, int, TDirectory*) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libRIO.so #11 0x00007f6b06efffa5 in TFile::CreateKey(TDirectory*, TObject const*, char const*, int) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libRIO.so #12 0x00007f6b06f1b2f2 in TDirectoryFile::WriteTObject(TObject const*, char const*, char const*, int) () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libRIO.so #13 0x00007f6b0a341901 in TObject::Write(char const*, int, int) const () from /home/marc/LinuxSystemFiles/ROOT/root-5.34.09/install/lib/libCore.so #14 0x00007f6b0198b6b0 in EnergyDepositionInPaddles (mrdtree=0x259f3e0) at /home/marc/LinuxSystemFiles/WChSandBox/WChSandBox_v1/addmrdplots.cxx:502 #15 0x00007f6b0198957e in addmrdplots (mrdinfile=0x1f742c8 “MRDEvents2.root”, fullinfile=0x1c8d1d8 “FullEvent2.root”) at /home/marc/LinuxSystemFiles/WChSandBox/WChSandBox_v1/addmrdplots.cxx:84
I noticed that you are using ROOT 5. If this is a crash coming from CINT, I’d advice you to try the same code with ROOT6. CINT is not maintained anymore.
–Vassil
The function seems be:void EnergyDepositionInPaddles(TTree* mrdtree)
andvoid EnergyDepositioninLayers(TTree* mrdtree)Unless you are passing two distinct TTree object, this two functions are not independent and will ‘trample’ on each other.
We would need a full example to understand it fully.
And I said and meant that valgrind needs to be run on “root.exe” We have both “root” and “root.exe”. Try it
That said, Philippe is guiding you well - valgrind will provide less new info than just following Philippe’s recommendations.
Cheers, Axel.[/quote]
OK, firstly, the timeout on this board is too short; that’s the 3’rd message I’ve lost when trying to post because I had been logged out.
Anyway, with root.exe valgrind works as expected - thanks Axel, I completely missed the distinction before.
The corresponding output is actually very illuminating - a write off the end of allocated memory. The write comes from:
mrdtree->Draw(Form("mrdhit_paddlenum>>edepinpanel%d(%d,1,%d)",panelnum,mrdpaddlesperpanel,mrdpaddlesperpanel+1),Form("mrdhit_edep*(mrdhit_panelnum==%d)",panelnum));
whereas the “corresponding allocation” comes from:
hitedeps = new Double_t[arraysize];
Which is the allocation of the dynamic array at the address I had set for branch mrdhit_edep. This makes some sense now - I guess the Draw() function is implicitly getting entries, but is still using the memory space I allocated in the last function for the mrdhit_edep branch - except that allocated space isn’t enough for all entries.
My last question then is, what is the proper fix for this? Can I un-set the address for that branch and make ROOT handle memory allocation implicitly, as before the SetBranchAddress call?
Or, as Philippe has suggested, not pass the same pointer to both functions. In that case, how should I go about passing the same tree to two functions?
[quote=“vvassilev”]I noticed that you are using ROOT 5. If this is a crash coming from CINT, I’d advice you to try the same code with ROOT6. CINT is not maintained anymore.
–Vassil[/quote]
Cheers for the note Vassil. Unfortunately I’m somewhat tied to ROOT 5 at the moment as I’ve been given GEANT4 code that requires an old version of GEANT4, which in turn doesn’t play nice with ROOT 6. (Specifically, it seems ROOT6 requires C++11, while the old GEANT code requires C++98 standard).
I may install ROOT6 for reference in this sort of situation in the future, though.
[quote=“Pepe Le Pew”]Replace all “Form” calls with “TString::Format”.
BTW. You dynamically allocate arrays, even inside of the event loop, but you never delete them (“memory leaks”).[/quote]
Hi Pepe,
thanks for the tip, I’ll use TString::Format if that’s preferred. Any particular reason?
The posted code was trimmed a bit, but I must admit I’m not completely clear on the deleting requirements for branch arrays of this type. My understanding with kAutoDelete is that it will automatically delete the array when getting a new entry. But I’ve also read that even with this as false, getting a new entry will re-use the same memory. Presumably if the size is dynamic, though, true is necessary. As this delete occurs on each read, that would suggest I need one final manual call to ‘delete[] hitzvals and delete[] hitedeps’ at the end of my function - but Valgrind marks this as a double delete… ?
if(arraysize!=0){
// ...
delete[] hitedeps;
delete[] hitzvals;
}
Note: You should also delete[] “edeps”, “layercentres” and “totedepinpanels”, once they are not used any more (and get rid of “SetAutoDelete” as it seems to me that it’s not doing what you think it is).
[quote=“Pepe Le Pew”] if(arraysize!=0){
// ...
delete[] hitedeps;
delete[] hitzvals;
}
Note: You should also delete[] “edeps”, “layercentres” and “totedepinpanels”, once they are not used any more (and get rid of “SetAutoDelete” as it seems to me that it’s not doing what you think it is).[/quote]
Yep, those are deleted also, just trimmed for clarity.
If SetAutoDelete is not doing what I think it is, perhaps you could explain how it actually works instead?
I’m now having further issues on the same topic. I have a macro that loops over events in a tree, performs a Draw, does some checks on the produced histogram, and if necessary, then retrieves a branch for further analysis.
To retrieve the branch I need to use SetBranchAddress followed by GetEntry - but in the next loop/event, the Draw ROOT crashes unless sufficient memory has been allocated. So I need to manually read the number of events and allocate memory for the branch, even if I don’t actually want the branch data. I have tried ResetAddress on the branch, but that still leads to crashes - there must be some method to do this…
In addition, having read certain branches (like number of hits this entry) with GetEntry, the result gets overwritten by the Draw call! So I have to create a secondary variable, call GetEntry, then copy the value across before the Draw! Is this intended behaviour? Is it not possible to make ROOT use it’s own memory allocation for Draw calls?
Currently the branches only have ‘one’ address to which they are supposed to copy the data.
So indeed when you do: MyType *ptr = ...;
tree->SetBranchAddress(branchname,&ptr);
tree->GetEntry(specific_entry);
tree->Draw(); // Loop over all entryThe end result is that ptr will not points to the data of the last entry. To avoid this, you should do: MyType *ptr = ...;
tree->SetBranchAddress(branchname,&ptr);
tree->GetEntry(specific_entry);
tree->ResetBranchAddresses();
tree->Draw(); // Loop over all entry
// and if needed/wanted
tree->SetBranchAddress(branchname,&ptr);
Currently the branches only have ‘one’ address to which they are supposed to copy the data.
So indeed when you do: MyType *ptr = ...;
tree->SetBranchAddress(branchname,&ptr);
tree->GetEntry(specific_entry);
tree->Draw(); // Loop over all entryThe end result is that ptr will not points to the data of the last entry. To avoid this, you should do: MyType *ptr = ...;
tree->SetBranchAddress(branchname,&ptr);
tree->GetEntry(specific_entry);
tree->ResetBranchAddresses();
tree->Draw(); // Loop over all entry
// and if needed/wanted
tree->SetBranchAddress(branchname,&ptr);
Cheers,
Philippe.[/quote]
So ResetAddress() is what I’m after! I don’t know why my previous attempts to use it appeared to give segmentation faults. Everything is now working fine, Valgrind reporting no errors. Hurray!
Thanks for the help!