TChain of non-identical TTrees

Rooters,

This question is motivated out of a recurring seg-fault error that I have been dealing with, but that would be too much of a mess to actually post and ask for help debugging. So, I only ask for some education as to the inner-workings of TChain so that I can fix it myself.

Here is the situation:

Ntuple 1: NT1.root
NT1.root contains a TTree “NTree” which contains the following branches: [A, B, C, D]

Ntuple 2: NT2.root
NT2.root contains a TTree “NTree” wich contains these following branches: [A, C, D, E]

To Create a TChain for my analysis I use the following:

TChain DataChain = new TChain("NTree");
DataChain->Add("NT1.root");
DataChain->Add("NT2.root");

I have subsequently set up a NTree Class using MakeClass() that initializes branches [A, B, C, D, E];

Therefore, in my analysis function, I fill the branches via the following:

NTree(DataChain)->A->GetEntry(i);  //Works All the time
NTree(DataChain)->B->GetEntry(i);  //Causes Segfault when NT2.root is open
NTree(DataChain)->C->GetEntry(i);  //Works All the time
NTree(DataChain)->D->GetEntry(i);  //Works All the time
NTree(DataChain)->E->GetEntry(i);  //Causes Segfault when NT1.root is open

Thus, my question is the following, how do I make the above GetEntry calls robust to this? I have tried a few methods ( i.e. if(B != NULL){…Get Entry…} ) but none of them seem to work.

Thanks for your help and patience with me.
~Austin

Hi,

I am not sure how much you modified and/or are using the MakeClass skeleton. However testing for null should have worked. I.e you have the following://NTree.h ... TypeOfThingInB *B; .... TBranch *b_B; .... fChain->SetBranchAddress("B", &B, &b_B); ....

[code]//NTree.cxx

void NTree::Loop()
{
if (fChain == 0) return;
Long64_t nentries = fChain->GetEntriesFast();
Long64_t nbytes = 0, nb = 0;
for (Long64_t jentry=0; jentry<nentries;jentry++) {
Long64_t ientry = LoadTree(jentry);
if (ientry < 0) break;

if (b_B) {
b_B->GetEntry(ientry);
}
}
}[/code]

Cheers,
Philippe.

Philippe,

Thanks for the reply!

Sorry, I should have been more specific. This ( if(b_A) {…} ) works properly so long as I run on each file separately, when they aren’t chained. I think I tracked down my problem to somewhere else though. It appears that the Branch->GetEntry(entry) function is working fine, but the seg fault is actually occurring in my LoadTree call somewhere in here: Long64_t centry = fChain->LoadTree(entry); (according to Xcode) Which was somewhat obfuscated by my prior use of Chain->GetEntry(event).

I am currently calling the MakeClass produced class “CollectionTree” in my analysis software and not actually using the loop() function. In fact, its a tad bit more complicated as I am really calling my NtupleInterfaceClass that inherits from “CollectionTree.”

So it looks something like the following:[code]

NTInterfaceClass::NTInterfaceClass(TTree* Tree) : SYNTClass_Version_16(Tree){
nentries = Tree->GetEntries();
printf(“INFO: This dataset contains %lld events.\n”,nentries);
}

NTInterfaceClass::~NTInterfaceClass(){}

Int_t NTInterfaceClass::GetEntrySubSet(Long64_t jentry){

Long64_t event = (Long64_t)LoadTree(jentry);  //LoadTree Call that dies !!!
if (event < 0) return 0;

Int_t nb = 0;

nb += b_RunNumber->GetEntry(event);
nb += b_EventNumber->GetEntry(event);
//nb += b_StreamESD_ref->GetEntry(event);
//nb += b_Stream1_ref->GetEntry(event);
//nb += b_Token->GetEntry(event);

   .
   .
   .

}
[/code]

Where SYNTClass_Version_16 is generated by MakeClass, and contains the default LoadTree definition:

Long64_t SYNTClass_Version_16::LoadTree(Long64_t entry)
{
// Set the environment to read one entry
   if (!fChain) return -5;
   Long64_t centry = fChain->LoadTree(entry);   //  !!! SegFault in this function call !!!
   if (centry < 0) return centry;
   if (!fChain->InheritsFrom(TChain::Class()))  return centry;
   TChain *chain = (TChain*)fChain;
   if (chain->GetTreeNumber() != fCurrent) {
      fCurrent = chain->GetTreeNumber();
      Notify();
   }
   return centry;
}

