Cloning Tree Many Times Gives Seg Fault

Hi,

I am trying to take an original TTree and then make multiple subsets of this TTree saved to different files. i.e. pseudocode of what I am trying to do:

Open file with original TTree
Extract original TTree
Make many empty clones of the original TTree
Loop over the original TTree{
Copy entry to each clone that passes some selection criteria.
}
Save out each cloned TTree which is now a subset of the original TTree, all to different files.

It works fine with up to 3 TTrees, but after that it gives a seg fault. e.g.

TFile inputfile("input.root");
TTree *inputTree;
inputfile.GetObject("treeName",inputTree);
const auto nentries = inputTree->GetEntries();

TFile file1("file1.root","recreate")
auto tree1 = inputTree->CloneTree(0);
TFile file2("file2.root","recreate")
auto tree2 = inputTree->CloneTree(0);
TFile file3("file3.root","recreate")
auto tree3 = inputTree->CloneTree(0);

std::vector<int>* someVectorInOriginalTTree;
inputtree->SetBranchAddress("someVectorInOriginalTTree",&someVectorInOriginalTTree);


for (auto i : ROOT::TSeqI(nentries) ){

inputTree->GetEntry(i);
if ( someVectorInOriginalTTree.at(0)==1){
tree1->Fill();
}
if ( someVectorInOriginalTTree.at(1)==1){
tree2->Fill();
}
if (someVectorInOriginalTTree.at(2)==1){
tree3->Fill();
}
}

Manages to run fine, but if I add the line “auto tree4 = inputTree->CloneTree(0);” (even without changing anything else so it shouldn’t actually even be doing anything), I get a segmentation fault, on the line “inputTree->GetEntry(i);”, even though nothing about inputTree should have changed.

Using root 6.14.04-x86_64-slc6-gcc62-opt has the problem above.
Using root 6.20.06-x86_64-centos7-gcc8-opt has the same problem, but for some reason it manages to do 6 clones before failing instead of 3 as in the 6.14.04 case.

If I remove the lines

TFile file1("file1.root","recreate")
TFile file2("file2.root","recreate")
TFile file3("file3.root","recreate")

then I am able to do auto tree4 = inputTree->CloneTree(0); without it seg faulting… However if I then add these lines after the loop, i.e.

TFile inputfile("input.root");
TTree *inputTree;
inputfile.GetObject("treeName",inputTree);
const auto nentries = inputTree->GetEntries();

auto tree1 = inputTree->CloneTree(0);
auto tree2 = inputTree->CloneTree(0);
auto tree3 = inputTree->CloneTree(0);

for (auto i : ROOT::TSeqI(nentries) ){

inputTree->GetEntry(i);
if ( [...some selection...]){
tree1->Fill();
}
if ( [...some other selection...]){
tree2->Fill();
}
if ( [...some other selection...]){
tree3->Fill();
}
}

TFile file1("file1.root","recreate")
TFile file2("file2.root","recreate")
TFile file3("file3.root","recreate")

It then seg faults again… And again it is seg faulting on the line

inputTree->GetEntry(i);

How can initiating some TFiles after a loop cause something within the loop to seg fault?

What is wrong with what I am doing (in particular how can initiating TFiles after a loop cause something completely unrelated during a loop to seg fault?), and how do I do what I want to do?

I’ve also noticed that when removing the selection if statement the issue is still there, but removing the line “inputtree->SetBranchAddress(“someVectorInOriginalTTree”,&someVectorInOriginalTTree);” gets rid of the issue (but I need this line to make selections).

MWE below:

#include "ROOT/TSeq.hxx"
#include "TFile.h"
#include "TTree.h"
#include <iostream>
#include "TROOT.h"
#include "TRandom.h"


void tree1w() //This function is just to make a TTree to read, not part of the issue just needed for MWE. Took from https://root.cern/doc/master/tree1_8C_source.html with a slight edit of adding   std::vector<int> someVectorInOriginalTTree;
{
  //create a Tree file tree1.root                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     

  //create the file, the Tree and a few branches                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
  TFile f("tree1.root","recreate");
  TTree t1("outTree","a simple Tree with simple variables");
  Float_t px, py, pz;
  Double_t random;
  Int_t ev;
  gROOT->ProcessLine("#include <vector>");
  std::vector<int> someVectorInOriginalTTree;
  t1.Branch("px",&px,"px/F");
  t1.Branch("py",&py,"py/F");
  t1.Branch("pz",&pz,"pz/F");
  t1.Branch("random",&random,"random/D");
  t1.Branch("ev",&ev,"ev/I");
  t1.Branch("someVectorInOriginalTTree",&someVectorInOriginalTTree);

  //fill the tree                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
  for (Int_t i=0;i<100;i++) {
    gRandom->Rannor(px,py);
    pz = px*px + py*py;
    random = gRandom->Rndm();
    ev = i;
    for (int j=0;j<5;j++){
      someVectorInOriginalTTree.push_back(j);
    }
    t1.Fill();
  }

  //save the Tree header. The file will be automatically closed                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
  //when going out of the function scope                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
  t1.Write();
}



