Odd Seg Fault When Drawing TRatioPlot if doing SetGraphDrawOpt("E0")

Hi,

I am receiving an odd seg fault from TRatioPlot. I am unfortunately so far unable to reproduce this without using data internal to ATLAS, but I can give a basic explanation and code snippet/pseudocode, perhaps there is something I am doing obviously wrong.

I am looping through multiple ROOT files which each have a single TTree in them. From this I am filling two histograms.

With these two histograms I produce a TRatioPlot every certain number of events, which I save as a PDF with gPad->Print();.

However, occasionally at seemingly random points TRatioPlot will give a seg fault as such:

===========================================================
There was a crash.
This is the entire stack trace of all threads:
===========================================================
#0  0x00007f3e140f460c in waitpid () from /lib64/libc.so.6
#1  0x00007f3e14071f62 in do_system () from /lib64/libc.so.6
#2  0x00007f3e18ca1663 in TUnixSystem::StackTrace() () from /cvmfs/sft.cern.ch/lcg/releases/LCG_97a/ROOT/v6.20.06/x86_64-centos7-gcc8-opt/lib/libCore.so
#3  0x00007f3e18ca3eb4 in TUnixSystem::DispatchSignals(ESignals) () from /cvmfs/sft.cern.ch/lcg/releases/LCG_97a/ROOT/v6.20.06/x86_64-centos7-gcc8-opt/lib/libCore.so
#4  <signal handler called>
#5  0x00007f3e18c08ba0 in TList::FindObject(char const*) const () from /cvmfs/sft.cern.ch/lcg/releases/LCG_97a/ROOT/v6.20.06/x86_64-centos7-gcc8-opt/lib/libCore.so
#6  0x00007f3e170b90aa in TPad::GetFrame() () from /cvmfs/sft.cern.ch/lcg/releases/LCG_97a/ROOT/v6.20.06/x86_64-centos7-gcc8-opt/lib/libGpad.so
#7  0x00007f3e170b8df3 in TPad::PaintPadFrame(double, double, double, double) () from /cvmfs/sft.cern.ch/lcg/releases/LCG_97a/ROOT/v6.20.06/x86_64-centos7-gcc8-opt/lib/libGpad.so
#8  0x00007f3dfe13cc2b in THistPainter::Paint(char const*) () from /cvmfs/sft.cern.ch/lcg/releases/ROOT/v6.20.06-3f7fd/x86_64-centos7-gcc8-opt/lib/libHistPainter.so
#9  0x00007f3dfe1097de in TGraphPainter::PaintGraph(TGraph*, int, double const*, double const*, char const*) () from /cvmfs/sft.cern.ch/lcg/releases/ROOT/v6.20.06-3f7fd/x86_64-centos7-gcc8-opt/lib/libHistPainter.so
#10 0x00007f3dfe101036 in TGraphPainter::PaintGraphSimple(TGraph*, char const*) () from /cvmfs/sft.cern.ch/lcg/releases/ROOT/v6.20.06-3f7fd/x86_64-centos7-gcc8-opt/lib/libHistPainter.so
#11 0x00007f3dfe10e4e7 in TGraphPainter::PaintGraphAsymmErrors(TGraph*, char const*) () from /cvmfs/sft.cern.ch/lcg/releases/ROOT/v6.20.06-3f7fd/x86_64-centos7-gcc8-opt/lib/libHistPainter.so
#12 0x00007f3dfe119876 in TGraphPainter::PaintHelper(TGraph*, char const*) () from /cvmfs/sft.cern.ch/lcg/releases/ROOT/v6.20.06-3f7fd/x86_64-centos7-gcc8-opt/lib/libHistPainter.so
#13 0x00007f3e170d2ab3 in TPad::Paint(char const*) () from /cvmfs/sft.cern.ch/lcg/releases/LCG_97a/ROOT/v6.20.06/x86_64-centos7-gcc8-opt/lib/libGpad.so
#14 0x00007f3e170b8850 in TPad::Draw(char const*) () from /cvmfs/sft.cern.ch/lcg/releases/LCG_97a/ROOT/v6.20.06/x86_64-centos7-gcc8-opt/lib/libGpad.so
#15 0x00007f3e170e03ef in TRatioPlot::Draw(char const*) () from /cvmfs/sft.cern.ch/lcg/releases/LCG_97a/ROOT/v6.20.06/x86_64-centos7-gcc8-opt/lib/libGpad.so
#16 0x00000000004135e9 in plotTratio(TH1D*&, TH1D*&, double, double, double, double, double, double, double, double, double, double, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) ()
#17 0x000000000040b7f2 in main ()
===========================================================

