From a82fb68b1404f5ffaf4af75c2b378b6925795902 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 9 Apr 2025 17:25:37 -0500 Subject: [PATCH 1/5] meta: Allow global disabling of AutoParsing during TClass::GetClass If TClass.cxx is build with the cpp macro: ROOT_DISABLE_TCLASS_GET_CLASS_AUTOPARSING defined, it will no longer do any auto-parsing during the execution of `TClass::GetClass`. This will result in not being able to find TClass-es when the name requires not-already loaded interpreted information (eg. a typedef to be resolved). Comments include additional possible interfaces to turn on this feature. --- core/meta/src/TClass.cxx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index bc34a3e6323e7..13f2db0b37a32 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -3153,6 +3153,21 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent, size_t hi // continue as before ... } + bool disableAutoParsing = gInterpreter->IsAutoParsingSuspended(); + // FIXME: We need to decided on the interface to disable auto-parsing only during TClass::GetClass. +#ifdef ROOT_DISABLE_TCLASS_GET_CLASS_AUTOPARSING + constexpr bool requestDisableAutoLoading = true; +#else + // We could get the user choice from: + // - environment variable ROOT_DISABLE_TCLASS_GET_CLASS_AUTOPARSING + // - rootrc key Root.TClass.GetClass.AutoParsing + // - TClass::SetGetClassAutoParsing + constexpr bool requestDisableAutoLoading = false; +#endif + if (requestDisableAutoLoading) + disableAutoParsing = true; + TInterpreter::SuspendAutoParsing autoparseFence(gInterpreter, disableAutoParsing); + // Note: this variable does not always holds the fully normalized name // as there is information from a not yet loaded library or from header // not yet parsed that may be needed to fully normalize the name. From e7454e8520923d394507571a694121c865a2bd34 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 9 Apr 2025 17:26:39 -0500 Subject: [PATCH 2/5] TCling: Register, give access to and print list of autoparsed class. Use `gInterpreter->Print("autoparsed");` to print a list of the class names that directly lead to auto-parsing. Use `gCling->GetAutoParseClasses()` to programatically get a set of the class names that directly lead to auto-parsing. --- core/metacling/src/TCling.cxx | 29 +++++++++++++++++++++++++++++ core/metacling/src/TCling.h | 3 +++ 2 files changed, 32 insertions(+) diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index 867c6d9386533..9db881ce23343 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -2662,6 +2662,29 @@ void TCling::PrintIntro() { } +//////////////////////////////////////////////////////////////////////////////// +/// Print information about the interpreter. +///\param[in] option Selects the type of information to print. +/// +/// List of currently support options: +/// - autoparsed: Print the list of classes that triggered autoparsing. +void TCling::Print(Option_t *option) const +{ + if (option && *option) { + if (!strcmp(option, "autoparsed")) { + std::cout << "Auto parsed classes:" << std::endl; + for (auto & cls : fAutoParseClasses) { + std::cout << " " << cls << std::endl; + } + } else { + ::Error("TCling::Print", "Unknown option '%s'", option); + } + } else { + ::Info("TCling::Print", "No options specified"); + } +} + + //////////////////////////////////////////////////////////////////////////////// /// \brief Add a directory to the list of directories in which the /// interpreter looks for include files. @@ -6536,6 +6559,12 @@ UInt_t TCling::AutoParseImplRecurse(const char *cls, bool topLevel) } } + if (nHheadersParsed) { + // Register that we did autoparsing for this class. + fAutoParseClasses.insert(cls); + if (gDebug) + Info("AutoParse", "Parsed %d headers for %s", nHheadersParsed, cls); + } return nHheadersParsed; } diff --git a/core/metacling/src/TCling.h b/core/metacling/src/TCling.h index 3fafddd266049..3d037f2f06876 100644 --- a/core/metacling/src/TCling.h +++ b/core/metacling/src/TCling.h @@ -121,6 +121,7 @@ class TCling final : public TInterpreter { std::set fLookedUpClasses; // Set of classes for which headers were looked up already std::set fPayloads; // Set of payloads std::set fParsedPayloadsAddresses; // Set of payloads which were parsed + std::set fAutoParseClasses; // Set of classes for which we autoparsed a header std::hash fStringHashFunction; // A simple hashing function std::unordered_set fNSFromRootmaps; // Collection of namespaces fwd declared in the rootmaps TObjArray* fRootmapFiles; // Loaded rootmap files. @@ -200,6 +201,7 @@ class TCling final : public TInterpreter { Int_t AutoLoad(const char *classname, Bool_t knowDictNotLoaded = kFALSE) final; Int_t AutoLoad(const std::type_info& typeinfo, Bool_t knowDictNotLoaded = kFALSE) final; Int_t AutoParse(const char* cls) final; + const std::set& GetAutoParseClasses() const { return fAutoParseClasses; } void* LazyFunctionCreatorAutoload(const std::string& mangled_name); bool LibraryLoadingFailed(const std::string&, const std::string&, bool, bool); Bool_t IsAutoLoadNamespaceCandidate(const clang::NamespaceDecl* nsDecl); @@ -240,6 +242,7 @@ class TCling final : public TInterpreter { Longptr_t ProcessLineAsynch(const char* line, EErrorCode* error = nullptr); Longptr_t ProcessLineSynch(const char* line, EErrorCode* error = nullptr) final; void PrintIntro() final; + void Print(Option_t *option="") const final; bool RegisterPrebuiltModulePath(const std::string& FullPath, const std::string& ModuleMapName = "module.modulemap") const final; void RegisterModule(const char* modulename, From 3067cfee7b57a222af6abc05e0f5e7db0a0dc139 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Fri, 11 Apr 2025 12:19:23 -0500 Subject: [PATCH 3/5] Remove use of obsolete gccxmlpath --- cmake/modules/RootMacros.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/RootMacros.cmake b/cmake/modules/RootMacros.cmake index f8f02be27b490..34a7459b95da5 100644 --- a/cmake/modules/RootMacros.cmake +++ b/cmake/modules/RootMacros.cmake @@ -187,7 +187,7 @@ function(REFLEX_GENERATE_DICTIONARY dictionary) OUTPUT ${gensrcdict} ${rootmapname} COMMAND ${ROOT_genreflex_CMD} ARGS ${headerfiles} -o ${gensrcdict} ${rootmapopts} --select=${selectionfile} - --gccxmlpath=${GCCXML_home}/bin ${ARG_OPTIONS} + ${ARG_OPTIONS} "-I$>,;-I>" "$<$>:-D$>" DEPENDS ${headerfiles} ${selectionfile} ${ARG_DEPENDS} From 885f558353353b4524135923b99da75f106ac96a Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 14 Apr 2025 15:55:05 -0500 Subject: [PATCH 4/5] [meta] Add `Root.TClass.GetClass.AutoParsing` rootrc key. This allows to disable auto-parsing during `TClass::GetClass` for debugging purposes. --- config/rootrc.in | 8 ++++++++ core/meta/src/TClass.cxx | 7 ++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/config/rootrc.in b/config/rootrc.in index f1bfd4c775200..0b57c1808fc45 100644 --- a/config/rootrc.in +++ b/config/rootrc.in @@ -644,3 +644,11 @@ Rint.Canvas.HighLightColor: 5 # 1 All Branches (default) # Can be overridden by the environment variable ROOT_TTREECACHE_PREFILL # TTreeCache.Prefill: 1 + +# Advanced Debug Settings +# Setting Root.TClass.GetClass.AutoParsing to false +# will disable any auto-parsing execution of `TClass::GetClass`. This will +# result in not being able to find TClass-es when the name requires not-already +# loaded interpreted information (eg. a typedef to be resolved). +# +# Root.TClass.GetClass.AutoParsing: true diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 13f2db0b37a32..e66d4211d9f32 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -55,6 +55,7 @@ In order to access the name of a class within the ROOT type system, the method T #include "TDataType.h" #include "TDatime.h" #include "TEnum.h" +#include "TEnv.h" #include "TError.h" #include "TExMap.h" #include "TFunctionTemplate.h" @@ -3158,11 +3159,7 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent, size_t hi #ifdef ROOT_DISABLE_TCLASS_GET_CLASS_AUTOPARSING constexpr bool requestDisableAutoLoading = true; #else - // We could get the user choice from: - // - environment variable ROOT_DISABLE_TCLASS_GET_CLASS_AUTOPARSING - // - rootrc key Root.TClass.GetClass.AutoParsing - // - TClass::SetGetClassAutoParsing - constexpr bool requestDisableAutoLoading = false; + static const bool requestDisableAutoLoading = !gEnv->GetValue("Root.TClass.GetClass.AutoParsing", true); #endif if (requestDisableAutoLoading) disableAutoParsing = true; From 214f51314e026ae40b0a677039d7f22e2fc94e04 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 14 Apr 2025 17:19:07 -0500 Subject: [PATCH 5/5] TCling: Register and print list of autoloaded libraries. Use `gInterpreter->Print(autoloaded);` to print a list of the libraries that have been automaticaly loaded during TClass::GetClass and due to a symbol requested during code interpretation. --- core/base/src/TROOT.cxx | 3 +++ core/meta/inc/TInterpreter.h | 1 + core/metacling/src/TCling.cxx | 14 ++++++++++++++ core/metacling/src/TCling.h | 3 +++ 4 files changed, 21 insertions(+) diff --git a/core/base/src/TROOT.cxx b/core/base/src/TROOT.cxx index b24925bf966e9..5760dc3438b9e 100644 --- a/core/base/src/TROOT.cxx +++ b/core/base/src/TROOT.cxx @@ -2202,6 +2202,9 @@ Int_t TROOT::LoadClass(const char * /*classname*/, const char *libname, // TSystem::Load returns 1 when the library was already loaded, return success in this case. if (err == 1) err = 0; + if (err == 0) + // Register the Autoloading of the library + gCling->RegisterAutoLoadedLibrary(libname); return err; } } else { diff --git a/core/meta/inc/TInterpreter.h b/core/meta/inc/TInterpreter.h index 59f2a09b91039..94980fdb0a7fc 100644 --- a/core/meta/inc/TInterpreter.h +++ b/core/meta/inc/TInterpreter.h @@ -194,6 +194,7 @@ class TInterpreter : public TNamed { virtual void AddAvailableIndentifiers(TSeqCollection&) = 0; virtual void RegisterTClassUpdate(TClass *oldcl,DictFuncPtr_t dict) = 0; virtual void UnRegisterTClassUpdate(const TClass *oldcl) = 0; + virtual void RegisterAutoLoadedLibrary(const char *libname) = 0; virtual Int_t SetClassSharedLibs(const char *cls, const char *libs) = 0; virtual void SetGetline(const char*(*getlineFunc)(const char* prompt), void (*histaddFunc)(const char* line)) = 0; diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index 9db881ce23343..991b52cda289b 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -2676,6 +2676,11 @@ void TCling::Print(Option_t *option) const for (auto & cls : fAutoParseClasses) { std::cout << " " << cls << std::endl; } + } else if (!strcmp(option, "autoloaded")) { + std::cout << "Auto loaded libraries:" << std::endl; + for (auto & lib : fAutoLoadedLibraries) { + std::cout << " " << lib << std::endl; + } } else { ::Error("TCling::Print", "Unknown option '%s'", option); } @@ -3455,6 +3460,14 @@ static bool StartsWithStrLit(const char *haystack, const char (&needle)[N]) { } } +//////////////////////////////////////////////////////////////////////////////// +/// Register that a library was autoloaded either to provide a 'missing' symbol +/// or to provide a class (see TClass::GetClass and TROOT::LoadClass). +void TCling::RegisterAutoLoadedLibrary(const char *libname) +{ + fAutoLoadedLibraries.insert(libname); +} + //////////////////////////////////////////////////////////////////////////////// /// Register a new shared library name with the interpreter; add it to /// fSharedLibs. @@ -6671,6 +6684,7 @@ void* TCling::LazyFunctionCreatorAutoload(const std::string& mangled_name) { if (!LibLoader(libName)) return nullptr; + fAutoLoadedLibraries.insert(libName); return llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(dlsym_mangled_name); } diff --git a/core/metacling/src/TCling.h b/core/metacling/src/TCling.h index 3d037f2f06876..5f9ee0b43bee7 100644 --- a/core/metacling/src/TCling.h +++ b/core/metacling/src/TCling.h @@ -122,6 +122,7 @@ class TCling final : public TInterpreter { std::set fPayloads; // Set of payloads std::set fParsedPayloadsAddresses; // Set of payloads which were parsed std::set fAutoParseClasses; // Set of classes for which we autoparsed a header + std::set fAutoLoadedLibraries; // Set of libraries that were autoloaded std::hash fStringHashFunction; // A simple hashing function std::unordered_set fNSFromRootmaps; // Collection of namespaces fwd declared in the rootmaps TObjArray* fRootmapFiles; // Loaded rootmap files. @@ -259,6 +260,8 @@ class TCling final : public TInterpreter { void RegisterTClassUpdate(TClass *oldcl,DictFuncPtr_t dict) final; void UnRegisterTClassUpdate(const TClass *oldcl) final; + void RegisterAutoLoadedLibrary(const char *libname) final; + Int_t SetClassSharedLibs(const char *cls, const char *libs) final; void SetGetline(const char * (*getlineFunc)(const char* prompt), void (*histaddFunc)(const char* line)) final;