Valgrind: Massive amount of Conditional jump or move depends on uninitialized value(s) wit 6.14


since we moved to 6.14/04 we noticed a massive amount of valgrind complaints about “Conditional jump or move depends on uninitialized value(s)”. In fact, running something simple like

valgrind --num-callers=30 --suppressions=$ROOTSYS/etc/valgrind-root.supp \
    root.exe -l -q -e 'std::cout << "hello world\n";'

results in 573 errors from 100 contexts (valgrind stops at 100). All of them are “Conditional jump”, for example

==1920949== Conditional jump or move depends on uninitialised value(s)
==1920949==    at 0x4C6E0D7: CallRecursiveRemoveIfNeeded (TROOT.h:401)
==1920949==    by 0x4C6E0D7: TObject::~TObject() (TObject.cxx:84)
==1920949==    by 0x4CB20A2: ~TInetAddress (TInetAddress.h:66)

In general it seems like every time TObject::TestBit is called there’s a complaint and this function is inlined so the stack traces are all over the place.

Are we the only ones with this problem?

ROOT Version: 6.14/04
Platform: Ubuntu 16.04
Compiler: GCC 8.2 -std=c++17


We heard rumors that this problem exists with GCC8 / C++17. Would you be able to share the valgrind log, such that we can assess what we need to suppress?

@pcanal - FYI!

Cheers, Axel.

Hi Axel,

sure thing, valgrind.txt (210.7 KB) is the logfile created with valgrind 3.14 running

valgrind --num-callers=30 --log-file=valgrind.txt --gen-suppressions=all --error-limit=no  --suppressions=$ROOTSYS/etc/valgrind-root.supp \
    root.exe -l -q -e 'std::cout << "hello world\n";'

However the number of errors and different stack traces increases when we actually use ROOT objects in a real program so this is definitely not everything :slight_smile:

Thanks a lot!

While I could just take your file and update the suppression file I would like to hear @pcanal’s opinion here: especially CallRecursiveRemoveIfNeeded's TestBit(kMustCleanup) looks surprising; I don’t see how this bit could remain uninitialized for instance after construction of an TInetAddress.

Cheers, Axel.

I tried adding these suppressions but believe me, there are much more cropping up. After suppressing the ones from the logfile above (in a very broad sense) running simple reconstruction job still leaves me with a long list.

For example, all of these (only keeping the top frame) and more:

at 0x5EFCF39: TObject::Streamer(TBuffer&) (TObject.cxx:802)
at 0x5EFCFE3: TObject::Streamer(TBuffer&) (TObject.cxx:820)
at 0x5F145C2: TRef::operator=(TObject*) (TRef.cxx:287)
at 0x5F47F31: TUrl::GetUrl(bool) const (TUrl.cxx:390)
at 0x5F63971: TClonesArray::Delete(char const*) (TClonesArray.cxx:435)
at 0x5F6B903: TList::RemoveLast() (TList.cxx:907)
at 0x5F6C945: TList::FindLink(TObject const*, int&) const (TList.cxx:642)
at 0x5F7132F: TList::AddLast(TObject*) (TList.cxx:155)
at 0x5F74044: TList::Streamer(TBuffer&) (TList.cxx:1242)
at 0x5F767BD: TObjArray::GetEntries() const (TObjArray.cxx:526)
at 0x5F7734B: TObjArray::GetAbsLast() const (TObjArray.cxx:544)
at 0x5F7745A: TObjArray::AddAtAndExpand(TObject*, int) (TObjArray.cxx:236)
at 0x5F775C8: TObjArray::FindObject(char const*) const (TObjArray.cxx:416)
at 0x5F77791: TObjArray::Last() const (TObjArray.cxx:507)
at 0x5F77841: TObjArray::GetLast() const (TObjArray.cxx:563)
at 0x5F77CC5: TObjArray::Init(int, int) (TObjArray.cxx:613)
at 0x5F78326: TObjArray::~TObjArray() (TObjArray.cxx:95)
at 0x5F78832: TObjArray::operator[](int) (TObjArray.cxx:132)
at 0x5F78AD1: TObjArray::MakeIterator(bool) const (TObjArray.cxx:635)
at 0x5F78D0C: TObjArray::Streamer(TBuffer&) (TObjArray.cxx:476)
at 0x6264647: TFile::Init(bool) (TFile.cxx:868)
at 0x6266563: TFile::Close(char const*) (TFile.cxx:1000)
at 0x6266948: TFile::~TFile() (TFile.cxx:577)

So I don’t think this is something to be easily solved by adding suppressions but some deeper misunderstanding between gcc 8 or c++17, ROOT and valgrind …

let me see what happens if I use clang for our code instead.

When is the file object deleted in the process lifetime? I.e. who calls ~TFile? All those are very suspicious (especially the last one).

I agree that they are all very suspicious. The ~TFile in this particular case is a short lived TFile on the stack and the destructor is called on return from the function.

I still believe that for whatever reason, valgrind seems that TObject::fBits is dirty and so every TestBit() call gets flagged.

I ran valgrind again on root.exe and added --track-origins=yes -v to print the origins of the offending allocations and also list all used suppressions in the end. And then I tried comparing this against a vanilla ROOT on cvmfs following the release site. So without any additional setup I logged into a SL7 machine at DESY and ran

. /cvmfs/
. /cvmfs/
valgrind --num-callers=30 --log-file=valgrind-sft.txt --gen-suppressions=all --error-limit=no  --suppressions=$ROOTSYS/etc/valgrind-root.supp --track-origins=yes -v \
    root.exe -l -q -e 'std::cout << "hello world\n";'

and the result (valgrind-sft.txt (238.0 KB)) has a similar amount of complaints.

So this might not be gcc 8 or C++17 at all :confused:

Since I can reproduce this problem with the latest cvmfs release and there is no further feedback I opened ROOT-9812 to keep track of this.

Cheers, Martin

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