I have ran with gdb and the output is:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff78efba0 in TList::FindObject(char const*) const () from /cvmfs/sft.cern.ch/lcg/releases/LCG_97a/ROOT/v6.20.06/x86_64-centos7-gcc8-opt/lib/libCore.so
Missing separate debuginfos, use: debuginfo-install bzip2-libs-1.0.6-13.el7.x86_64 freetype-2.8-14.el7_9.1.x86_64 glibc-2.17-325.el7_9.x86_64 jbigkit-libs-2.0-11.el7.x86_64 keyutils-libs-1.5.8-3.el7.x86_64 krb5-libs-1.15.1-51.el7_9.x86_64 libICE-1.0.9-9.el7.x86_64 libSM-1.2.2-2.el7.x86_64 libX11-1.6.7-4.el7_9.x86_64 libXau-1.0.8-2.1.el7.x86_64 libXext-1.3.3-3.el7.x86_64 libcom_err-1.42.9-19.el7.x86_64 libjpeg-turbo-1.2.90-8.el7.x86_64 libpng-1.5.13-8.el7.x86_64 libselinux-2.5-15.el7.x86_64 libtiff-4.0.3-35.el7.x86_64 libuuid-2.23.2-65.el7_9.1.x86_64 libxcb-1.13-1.el7.x86_64 ncurses-libs-5.9-14.20130511.el7_4.x86_64 openssl-libs-1.0.2k-25.el7_9.x86_64 pcre-8.32-17.el7.x86_64 xz-libs-5.2.2-1.el7.x86_64

The weirdest part however is I have now put all of the code relevant to TRatioPlot in a function, were nothing is passed by reference. I have also logged every variable passed to this function in an attempt to debug, and then have made a standalone program just using this function and passing the same variables to it… And it does not seg fault. It only seems to seg fault as part of my program when it has been running for a long time on large input files.

I’ve managed to prevent the seg fault by commenting out the lines

  rp->GetLowerRefXaxis()->SetRangeUser(viewMjjLowerLimit,viewMjjUpperLimit);
  rp->GetUpperRefYaxis()->SetRangeUser(viewEventsLowerLimit,viewEventsUpperLimit);
  rp->GetLowerRefXaxis()->SetLabelSize(fontSize);
  rp->GetUpperRefYaxis()->SetLabelSize(fontSize);
  rp->GetLowerRefXaxis()->SetTitleSize(fontSize);
  rp->GetUpperRefYaxis()->SetTitleSize(fontSize);
  rp->GetLowerRefXaxis()->SetTitleOffset(xAxisTitleOffset);
  rp->GetUpperRefYaxis()->SetTitleOffset(yAxisTitleOffset);
  rp->GetLowerRefYaxis()->SetLabelSize(fontSize);
  rp->GetLowerRefYaxis()->SetTitleSize(fontSize);
  rp->SetGraphDrawOpt("E0")".

(It is hard to find the exact line that is the problem as it takes roughly 5 hours before the seg fault occurs. In addition the seg fault does not always occur at the same point in the code, but usually at rouglhy the same point). However, I suspect it is most likely due to the line rp->SetGraphDrawOpt(“E0”)".)

I have also noticed that changing:

  std::vector<double> gridlines({1});
  rp->SetGridlines(gridlines);

