Segfault in Batch Mode: ROOT.gROOT.SetBatch(True)

I have (re)discovered that there is a bug that causes a segfault when drawing a TRatioPlot when the histograms are read off of a ROOT file while using PyROOT in batch mode — that is, with ROOT.gROOT.SetBatch(True) on lxplus for ROOT v6.08.

I have attached a (admittedly lengthy) minimal working example to demonstrate (minimal_example.tar.gz) and the resulting error logs (out.log.txt).

When running on lxplus if I have the line ROOT.gROOT.SetBatch(True) commented out then everything works fine but will print out all of the plots I’m drawing (which I don’t want), and if ROOT.gROOT.SetBatch(True) is uncommented it will segfault as soon as TRatioPlot’s constructor is called.

My configuration on lxplus (from sourcing the included shell file):

$ uname -a
Linux lxplus136.cern.ch 2.6.32-642.15.1.el6.x86_64 #1 SMP Fri Feb 24 16:56:37 CET 2017 x86_64 x86_64 x86_64 GNU/Linux
$ source /cvmfs/sft.cern.ch/lcg/releases/LCG_88/ROOT/6.08.06/x86_64-slc6-gcc62-opt/ROOT-env.sh
$ root-config --version
6.08/06
$ python --version
Python 2.7.13
$ gcc --version | head -n 1
gcc (GCC) 6.2.0

and as I said above, if ROOT.gROOT.SetBatch(True) is commented out then

python minimal.py

will run and make plots fine. This solution to a similar problem from 2006 also causes a segfault.

I should note that if I run this on my local machine (ROOT v6.09/03 and Python 3.5.3) then setting ROOT.gROOT.SetBatch(True) works perfectly fine. However, even if I run this on lxplus7.cern.ch (with the appropriate environment) it will segfault.

Does anyone have thoughts on how to get a fix for this?

If I turn on the batch mode in your macro I get the segfault too with the master version on my Mac. I am looking at it now. This time I will put the fix in master and 6.08 directly :slight_smile:

1 Like

I see it crashes in TCanvas::Destructor() on the line:

   delete fPainter;

Adding a test on fPainter does not help because it is never 0.

still investigating…

I see you do:

    # cleanup canvas
    can.Destructor()

This is the reason of the crash. Why do you need this call ? Destructor() is an internal TCanvas method. You are not supposed to call it. You should let ~TCanvas do the work.

I have that there as if I take it out I get:

TCanvas::Constructor:0: RuntimeWarning: Deleting canvas with same name: c

It seems like it is bad to be forcing deletion of an object this way. If I was in C++ I would have just had this on the stack so it would get cleaned up, but as this is Python I assumed to not have this I should call the Destructor method to properly clean up the canvas (even though I know that calling the destructor manually is a bad idea).

It seems given your comments that this is wrong. Is there better way to tell ~TCanvas to cleanup before the next constructor is called?

In C++ I would do a delete.
I am not a Python expert but googling a bit it seems the equivalent is del

That would be true if this was pure Python, but del doesn’t work with all objects in PyROOT. The answer seems to be to still use the destructor, but to use it correctly (which I was not doing). :frowning:

A demonstration is probably better than me trying to write in words:

# on lxplus
$ python
>>> import ROOT
>>> c1 = ROOT.TCanvas()
>>> c1.Destructor()
>>> c1
<ROOT.TCanvas object ("c1_n2") at 0x3b70890>
# c1 hasn't been cleaned up properly!
>>> c2 = ROOT.TCanvas()
>>> c2.IsA().Destructor(c2)
>>> c2
None
# c2 has been cleaned up safely

I am sorry to have wasted your time looking at this, as this was my code’s fault, not ROOT’s. I really appreciate your patience and assistance here.

Thanks, you are welcome :slight_smile:

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