Valgrind reports memory leak from TObject::Write and TTree::Branch


ROOT Version: 6.32.10
Platform: Ubuntu 22.04.5
Compiler: g++ 11.4.0


I created a small example of a simple class, that is written to a tree. When I check the program with valgrind using

valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --errors-for-leak-kinds=all -s --num-callers=30 --suppressions=$ROOTSYS/etc/valgrind-root.supp ./branchTest

I get a lot of messages of definitely lost bytes from creating the branch (line 12), writing the tree (line 19), and creating or closing the file (line 8 and 20).

I’ve attached the example program (branchTest.cxx), the header for the class (MyLeaf.h), and the LinkDef.h file. The program can be compiled using

rootcling -f MyLeafDict.cxx -rml MyLeaf.so -rmf MyLeaf.rootmap MyLeaf.h LinkDef.h
g++ -fPIC -c MyLeafDict.cxx -o MyLeafDict.o -g -O3 -Wall -Wextra -pedantic -Wno-unknown-pragmas -Wno-unused-function -Wshadow $(root-config --cflags)
g++ -fPIC branchTest.cxx -o branchTest -g -O3 -Wall -Wextra -pedantic -Wno-unknown-pragmas -Wno-unused-function -Wshadow $(root-config --cflags --libs) MyLeafDict.o -I.

branchTest.cxx (465 Bytes)

LinkDef.h (190 Bytes)

MyLeaf.h (321 Bytes)

I can’t attach the full output here as it is too big, but these are excerpts of it:

==431734== 112 bytes in 1 blocks are definitely lost in loss record 9,294 of 14,174
==431734==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==431734==    by 0x914B90F: clang::Parser::AnnotateTemplateIdToken(clang::OpaquePtr<clang::TemplateName>, clang::TemplateNameKind, clang::CXXScopeSpec&, clang::SourceLocation, clang::UnqualifiedId&, bool, bool) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x90CAFA1: clang::Parser::ParseOptionalCXXScopeSpecifier(clang::CXXScopeSpec&, clang::OpaquePtr<clang::QualType>, bool, bool, bool*, bool, clang::IdentifierInfo**, bool, bool) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x915B917: clang::Parser::TryAnnotateCXXScopeToken(bool) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x85ED145: cling::LookupHelper::findScope(llvm::StringRef, cling::LookupHelper::DiagSetting, clang::Type const**, bool) const (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x84F92D0: TCling::CheckClassInfo(char const*, bool, bool) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x4B15B41: TClass::Init(char const*, short, std::type_info const*, TVirtualIsAProxy*, char const*, char const*, int, int, ClassInfo_t*, bool) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B1739E: TClass::TClass(char const*, short, std::type_info const&, TVirtualIsAProxy*, char const*, char const*, int, int, bool) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B178EC: ROOT::CreateClass(char const*, short, std::type_info const&, TVirtualIsAProxy*, char const*, char const*, int, int) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B28691: ROOT::TGenericClassInfo::GetClass() (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B0B541: TClass::GetClass(char const*, bool, bool, unsigned long, unsigned long) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4ED7A2D: TStreamerInfo::Build(bool) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4B02050: TClass::GetStreamerInfoImpl(int, bool) const (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B021FD: TClass::GetStreamerInfo(int, bool) const (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x5B9212B: TTree::BuildStreamerInfo(TClass*, void*, bool) (in /opt/cern/root/root_v6.32.10/lib/libTree.so)
==431734==    by 0x5BA4A19: TTree::BronchExec(char const*, char const*, void*, bool, int, int) (in /opt/cern/root/root_v6.32.10/lib/libTree.so)
==431734==    by 0x5B90568: TTree::Branch(char const*, char const*, void*, int, int) (in /opt/cern/root/root_v6.32.10/lib/libTree.so)
==431734==    by 0x10D97B: Branch<MyLeaf> (TTree.h:403)
==431734==    by 0x10D97B: main (branchTest.cxx:15)

==431734== 112 bytes in 1 blocks are definitely lost in loss record 9,297 of 14,174
==431734==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==431734==    by 0x914B90F: clang::Parser::AnnotateTemplateIdToken(clang::OpaquePtr<clang::TemplateName>, clang::TemplateNameKind, clang::CXXScopeSpec&, clang::SourceLocation, clang::UnqualifiedId&, bool, bool) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x90CAFA1: clang::Parser::ParseOptionalCXXScopeSpecifier(clang::CXXScopeSpec&, clang::OpaquePtr<clang::QualType>, bool, bool, bool*, bool, clang::IdentifierInfo**, bool, bool) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x915B917: clang::Parser::TryAnnotateCXXScopeToken(bool) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x85ED145: cling::LookupHelper::findScope(llvm::StringRef, cling::LookupHelper::DiagSetting, clang::Type const**, bool) const (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x84ECCAD: TCling::GetClassSharedLibs(char const*) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x84F8F3B: TClingLookupHelper__ExistingTypeCheck(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x4AEEC16: TClassEdit::GetNormalizedName(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::basic_string_view<char, std::char_traits<char> >) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B0B768: TClass::GetClass(char const*, bool, bool, unsigned long, unsigned long) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B1224D: TBuildRealData::Inspect(TClass*, char const*, char const*, void const*, bool) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x84F600F: TCling::InspectMembers(TMemberInspector&, void const*, TClass const*, bool) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x4B03D2D: TClass::CallShowMembers(void const*, TMemberInspector&, bool) const (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B1086F: TClass::BuildRealData(void*, bool) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4ED5BE5: TStreamerInfo::Build(bool) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4B02050: TClass::GetStreamerInfoImpl(int, bool) const (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B021FD: TClass::GetStreamerInfo(int, bool) const (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4E0220B: TBufferFile::WriteClassBuffer(TClass const*, void*) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x5B3DCBA: TBranchElement::Streamer(TBuffer&) (in /opt/cern/root/root_v6.32.10/lib/libTree.so)
==431734==    by 0x4E011FA: TBufferFile::WriteObjectClass(void const*, TClass const*, bool) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4E093B3: TBufferIO::WriteObjectAny(void const*, TClass const*, bool) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4ACB65C: TObjArray::Streamer(TBuffer&) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4DFC7AB: TBufferFile::WriteFastArray(void*, TClass const*, long long, TMemberStreamer*) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x508CA65: int TStreamerInfo::WriteBufferAux<char**>(TBuffer&, char** const&, TStreamerInfo::TCompInfo* const*, int, int, int, int, int) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4ED9C03: TStreamerInfoActions::GenericWriteAction(TBuffer&, void*, TStreamerInfoActions::TConfiguration const*) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4E02144: TBufferFile::WriteClassBuffer(TClass const*, void*) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4EB1ACC: TKey::TKey(TObject const*, char const*, int, TDirectory*) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4E6E2A8: TFile::CreateKey(TDirectory*, TObject const*, char const*, int) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4E5EC20: TDirectoryFile::WriteTObject(TObject const*, char const*, char const*, int) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4A4273C: TObject::Write(char const*, int, int) const (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x10D9FD: main (branchTest.cxx:22)

==431734== 1,344 bytes in 8 blocks are definitely lost in loss record 12,711 of 14,174
==431734==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==431734==    by 0x914B90F: clang::Parser::AnnotateTemplateIdToken(clang::OpaquePtr<clang::TemplateName>, clang::TemplateNameKind, clang::CXXScopeSpec&, clang::SourceLocation, clang::UnqualifiedId&, bool, bool) (in /opt/cern/root/root_v6.32.10/l
ib/libCling.so)
==431734==    by 0x90CAFA1: clang::Parser::ParseOptionalCXXScopeSpecifier(clang::CXXScopeSpec&, clang::OpaquePtr<clang::QualType>, bool, bool, bool*, bool, clang::IdentifierInfo**, bool, bool) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x915B917: clang::Parser::TryAnnotateCXXScopeToken(bool) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x85ED145: cling::LookupHelper::findScope(llvm::StringRef, cling::LookupHelper::DiagSetting, clang::Type const**, bool) const (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x84ECCAD: TCling::GetClassSharedLibs(char const*) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x84DB48A: TCling::ShallowAutoLoadImpl(char const*) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x84F3AF7: TCling::DeepAutoLoadImpl(char const*, std::unordered_set<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocat
or<char> > >, std::equal_to<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> > > >&, bool) (in /opt/cern/root/root_v6.
32.10/lib/libCling.so)
==431734==    by 0x84F4073: TCling::AutoLoad(char const*, bool) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x4B02EE8: TClass::LoadClassDefault(char const*, bool) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B0BD62: TClass::GetClass(char const*, bool, bool, unsigned long, unsigned long) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4EA9F34: TGenCollectionProxy::Value::Value(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool, unsigned long, unsigned long) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4E5977C: TEmulatedCollectionProxy::InitializeEx(bool) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4E5A447: TEmulatedCollectionProxy::TEmulatedCollectionProxy(char const*, bool) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4E5503B: (anonymous namespace)::GenEmulation(char const*, bool) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4B1524E: TClass::Init(char const*, short, std::type_info const*, TVirtualIsAProxy*, char const*, char const*, int, int, ClassInfo_t*, bool) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B161C8: TClass::TClass(char const*, short, bool) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x84F788D: TCling::GenerateTClass(char const*, bool, bool) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x4B0BDB2: TClass::GetClass(char const*, bool, bool, unsigned long, unsigned long) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B1224D: TBuildRealData::Inspect(TClass*, char const*, char const*, void const*, bool) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x84F600F: TCling::InspectMembers(TMemberInspector&, void const*, TClass const*, bool) (in /opt/cern/root/root_v6.32.10/lib/libCling.so)
==431734==    by 0x4B03D2D: TClass::CallShowMembers(void const*, TMemberInspector&, bool) const (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B1086F: TClass::BuildRealData(void*, bool) (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4ED5BE5: TStreamerInfo::Build(bool) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4B02050: TClass::GetStreamerInfoImpl(int, bool) const (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4B021FD: TClass::GetStreamerInfo(int, bool) const (in /opt/cern/root/root_v6.32.10/lib/libCore.so)
==431734==    by 0x4E0220B: TBufferFile::WriteClassBuffer(TClass const*, void*) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4EB1ACC: TKey::TKey(TObject const*, char const*, int, TDirectory*) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4E6E2A8: TFile::CreateKey(TDirectory*, TObject const*, char const*, int) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)
==431734==    by 0x4E5EC20: TDirectoryFile::WriteTObject(TObject const*, char const*, char const*, int) (in /opt/cern/root/root_v6.32.10/lib/libRIO.so)

Hi,

Thanks a lot for this report: it’s interesting.
It seems like it comes from a clang symbol and not a ROOT symbol. Do you have means to check the very same setup but with any 6.36 release or the main branch (called master, in the case of ROOT)?
I am asking because the 6.32 release series features llvm16, 6.36 llvm18 and, since today, master llvm20.

Cheers,
D