to have different values for gridlines({}) seems to wildly vary at what point in the code the seg fault will occur.

I have included the relevant function below, every call of the function has a unique value for “dayCount” so it is not a case of the histograms h1 and h2 or canvas c1 being overwritten. I have made this function take input as vectors and reproduce the histogram from scratch within the function in case it was a problem of the histograms having some issue, but it still occurs.

void plotTratio(    std::vector<double> h1_y, std::vector<double> h1_ey
                  , std::vector<double> h2_y, std::vector<double> h2_ey
                  , double viewMjjLowerLimit, double viewMjjUpperLimit
                  , double viewEventsLowerLimit, double  viewEventsUpperLimit
                  , double fontSize
                  , double lineWidth
                  , double xAxisTitleOffset,double  yAxisTitleOffset
                  , double plotTitleXPos, double plotTitleYPos
                  , std::vector<std::string> v_title
                  , std::vector<std::string> v_title2
                  , std::string wordAfterATLAS
                  , std::string outputName
                  , int dayCount
                  , std::string outputROOTName
                ){

  float shiftY{0.04}; //Just for positioning of titles

  //////////////////////////////////
  // Append to currently existing ROOT file. I am opening this ROOT file again every time rather than
 // passing an already open ROOT file to the function to keep the function self contained for debugging.
// I have tried doing this with passing a reference to an already open output ROOT file and it still 
// I have also tried with no ROOT file and doing h1/h2 setDirectory(nullptr) and h1/h2
// AddDirectory(kFALSE) and it still occurs
  TFile outputROOTFile(outputROOTName.c_str(),"update");
  outputROOTFile.cd();

  //Initialize new histograms from scratch
  float massbining[] = { 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100,
                         1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2100,
                         2200, 2300, 2400, 2500, 2600, 2700, 2800, 2900, 3000, 3100,
                         3200, 3300, 3400, 3500, 3600, 3700, 3800, 3900, 4000, 4100,
                         4200, 4300, 4400, 4500, 4600, 4700, 4800, 4900, 5000, 5100,
                         5200, 5300, 5400, 5500, 5600, 5700, 5800, 5900, 6000, 6100,
                         6200, 6300, 6400, 6500, 6600, 6700, 6800, 6900, 7000, 7100,
                         7200, 7300, 7400, 7500, 7600, 7700, 7800, 7900, 8000, 8100,
                         8200, 8300, 8400, 8500, 8600, 8700, 8800, 8900, 9000, 9100,
                         9200, 9300, 9400, 9500, 9600, 9700, 9800, 9900, 10000, 10100,
                         10200, 10300, 10400, 10500, 10600, 10700, 10800, 10900, 11000, 11100,
                         11200, 11300, 11400, 11500, 11600, 11700, 11800, 11900, 12000, 12100,
                         12200, 12300, 12400, 12500, 12600, 12700, 12800, 12900, 13000};
  int nbins = sizeof(massbining)/sizeof(float)-1 ;
  TH1D* h1 = new TH1D(("h1_"+std::to_string(dayCount)).c_str(), "; m_{jj} [GeV] ; Events ", nbins, massbining);
  TH1D* h2 = new TH1D(("h2_"+std::to_string(dayCount)).c_str(), "; m_{jj} [GeV] ; Events ", nbins, massbining);
//Fill histograms from vectors (all of h1_y, h1_ey h2_y and h2_ey are always the exact same size).
//No element is inf or nan, every element of h2_y is positive and non-zero so it is not an issue with division by zero (there are 0 elements in h1_y but this is the numerator so does not cause division by 0, in addition h1_ey and h2_ey are 0 in every bin were h1_y is 0, but these are the errors so should not be an issue.).
  for (int i=0;i<h1_y.size();i++){
    h1->SetBinContent(i,h1_y.at(i));
    h1->SetBinError(i,h1_ey.at(i));
  }
  for (int i=0;i<h2_y.size();i++){
    h2->SetBinContent(i,h2_y.at(i));
    h2->SetBinError(i,h2_ey.at(i));
  }

  //Initiliaze new TCanvas
  TCanvas* c1 = new TCanvas(("c1_"+std::to_string(dayCount)).c_str(),"transparent pad",3840,2160);
  gStyle->SetOptStat(0);
  c1->SetLogy(true);
  c1->SetTicks(1,1);
  c1->SetLeftMargin(0.5);
  c1->SetRightMargin(0.03);
  c1->SetTopMargin(0.03);
  c1->SetGrid();
  //Set h1 formatting
  h1->SetLineWidth(5);
  h1->GetXaxis()->SetRangeUser(viewMjjLowerLimit,viewMjjUpperLimit);
  h1->GetYaxis()->SetRangeUser(viewEventsLowerLimit,viewEventsUpperLimit);
  h1->GetXaxis()->SetLabelSize(fontSize);
  h1->GetYaxis()->SetLabelSize(fontSize);
  h1->GetXaxis()->SetTitleSize(fontSize);
  h1->GetYaxis()->SetTitleSize(fontSize);
  h1->GetXaxis()->SetTitleOffset(xAxisTitleOffset);
  h1->GetYaxis()->SetTitleOffset(yAxisTitleOffset);
  h1->Draw("E0");
 //Set h2 formatting
  h2->SetTitle("");
  h2->SetLineWidth(5);
  h2->SetLineColor(kRed);
  h2->SetLineStyle(2);
  h2->GetXaxis()->SetRangeUser(viewMjjLowerLimit,viewMjjUpperLimit);
  h2->GetXaxis()->SetLabelSize(fontSize);
  h2->GetYaxis()->SetLabelSize(fontSize);
  h2->GetXaxis()->SetTitleSize(fontSize);
  h2->GetYaxis()->SetTitleSize(fontSize);
  h2->GetXaxis()->SetTitleOffset(xAxisTitleOffset);
  h2->GetYaxis()->SetTitleOffset(yAxisTitleOffset);
  h2->Draw("C");
  h1->Write();
  h2->Write();

  //Produce TRatioPlot
  auto rp = new TRatioPlot(h1, h2);
  //Format TRatioPlot
  rp->Draw();
  rp->SetLeftMargin(0.075);
  rp->SetRightMargin(0.03);
  rp->SetLowTopMargin(0.01);
  rp->SetUpTopMargin(0.025);
  rp->SetUpBottomMargin(0);
  rp->GetLowerRefYaxis()->SetRangeUser(0.5,2);
  rp->GetLowerRefYaxis()->SetTitle("Data/fit");

  rp->GetLowerRefXaxis()->SetRangeUser(viewMjjLowerLimit,viewMjjUpperLimit);
  rp->GetUpperRefYaxis()->SetRangeUser(viewEventsLowerLimit,viewEventsUpperLimit);
  rp->GetLowerRefXaxis()->SetLabelSize(fontSize);
  rp->GetUpperRefYaxis()->SetLabelSize(fontSize);
  rp->GetLowerRefXaxis()->SetTitleSize(fontSize);
  rp->GetUpperRefYaxis()->SetTitleSize(fontSize);
  rp->GetLowerRefXaxis()->SetTitleOffset(xAxisTitleOffset);
  rp->GetUpperRefYaxis()->SetTitleOffset(yAxisTitleOffset);
  rp->GetLowerRefYaxis()->SetLabelSize(fontSize);
  rp->GetLowerRefYaxis()->SetTitleSize(fontSize);


  std::vector<double> gridlines({1});
  rp->SetGridlines(gridlines);

  rp->SetH1DrawOpt("E0");
  rp->SetH2DrawOpt("C");
  rp->SetGraphDrawOpt("E0");
  rp->Draw(); //THIS IS THE LINE THAT SEG FAULTS

  //Format canvas with titles
  std::vector<TLatex*> v_titleTex{};
  TLatex* tex_ATLAS(new TLatex(plotTitleXPos,plotTitleYPos+0.01,"ATLAS"));
  tex_ATLAS->SetNDC();
  tex_ATLAS->SetTextFont(72);
  tex_ATLAS->SetTextSize(0.0455);
  tex_ATLAS->SetLineWidth(2);
  v_titleTex.push_back(tex_ATLAS);
  TLatex* tex_wordAfterATLAS(new TLatex(plotTitleXPos+0.09,plotTitleYPos+0.01,wordAfterATLAS.c_str()));
  tex_wordAfterATLAS->SetNDC();
  tex_wordAfterATLAS->SetTextFont(42);
  tex_wordAfterATLAS->SetTextSize(0.04225);
  tex_wordAfterATLAS->SetLineWidth(2);
  v_titleTex.push_back(tex_wordAfterATLAS);


  for (size_t i=0; i<v_title.size();i++){
    if (i!=v_title.size()-1){
    }
    else{
      shiftY+=0.01;
    }
    TLatex* tex_Title(new TLatex(plotTitleXPos,plotTitleYPos-0.02-i*shiftY,v_title.at(i).c_str()));
    tex_Title->SetNDC();
    tex_Title->SetTextFont(82);
    if (i!=v_title.size()-1){
      tex_Title->SetTextSize(fontSize);
    }
    else{
      tex_Title->SetTextSize(fontSize/1.5);
    }
    tex_Title->SetLineWidth(lineWidth);
    v_titleTex.push_back(tex_Title);
  }

  for (size_t i=0; i<v_title2.size();i++){
    TLatex* tex_Title(new TLatex(0.42,0.9-0.02-i*shiftY,v_title2.at(i).c_str()));
    tex_Title->SetNDC();
    tex_Title->SetTextFont(82);
    tex_Title->SetTextSize(fontSize);
    tex_Title->SetLineWidth(lineWidth);
    v_titleTex.push_back(tex_Title);
  }
  for (size_t i=0;i<v_titleTex.size();i++){
    v_titleTex.at(i)->Draw();
  }
  //Write output
  gPad->Print(outputName.c_str());
  c1->Write();
  //Cleanup
  delete c1;
  delete rp;
  for (auto &i:v_titleTex){
    delete i; //this also deletes tex_wordAfterATLAS and tex_ATLAS
  }
  v_titleTex.clear();
  delete h1;
  delete h2;
  outputROOTFile.Close();
}