int main(int argc, char **argv){

  tree1w();
  TFile inputfile("tree1.root");
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
  TTree *inputtree;
  inputfile.GetObject("outTree",inputtree);
  const auto nentries = inputtree->GetEntries();
  std::cout << "Got tree entries = " << nentries << std::endl;


  TFile file1("file1.root", "recreate");
  TTree* tree1 = inputtree->CloneTree(0);
  TFile file2("file2.root", "recreate");
  TTree* tree2 = inputtree->CloneTree(0);
  TFile file3("file3.root", "recreate");
  TTree* tree3 = inputtree->CloneTree(0);
  TFile file4("file4.root", "recreate");
  TTree* tree4 = inputtree->CloneTree(0);
  TFile file5("file5.root", "recreate");
  TTree* tree5 = inputtree->CloneTree(0);
  TFile file6("file6.root", "recreate");
  TTree* tree6 = inputtree->CloneTree(0);
//Causes Seg Fault BLOCK1 TFile file7("file7.root", "recreate");
//Causes Seg Fault BLOCK1   TTree* tree7 = inputtree->CloneTree(0);

  std::cout << "Beggining Loop " << std::endl;

  std::vector<int>* someVectorInOriginalTTree;
//Causes Seg Fault BLOCK2  inputtree->SetBranchAddress("someVectorInOriginalTTree",&someVectorInOriginalTTree);
  for (auto i : ROOT::TSeqI(nentries)) {
    std::cout << "Getting Entry " << std::endl;
    inputtree->GetEntry(i);
    std::cout << "Got Entry " << std::endl;
  }
  std::cout << "Ended Loop " << std::endl;
}

The MWE above runs correctly. However, include the lines that are commented out which start with “//Causes Seg Fault” and it will seg fault. If you only include either the ones labelled “BLOCK1” or “BLOCK2” but not both it will also not seg fault, looking at the output to the terminal you can see it seg faults on the line

inputtree->GetEntry(i);

If you instead move lines
//Causes Seg Fault BLOCK1 TFile file7(“file7.root”, “recreate”);
//Causes Seg Fault BLOCK1 TTree* tree7 = inputtree->CloneTree(0);
to after the loop, it will still seg fault on the line inputtree->GetEntry(i); , which makes no sense to me, how can something that is added after the loop cause something within the loop to seg fault?

Note: the exact number of files/clones needed to cause the seg fault varies depending on ROOT version (and perhaps other things I haven’t noticed?) this was done with v6.20.06 , if it doesn’t seg fault with this many files for you, just add more and eventually it will.

Before the “for” loop, correct:
std::vector<int> *someVectorInOriginalTTree = 0; // must be initialized

After the “for” loop, add:
inputtree->ResetBranchAddresses(); // disconnect from local variables
delete someVectorInOriginalTTree; // cleanup

Hi,

Thanks very much this fixes the problem. I don’t quite understand what is going on though. In particular I don’t understand how changing something after the loop can cause it to seg fault/not seg fault within the loop. I’ve tried looking up the documentation but I don’t understand how this is fixing it, or what the initial problem was (and I also especially don’t understand why this works fine for a few clones of the ttree but after a certain number that is ROOT version dependent then causes a seg fault). Would you be able to give a quick explanation as to what I was doing wrong that causes this to happen?

Thanks again a lot for your help,
Jack

The main problems was the missing initialization:

std::vector<int> *someVectorInOriginalTTree = 0; // must be initialized

without this initialization the pointer someVectorInOriginalTTree had an arbitrary value that depended (amongst other things) of its placement inside the stack, the stack usage, etc.

Since the value was arbitrary/undefined the resulting overall behavior was undefined where undefined ranges from ‘appearing to work correctly’ to 'segmentation fault`

Ok, thanks very much for your help

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.