Hello,
I get crashes when iterating with pyroot over empty std::vectors
in some cases, where T is a class of my own. The code below reproduces
(hopefully) and illustrates the problem.
For what concerns pyroot, I have traced this back to a call to
vector::data() in Pythonize.cxx. I suggest a fix/workaround
below by simply checking size()>0 before calling data() to
prevent the crash.
I am not sure if calling data() on an empty vector is supposed to be
allowed. Maybe it is cling/gcc or some combination of them that is
not compliant here? (it is actually hard to undertand for me exactly
which things come from cling and which come from gcc via the .so
file.).
Cheers,
Aart.
python script reproducing the problem
import ROOT
open("aa.hh", 'w').write("""
#include "TObject.h"
#include <vector>
#include <iostream>
using std::cout;
using std::endl;
struct AA : public TObject
{
int x;
int y;
ClassDef( AA, 1 );
};
inline std::vector<AA> getv()
{
std::vector<AA> r;
// cout << r.data() << endl; // crashes!
for( auto x : r ) { cout << &x << endl; } // fine (c++ iteration does not call data(), I guess)
return r;
}
// BB is identical to AA, but there is no mention of vector<BB> anywhere
struct BB : public TObject
{
int x;
int y;
ClassDef( BB, 1 );
};
""" )
open("aa.cc", 'w').write( """
#include "aa.hh"
""" )
ROOT.gROOT.ProcessLine(".L aa.cc+");
vaa = ROOT.vector( ROOT.AA )() # difference between AA and BB: there is no mention...
vbb = ROOT.vector( ROOT.BB )() # ...of vector<BB> on the c++ side.
for x in vbb : print x # fine
print vbb.data() # also fine (null ptr)
for x in vaa : print x # crashes.
print vaa.data() # also crashes (as does calling data on c++ side)
# The following prevents the crash
# (presumably some ptr is not initlized until the vector contains something)
v.resize(1)
v.resize(0)
print v.data()
# other observations
# - commenting out ClassDef does not matter
# - I use aclic in this example, but also observed with rootcling/g++ compiled libraries.
script output on my machine
[heijboer@cca009 tests]$ python -i bug.py
Info in <TUnixSystem::ACLiC>: creating shared library /sps/km3net/users/heijboer/aanet/tests/./aa_cc.so
<ROOT.BB object at 0x(nil)>
Thread 2 (Thread 0x7f350f7d5700 (LWP 61742)):
#0 0x00007f352914fafb in do_futex_wait.constprop.1 () from /lib64/libpthread.so.0
#1 0x00007f352914fb8f in __new_sem_wait_slow.constprop.0 () from /lib64/libpthread.so.0
#2 0x00007f352914fc2b in sem_wait
GLIBC_2.2.5 () from /lib64/libpthread.so.0
#3 0x00007f352946f735 in PyThread_acquire_lock () from /lib64/libpython2.7.so.1.0
#4 0x00007f352943b296 in PyEval_RestoreThread () from /lib64/libpython2.7.so.1.0
#5 0x00007f350f7d8016 in time_sleep () from /usr/lib64/python2.7/lib-dynload/timemodule.so
#6 0x00007f3529442cf0 in PyEval_EvalFrameEx () from /lib64/libpython2.7.so.1.0
#7 0x00007f352944503d in PyEval_EvalCodeEx () from /lib64/libpython2.7.so.1.0
#8 0x00007f35293cea6d in function_call () from /lib64/libpython2.7.so.1.0
#9 0x00007f35293a9a63 in PyObject_Call () from /lib64/libpython2.7.so.1.0
#10 0x00007f352943d6fd in PyEval_EvalFrameEx () from /lib64/libpython2.7.so.1.0
#11 0x00007f35294426bd in PyEval_EvalFrameEx () from /lib64/libpython2.7.so.1.0
#12 0x00007f35294426bd in PyEval_EvalFrameEx () from /lib64/libpython2.7.so.1.0
#13 0x00007f352944503d in PyEval_EvalCodeEx () from /lib64/libpython2.7.so.1.0
#14 0x00007f35293ce978 in function_call () from /lib64/libpython2.7.so.1.0
#15 0x00007f35293a9a63 in PyObject_Call () from /lib64/libpython2.7.so.1.0
#16 0x00007f35293b8a55 in instancemethod_call () from /lib64/libpython2.7.so.1.0
#17 0x00007f35293a9a63 in PyObject_Call () from /lib64/libpython2.7.so.1.0
#18 0x00007f352943b8f7 in PyEval_CallObjectWithKeywords () from /lib64/libpython2.7.so.1.0
#19 0x00007f3529473822 in t_bootstrap () from /lib64/libpython2.7.so.1.0
#20 0x00007f3529149e25 in start_thread () from /lib64/libpthread.so.0
#21 0x00007f352876abad in clone () from /lib64/libc.so.6
Thread 1 (Thread 0x7f3529911740 (LWP 61546)):
#0 0x00007f35287311c9 in waitpid () from /lib64/libc.so.6
#1 0x00007f35286aee52 in do_system () from /lib64/libc.so.6
#2 0x00007f35286af201 in system () from /lib64/libc.so.6
#3 0x00007f352631a0ef in TUnixSystem::StackTrace (this=0x1f24d80) at /pbs/throng/ccin2p3/support/gadrat/centos-7-x86_64/root/root_v6-10-02/core/unix/src/TUnixSystem.cxx:2412
#4 0x00007f3520d7f005 in cling::MultiplexInterpreterCallbacks::PrintStackTrace() () from /pbs/software/centos-7-x86_64/root/6.10.02/lib/libCling.so
#5 0x00007f3520d7e5e8 in cling_runtime_internal_throwIfInvalidPointer () from /pbs/software/centos-7-x86_64/root/6.10.02/lib/libCling.so
#6 0x00007f352992c07d in ?? ()
#7 0x00007ffdbb46cf50 in ?? ()
#8 0x00007f352992c128 in ?? ()
#9 0x00007ffdbb46d540 in ?? ()
#10 0x0000000000000000 in ?? ()
terminate called after throwing an instance of 'cling::InvalidDerefException'
what(): Trying to dereference null pointer or trying to call routine taking non-null arguments
Aborted
[heijboer@cca009 tests]$
suggested workaround in bindings/pyroot/src/Pythonize.cxx
static PyObject* vector_iter( PyObject* v ) {
vectoriterobject* vi = PyObject_GC_New( vectoriterobject, &VectorIter_Type );
if ( ! vi ) return NULL;
Py_INCREF( v );
vi->vi_vector = v;
PyObject* pyvalue_type = PyObject_GetAttrString( (PyObject*)Py_TYPE(v), "value_type" );
PyObject* pyvalue_size = PyObject_GetAttrString( (PyObject*)Py_TYPE(v), "value_size" );
PyObject* pysize = CallPyObjMethod( v, "size" ); // added
long size = PyLong_AsLong( pysize ); // added
if ( size > 0 && pyvalue_type && pyvalue_size ) { // added size> 0 &&
PyObject* pydata = CallPyObjMethod( v, "data" );
_ROOT Version: 6.10.02
_Platform: x86 / centos7
_Compiler: gcc version 4.8.5