I know the .root input files are not corrupted as they run perfectly well, one at a time. So I am lead to believe there is some file I/O problems occurring that I am not aware of.

Thanks for your time and patience with me,
~Austin

Xcode’s vague stack trace… if relevant

#0	0x1000b1777 in TClass::GetClass
#1	0x101094bbf in TStreamerInfo::BuildCheck
#2	0x10106b39a in TFile::ReadStreamerInfo
#3	0x10106df79 in TFile::Init
#4	0x10106e7a8 in TFile::TFile
#5	0x10107112a in TFile::Open
#6	0x101c6ec22 in TChain::LoadTree
#7	0x100049e6d in SYNTClass_Version_16::LoadTree at SYNTClass_Version_16.h:1359
#8	0x100001622 in NTInterfaceClass::GetEntrySubSet at NTInterfaceClass.cxx:28
#9	0x100030b1e in main at MuonTrigMatch.cpp:142

[quote]Xcode’s vague stack trace.[/quote]That code should not (can not) crash in normal condition. Hence I suspect a memory overwrite.

[quote] works properly so long as I run on each file separately, when they aren’t chained. [/quote]Most likely one of the array size(s) select by MakeClass (to fit the data of the first file) is not large enough to fit the data of the subsequent file. Rather than MakeClass you might want to try MakeProxy which does not suffer from this limitation. Alternatively, you need to find a way to get the array correctly sized.

Cheers,
Philippe.

Thanks a ton! I will look into MakeProxy… however, for my education, how would I go about resizing the arrays defined by MakeClass manually?

~Austin

Hi,

You would have to edit by hand the header file generated by MakeClass.

Philippe.

[quote=“pcanal”][quote]Xcode’s vague stack trace.[/quote]That code should not (can not) crash in normal condition. Hence I suspect a memory overwrite.

[quote] works properly so long as I run on each file separately, when they aren’t chained. [/quote]Most likely one of the array size(s) select by MakeClass (to fit the data of the first file) is not large enough to fit the data of the subsequent file. Rather than MakeClass you might want to try MakeProxy which does not suffer from this limitation. Alternatively, you need to find a way to get the array correctly sized.
[/quote]

Would this memory overwrite occur even on the first iteration through the loop? As it happens: [code]
//====== fails on very first iteration (jentry=0) ====== //
TChain * DataChain = new TChain(“CollectionTree”,"");
DataChain->Add("/Volumes/Research/Data/ … /*00246.root"); // 1 file
DataChain->Add("/Volumes/Research/Data/ …/*00245.root"); // 1 file

//====== Runs Fine ====== //
TChain * DataChain = new TChain(“CollectionTree”,"");
DataChain->Add("/Volumes/Research/Data/ … /*00246.root"); // 1 file

//====== Runs Fine ====== //
TChain * DataChain = new TChain(“CollectionTree”,"");
DataChain->Add("/Volumes/Research/Data/ … /*00245.root"); // 1 file
[/code]

Also, I did a diff between the output of MakeClass for both files, and they are identical including array sizes, save for the File1::function() — File2::function(), which is expected. So I don’t know how changing array sizes would help.

Sorry if you feel like you are attempting to explain the color blue to the blind…

Hi,

[quote]Would this memory overwrite occur even on the first iteration through the loop?[/quote]It would be very unlikely indeed. Is there any std::vector in your TTree? Either way, if you are running on Linux, I recommend that you re-run your failing case using valgrind (valgrind root.exe etc …)

Cheers,
Philippe.

[quote=“pcanal”]Hi,

[quote]Would this memory overwrite occur even on the first iteration through the loop?[/quote]It would be very unlikely indeed. Is there any std::vector in your TTree? Either way, if you are running on Linux, I recommend that you re-run your failing case using valgrind (valgrind root.exe etc …)
[/quote]

  1. Yes, I have many, ~many~ std::vector branches, however, they are declared as vector< [DataType] > after declaring “using namespace std;” at the start of the MakeClass header file… too lazy? :unamused:

  2. Valgrind lovingly bestows the following:

--30745-- /Users/atb03a/Research/root/cint/cint/stl/vectorbool.dll:
--30745-- dSYM directory is missing; consider using --dsymutil=yes
--30745-- /Users/atb03a/Research/root/cint/cint/stl/vector.dll:
--30745-- dSYM directory is missing; consider using --dsymutil=yes
--30745-- /Users/atb03a/Research/root/lib/libvectorDict.so:
--30745-- dSYM directory is missing; consider using --dsymutil=yes
==30745== Invalid read of size 8
==30745==    at 0x1000D1771: TClass::GetClass(char const*, bool, bool) (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x1010B4BBE: TStreamerInfo::BuildCheck() (in /Users/atb03a/Research/root/lib/libRIO.so)
==30745==    by 0x10108B399: TFile::ReadStreamerInfo() (in /Users/atb03a/Research/root/lib/libRIO.so)
==30745==    by 0x10108DF78: TFile::Init(bool) (in /Users/atb03a/Research/root/lib/libRIO.so)
==30745==    by 0x10108E7A7: TFile::TFile(char const*, char const*, char const*, int) (in /Users/atb03a/Research/root/lib/libRIO.so)
==30745==    by 0x101091129: TFile::Open(char const*, char const*, char const*, int, int) (in /Users/atb03a/Research/root/lib/libRIO.so)
==30745==    by 0x101C8EC21: TChain::LoadTree(long long) (in /Users/atb03a/Research/root/lib/libTree.so)
==30745==    by 0x10003FE08: CollectionTree::LoadTree(long long) (in ./MuonTrigMatch)
==30745==    by 0x10000183D: NTInterfaceClass::GetEntrySubSet(long long) (in ./MuonTrigMatch)
==30745==    by 0x100030CBE: main (in ./MuonTrigMatch)
==30745==  Address 0x1089a8cb0 is 0 bytes inside a block of size 408 free'd
==30745==    at 0x10009FB11: operator delete(void*) (vg_replace_malloc.c:387)
==30745==    by 0x1000C5457: TStorage::ObjectDealloc(void*) (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x1001A1632: TClass::~TClass() (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x1000D0464: TClass::ForceReload(TClass*) (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x1000D1A9F: TClass::GetClass(char const*, bool, bool) (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x10018E77F: TCint::UpdateClassInfoWork(char const*, long) (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x10018E9A1: TCint::UpdateClassInfo(char*, long) (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x100A6FD08: G__search_tagname (in /Users/atb03a/Research/root/lib/libCint.so)
==30745==    by 0x1009DFFFB: G__get_linked_tagnum (in /Users/atb03a/Research/root/lib/libCint.so)
==30745==    by 0x1009E0475: G__get_linked_tagnum_fwd (in /Users/atb03a/Research/root/lib/libCint.so)
==30745==    by 0x10B7A58C0: G__cpp_setup_tagtable (in /Users/atb03a/Research/root/cint/cint/stl/vector.dll)
==30745==    by 0x100A6885F: G__shl_load (in /Users/atb03a/Research/root/lib/libCint.so)
==30745== 
==30745== Invalid read of size 8
==30745==    at 0x100136904: TObject::GetName() const (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x1000D177C: TClass::GetClass(char const*, bool, bool) (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x1010B4BBE: TStreamerInfo::BuildCheck() (in /Users/atb03a/Research/root/lib/libRIO.so)
==30745==    by 0x10108B399: TFile::ReadStreamerInfo() (in /Users/atb03a/Research/root/lib/libRIO.so)
==30745==    by 0x10108DF78: TFile::Init(bool) (in /Users/atb03a/Research/root/lib/libRIO.so)
==30745==    by 0x10108E7A7: TFile::TFile(char const*, char const*, char const*, int) (in /Users/atb03a/Research/root/lib/libRIO.so)
==30745==    by 0x101091129: TFile::Open(char const*, char const*, char const*, int, int) (in /Users/atb03a/Research/root/lib/libRIO.so)
==30745==    by 0x101C8EC21: TChain::LoadTree(long long) (in /Users/atb03a/Research/root/lib/libTree.so)
==30745==    by 0x10003FE08: CollectionTree::LoadTree(long long) (in ./MuonTrigMatch)
==30745==    by 0x10000183D: NTInterfaceClass::GetEntrySubSet(long long) (in ./MuonTrigMatch)
==30745==    by 0x100030CBE: main (in ./MuonTrigMatch)
==30745==  Address 0x1089a8cb0 is 0 bytes inside a block of size 408 free'd
==30745==    at 0x10009FB11: operator delete(void*) (vg_replace_malloc.c:387)
==30745==    by 0x1000C5457: TStorage::ObjectDealloc(void*) (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x1001A1632: TClass::~TClass() (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x1000D0464: TClass::ForceReload(TClass*) (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x1000D1A9F: TClass::GetClass(char const*, bool, bool) (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x10018E77F: TCint::UpdateClassInfoWork(char const*, long) (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x10018E9A1: TCint::UpdateClassInfo(char*, long) (in /Users/atb03a/Research/root/lib/libCore.so)
==30745==    by 0x100A6FD08: G__search_tagname (in /Users/atb03a/Research/root/lib/libCint.so)
==30745==    by 0x1009DFFFB: G__get_linked_tagnum (in /Users/atb03a/Research/root/lib/libCint.so)
==30745==    by 0x1009E0475: G__get_linked_tagnum_fwd (in /Users/atb03a/Research/root/lib/libCint.so)
==30745==    by 0x10B7A58C0: G__cpp_setup_tagtable (in /Users/atb03a/Research/root/cint/cint/stl/vector.dll)
==30745==    by 0x100A6885F: G__shl_load (in /Users/atb03a/Research/root/lib/libCint.so)
==30745== 
==30745== Conditional jump or move depends on uninitialised value(s)
==30745==    at 0x10003FE8D: CollectionTree::LoadTree(long long) (in ./MuonTrigMatch)
==30745==    by 0x10000183D: NTInterfaceClass::GetEntrySubSet(long long) (in ./MuonTrigMatch)
==30745==    by 0x100030CBE: main (in ./MuonTrigMatch)
==30745== 

Hi,

MakeClass does not like any object (including std::vector). If you have only std::vector, the problem should be solved by removing SetMakeClass(1) from your MakeClass skeleton. If you have both, please use MakeProxy.

Cheers,
Philippe.

So, I finally tracked down what was happening after an afternoon filled with changing comment blocks and print statements… and frankly the result is ridiculously confusing. According to my extremely cut down code: [code]
using namespace std;

Int_t main (int argc, const char * argv[]) {

TChain * DataChain = new TChain("CollectionTree","");//Input Data
DataChain->Add("/Volumes/Research/Data/r16/.../*.root");
DataChain->Add("/Volumes/Research/Data/r16/.../*.root");
DataChain->Add("/Volumes/Research/Data/r16/.../*.root");
DataChain->Add("/Volumes/Research/Data/r16/.../*.root");
	
NTInterfaceClass * data = new NTInterfaceClass(DataChain);   //Inherits from MakeClass

AnalysisCore * MuonTrigMatch = new AnalysisCore();	//Basic Analysis Utilities
MuonTrigMatch->nentries=data->nentries;			// and an event/processor monitor
MuonTrigMatch->TimerStart();					//

//== THIS CAUSES A SEGFAULT @ THE GetEntrySubSet CALL IN THE FOLLOWING LOOP ==//

TCanvas *c1 = new TCanvas("SelectionDisplay","Selection Table",200,10,500,950); 
delete c1;                                               

//=========================================================================//

    //Event Loop
for (Long64_t event = 0; event < data->nentries; event++) {
	
	data->GetEntrySubSet(event);			// Fills "data" with current event information
	
	MuonTrigMatch->Report(100000, event);     // report every 100K events 
	
}


return 1;

}

[/code]

When the above TCanvas constructor is commented out, the above code runs flawlessly, otherwise, as stated, the first data->GetEntrySubSet(event) call segfaults with the stack trace from post #3.

Background/Why is a TCanvas constructor there:
I have a class, that I usually instantiate where the TCanvas constructor is above, that: reads an XML document containing my event/muon/electron selection, applies the selection criteria to a cut method, and also writes a root macro that builds the selection into a user-readable table in a TCanvas and adds that macro to my output file so that I always have a record of what cuts were used in every output file. Unfortunately, it appears that somehow the TCanvas constructor is messing with my file I/O. Any Ideas, is this a bug, or is it operator error?

~Austin

Hi,

Given this and the valgrind output, I lean toward bug. Would you be able to provide a complete running example reproducing the problem?

Cheers,
Philippe.