This function is called as such in pseudocode:

dayCount=0
TFile outputROOTFile(outputROOTName.c_str(),"recreate");
loopOverInputFilePaths:
    TFile* inFile = new TFile(filePath,"read")
    TTree* tree = (TTree*) inFile->Get(treeName)
    loopOverAllTTreeEntires:
       fillHistograms
       if (nextDay):
          dayCount++
          convert histograms into std::vectors
          plotTratio(...)
          inFile->cd();
  Finish looping over all TTree entries
  inFile->Close();
  delete inFile;
Finish looping over all files

I apologise as I realise that this is probably not enough information to be helpful but unfortunately as mentioned I can’t seem to replicate this behaviour with a MWE. I hope there is something I am doing obviously wrong in the function provided which makes it clear what is causing the issue.

I am assuming since the seg fault goes away if I comment out lines including SetGraphDrawOpt("E0") and that gdb reports that the seg fault occurs in:

0x00007ffff78efba0 in TList::FindObject(char const*) const () from /cvmfs/sft.cern.ch/lcg/releases/LCG_97a/ROOT/v6.20.06/x86_64-centos7-gcc8-opt/lib/libCore.so

that the issue is for some reason the Graph is not being drawn/is being lost from memory somehow.

Thank you,
Jack

SetGraphDrawOpt only set the drawing option. It could not be the method which produces the crash. May be there is something wrong elsewhere in your code (code overwritten?) which shows up (randomly) when calling SetGraphDrawOpt.