Index: lldb/trunk/include/lldb/Core/Mangled.h =================================================================== --- lldb/trunk/include/lldb/Core/Mangled.h +++ lldb/trunk/include/lldb/Core/Mangled.h @@ -11,18 +11,15 @@ #define liblldb_Mangled_h_ #if defined(__cplusplus) +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" + #include "lldb/Utility/ConstString.h" -#include "lldb/lldb-enumerations.h" // for LanguageType -#include "llvm/ADT/StringRef.h" // for StringRef -#include // for size_t +#include "llvm/ADT/StringRef.h" -namespace lldb_private { -class RegularExpression; -} -namespace lldb_private { -class Stream; -} +#include +#include namespace lldb_private { @@ -238,7 +235,6 @@ return true; return GetDemangledName(language) == name; } - bool NameMatches(const RegularExpression ®ex, lldb::LanguageType language) const; @@ -300,6 +296,36 @@ //---------------------------------------------------------------------- lldb::LanguageType GuessLanguage() const; + /// Function signature for filtering mangled names. + using SkipMangledNameFn = bool(llvm::StringRef, ManglingScheme); + + //---------------------------------------------------------------------- + /// Trigger explicit demangling to obtain rich mangling information. This is + /// optimized for batch processing while populating a name index. To get the + /// pure demangled name string for a single entity, use GetDemangledName() + /// instead. + /// + /// For names that match the Itanium mangling scheme, this uses LLVM's + /// ItaniumPartialDemangler. All other names fall back to LLDB's builtin + /// parser currently. + /// + /// This function is thread-safe when used with different \a context + /// instances in different threads. + /// + /// @param[in] context + /// The context for this function. A single instance can be stack- + /// allocated in the caller's frame and used for multiple calls. + /// + /// @param[in] skip_mangled_name + /// A filtering function for skipping entities based on name and mangling + /// scheme. This can be null if unused. + /// + /// @return + /// True on success, false otherwise. + //---------------------------------------------------------------------- + bool DemangleWithRichManglingInfo(RichManglingContext &context, + SkipMangledNameFn *skip_mangled_name); + private: //---------------------------------------------------------------------- /// Mangled member variables. Index: lldb/trunk/include/lldb/Core/RichManglingContext.h =================================================================== --- lldb/trunk/include/lldb/Core/RichManglingContext.h +++ lldb/trunk/include/lldb/Core/RichManglingContext.h @@ -0,0 +1,110 @@ +//===-- RichManglingContext.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RichManglingContext_h_ +#define liblldb_RichManglingContext_h_ + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" + +#include "lldb/Utility/ConstString.h" + +#include "llvm/ADT/Any.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Demangle/Demangle.h" + +namespace lldb_private { + +/// Uniform wrapper for access to rich mangling information from different +/// providers. See Mangled::DemangleWithRichManglingInfo() +class RichManglingContext { +public: + RichManglingContext() + : m_provider(None), m_ipd_buf_size(2048), m_ipd_str_len(0) { + m_ipd_buf = static_cast(std::malloc(m_ipd_buf_size)); + m_ipd_buf[m_ipd_str_len] = '\0'; + } + + ~RichManglingContext() { std::free(m_ipd_buf); } + + /// Use the ItaniumPartialDemangler to obtain rich mangling information from + /// the given mangled name. + bool FromItaniumName(const ConstString &mangled); + + /// Use the legacy language parser implementation to obtain rich mangling + /// information from the given demangled name. + bool FromCxxMethodName(const ConstString &demangled); + + /// If this symbol describes a constructor or destructor. + bool IsCtorOrDtor() const; + + /// If this symbol describes a function. + bool IsFunction() const; + + /// Get the base name of a function. This doesn't include trailing template + /// arguments, ie "a::b" gives "b". The result will overwrite the + /// internal buffer. It can be obtained via GetBufferRef(). + void ParseFunctionBaseName(); + + /// Get the context name for a function. For "a::b::c", this function returns + /// "a::b". The result will overwrite the internal buffer. It can be obtained + /// via GetBufferRef(). + void ParseFunctionDeclContextName(); + + /// Get the entire demangled name. The result will overwrite the internal + /// buffer. It can be obtained via GetBufferRef(). + void ParseFullName(); + + /// Obtain a StringRef to the internal buffer that holds the result of the + /// most recent ParseXy() operation. The next ParseXy() call invalidates it. + llvm::StringRef GetBufferRef() const { + assert(m_provider != None && "Initialize a provider first"); + return m_buffer; + } + +private: + enum InfoProvider { None, ItaniumPartialDemangler, PluginCxxLanguage }; + + /// Selects the rich mangling info provider. + InfoProvider m_provider; + + /// Reference to the buffer used for results of ParseXy() operations. + llvm::StringRef m_buffer; + + /// Members for ItaniumPartialDemangler + llvm::ItaniumPartialDemangler m_ipd; + char *m_ipd_buf; + size_t m_ipd_buf_size; + size_t m_ipd_str_len; + + /// Members for PluginCxxLanguage + /// Cannot forward declare inner class CPlusPlusLanguage::MethodName. The + /// respective header is in Plugins and including it from here causes cyclic + /// dependency. Instead keep a llvm::Any and cast it on-access in the cpp. + llvm::Any m_cxx_method_parser; + + /// Clean up memory and set a new info provider for this instance. + void ResetProvider(InfoProvider new_provider); + + /// Uniform handling of string buffers for ItaniumPartialDemangler. + void processIPDStrResult(char *ipd_res, size_t res_len); + + /// Cast the given parser to the given type. Ideally we would have a type + /// trait to deduce \a ParserT from a given InfoProvider, but unfortunately we + /// can't access CPlusPlusLanguage::MethodName from within the header. + template static ParserT *get(llvm::Any parser) { + assert(parser.hasValue()); + assert(llvm::any_isa(parser)); + return llvm::any_cast(parser); + } +}; + +} // namespace lldb_private + +#endif Index: lldb/trunk/include/lldb/Symbol/Symtab.h =================================================================== --- lldb/trunk/include/lldb/Symbol/Symtab.h +++ lldb/trunk/include/lldb/Symbol/Symtab.h @@ -197,6 +197,15 @@ void SymbolIndicesToSymbolContextList(std::vector &symbol_indexes, SymbolContextList &sc_list); + void RegisterMangledNameEntry( + NameToIndexMap::Entry &entry, std::set &class_contexts, + std::vector> &backlog, + RichManglingContext &rmc); + + void RegisterBacklogEntry(const NameToIndexMap::Entry &entry, + const char *decl_context, + const std::set &class_contexts); + DISALLOW_COPY_AND_ASSIGN(Symtab); }; Index: lldb/trunk/include/lldb/lldb-forward.h =================================================================== --- lldb/trunk/include/lldb/lldb-forward.h +++ lldb/trunk/include/lldb/lldb-forward.h @@ -191,6 +191,7 @@ class RegisterValue; class RegularExpression; class REPL; +class RichManglingContext; class Scalar; class ScriptInterpreter; class ScriptInterpreterLocker; @@ -492,5 +493,15 @@ } // namespace lldb +//---------------------------------------------------------------------- +// llvm forward declarations +//---------------------------------------------------------------------- +namespace llvm { + +struct ItaniumPartialDemangler; +class StringRef; + +} // namespace llvm + #endif // #if defined(__cplusplus) #endif // LLDB_lldb_forward_h_ Index: lldb/trunk/lldb.xcodeproj/project.pbxproj =================================================================== --- lldb/trunk/lldb.xcodeproj/project.pbxproj +++ lldb/trunk/lldb.xcodeproj/project.pbxproj @@ -487,6 +487,9 @@ 8C3BD9961EF45DA50016C343 /* MainThreadCheckerRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C3BD9951EF45D9B0016C343 /* MainThreadCheckerRuntime.cpp */; }; 2689004313353E0400698AC0 /* Mangled.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8010F1B85900F91463 /* Mangled.cpp */; }; 4F29D3CF21010FA3003B549A /* MangledTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F29D3CD21010F84003B549A /* MangledTest.cpp */; }; + 4FBC04EF211A06820015A814 /* RichManglingContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 4FBC04EE211A06820015A814 /* RichManglingContext.h */; }; + 4FBC04ED211A06200015A814 /* RichManglingContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4FBC04EC211A06200015A814 /* RichManglingContext.cpp */; }; + 4FBC04F5211A13770015A814 /* RichManglingContextTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4FBC04F3211A0F0F0015A814 /* RichManglingContextTest.cpp */; }; 4CD44CFC20B37C440003557C /* ManualDWARFIndex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CD44CF920B37C440003557C /* ManualDWARFIndex.cpp */; }; 49DCF702170E70120092F75E /* Materializer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 49DCF700170E70120092F75E /* Materializer.cpp */; }; 2690B3711381D5C300ECFBAE /* Memory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2690B3701381D5C300ECFBAE /* Memory.cpp */; }; @@ -2198,6 +2201,9 @@ 26BC7E8010F1B85900F91463 /* Mangled.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mangled.cpp; path = source/Core/Mangled.cpp; sourceTree = ""; }; 26BC7D6910F1B77400F91463 /* Mangled.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Mangled.h; path = include/lldb/Core/Mangled.h; sourceTree = ""; }; 4F29D3CD21010F84003B549A /* MangledTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MangledTest.cpp; sourceTree = ""; }; + 4FBC04EE211A06820015A814 /* RichManglingContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RichManglingContext.h; path = include/lldb/Core/RichManglingContext.h; sourceTree = ""; }; + 4FBC04EC211A06200015A814 /* RichManglingContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RichManglingContext.cpp; path = source/Core/RichManglingContext.cpp; sourceTree = ""; }; + 4FBC04F3211A0F0F0015A814 /* RichManglingContextTest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = RichManglingContextTest.cpp; sourceTree = ""; }; 4CD44CF920B37C440003557C /* ManualDWARFIndex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ManualDWARFIndex.cpp; sourceTree = ""; }; 4CD44D0020B37C580003557C /* ManualDWARFIndex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ManualDWARFIndex.h; sourceTree = ""; }; 2682100C143A59AE004BCF2D /* MappedHash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MappedHash.h; path = include/lldb/Core/MappedHash.h; sourceTree = ""; }; @@ -3676,13 +3682,14 @@ 23CB14E51D66CBEB00EDDDE1 /* Core */ = { isa = PBXGroup; children = ( + 23CB14E61D66CC0E00EDDDE1 /* BroadcasterTest.cpp */, + 23CB14E71D66CC0E00EDDDE1 /* CMakeLists.txt */, + 23CB14E81D66CC0E00EDDDE1 /* DataExtractorTest.cpp */, 58A080B12112AB2200D5580F /* HighlighterTest.cpp */, - 4F29D3CD21010F84003B549A /* MangledTest.cpp */, 9A3D43E31F3237D500EB767C /* ListenerTest.cpp */, + 4F29D3CD21010F84003B549A /* MangledTest.cpp */, + 4FBC04F3211A0F0F0015A814 /* RichManglingContextTest.cpp */, 9A3D43E11F3237D500EB767C /* StreamCallbackTest.cpp */, - 23CB14E71D66CC0E00EDDDE1 /* CMakeLists.txt */, - 23CB14E61D66CC0E00EDDDE1 /* BroadcasterTest.cpp */, - 23CB14E81D66CC0E00EDDDE1 /* DataExtractorTest.cpp */, ); path = Core; sourceTree = ""; @@ -5047,6 +5054,8 @@ 26BC7D7110F1B77400F91463 /* PluginManager.h */, 26BC7E8A10F1B85900F91463 /* PluginManager.cpp */, 2626B6AD143E1BEA00EF935C /* RangeMap.h */, + 4FBC04EE211A06820015A814 /* RichManglingContext.h */, + 4FBC04EC211A06200015A814 /* RichManglingContext.cpp */, 26BC7CF910F1B71400F91463 /* SearchFilter.h */, 26BC7E1510F1B83100F91463 /* SearchFilter.cpp */, 26BC7D7510F1B77400F91463 /* Section.h */, @@ -6955,6 +6964,7 @@ 2619C4862107A9A2009CDE81 /* RegisterContextMinidump_ARM64.h in Headers */, AF235EB11FBE77B6009C5541 /* RegisterContextPOSIX_ppc64le.h in Headers */, 267F68501CC02E270086832B /* RegisterContextPOSIXCore_s390x.h in Headers */, + 4FBC04EF211A06820015A814 /* RichManglingContext.h in Headers */, 4984BA181B979C08008658D4 /* ExpressionVariable.h in Headers */, 26C7C4841BFFEA7E009BD01F /* WindowsMiniDump.h in Headers */, 30B38A001CAAA6D7009524E3 /* ClangUtil.h in Headers */, @@ -7473,6 +7483,7 @@ 9A2057181F3B861400F6C293 /* TestType.cpp in Sources */, 9A2057171F3B861400F6C293 /* TestDWARFCallFrameInfo.cpp in Sources */, 4F29D3CF21010FA3003B549A /* MangledTest.cpp in Sources */, + 4FBC04F5211A13770015A814 /* RichManglingContextTest.cpp in Sources */, 9A3D43EC1F3237F900EB767C /* ListenerTest.cpp in Sources */, 9A3D43DC1F3151C400EB767C /* TimeoutTest.cpp in Sources */, 9A3D43D61F3151C400EB767C /* ConstStringTest.cpp in Sources */, @@ -7590,6 +7601,7 @@ 4C0083401B9F9BA900D5CF24 /* UtilityFunction.cpp in Sources */, AF415AE71D949E4400FCE0D4 /* x86AssemblyInspectionEngine.cpp in Sources */, 26474CCD18D0CB5B0073DEBA /* RegisterContextPOSIX_x86.cpp in Sources */, + 4FBC04ED211A06200015A814 /* RichManglingContext.cpp in Sources */, AEB0E4591BD6E9F800B24093 /* LLVMUserExpression.cpp in Sources */, 2689FFEF13353DB600698AC0 /* Breakpoint.cpp in Sources */, 267A47FB1B1411C40021A5BC /* NativeRegisterContext.cpp in Sources */, Index: lldb/trunk/source/Core/CMakeLists.txt =================================================================== --- lldb/trunk/source/Core/CMakeLists.txt +++ lldb/trunk/source/Core/CMakeLists.txt @@ -34,6 +34,7 @@ ModuleList.cpp Opcode.cpp PluginManager.cpp + RichManglingContext.cpp SearchFilter.cpp Section.cpp SourceManager.cpp Index: lldb/trunk/source/Core/Mangled.cpp =================================================================== --- lldb/trunk/source/Core/Mangled.cpp +++ lldb/trunk/source/Core/Mangled.cpp @@ -16,6 +16,7 @@ #pragma comment(lib, "dbghelp.lib") #endif +#include "lldb/Core/RichManglingContext.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Logging.h" @@ -233,6 +234,128 @@ } //---------------------------------------------------------------------- +// Local helpers for different demangling implementations. +//---------------------------------------------------------------------- +static char *GetMSVCDemangledStr(const char *M) { +#if defined(_MSC_VER) + const size_t demangled_length = 2048; + char *demangled_cstr = static_cast(::malloc(demangled_length)); + ::ZeroMemory(demangled_cstr, demangled_length); + DWORD result = safeUndecorateName(M, demangled_cstr, demangled_length); + + if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE)) { + if (demangled_cstr && demangled_cstr[0]) + log->Printf("demangled msvc: %s -> \"%s\"", M, demangled_cstr); + else + log->Printf("demangled msvc: %s -> error: 0x%lu", M, result); + } + + if (result != 0) { + return demangled_cstr; + } else { + ::free(demangled_cstr); + return nullptr; + } +#else + return nullptr; +#endif +} + +static char *GetItaniumDemangledStr(const char *M, + llvm::ItaniumPartialDemangler &ipd) { + char *demangled_cstr = nullptr; + bool err = ipd.partialDemangle(M); + if (!err) { + // Default buffer and size (will realloc in case it's too small). + size_t demangled_size = 80; + demangled_cstr = static_cast(std::malloc(demangled_size)); + demangled_cstr = ipd.finishDemangle(demangled_cstr, &demangled_size); + + assert(demangled_cstr && + "finishDemangle must always succeed if partialDemangle did"); + assert(demangled_cstr[demangled_size - 1] == '\0' && + "Expected demangled_size to return length including trailing null"); + } + + if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE)) { + if (demangled_cstr) + log->Printf("demangled itanium: %s -> \"%s\"", M, demangled_cstr); + else + log->Printf("demangled itanium: %s -> error: failed to demangle", M); + } + + return demangled_cstr; +} + +//---------------------------------------------------------------------- +// Explicit demangling for scheduled requests during batch processing. This +// makes use of ItaniumPartialDemangler's rich demangle info +//---------------------------------------------------------------------- +bool Mangled::DemangleWithRichManglingInfo( + RichManglingContext &context, SkipMangledNameFn *skip_mangled_name) { + // We need to generate and cache the demangled name. + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, + "Mangled::DemangleWithRichNameIndexInfo (m_mangled = %s)", + m_mangled.GetCString()); + + // Others are not meant to arrive here. ObjC names or C's main() for example + // have their names stored in m_demangled, while m_mangled is empty. + assert(m_mangled); + + // Check whether or not we are interested in this name at all. + ManglingScheme scheme = cstring_mangling_scheme(m_mangled.GetCString()); + if (skip_mangled_name && skip_mangled_name(m_mangled.GetStringRef(), scheme)) + return false; + + switch (scheme) { + case eManglingSchemeNone: + // The current mangled_name_filter would allow llvm_unreachable here. + return false; + + case eManglingSchemeItanium: + // We want the rich mangling info here, so we don't care whether or not + // there is a demangled string in the pool already. + if (context.FromItaniumName(m_mangled)) { + // If we got an info, we have a name. Copy to string pool and connect the + // counterparts to accelerate later access in GetDemangledName(). + context.ParseFullName(); + m_demangled.SetStringWithMangledCounterpart(context.GetBufferRef(), + m_mangled); + return true; + } else { + m_demangled.SetCString(""); + return false; + } + + case eManglingSchemeMSVC: { + // We have no rich mangling for MSVC-mangled names yet, so first try to + // demangle it if necessary. + if (!m_demangled && !m_mangled.GetMangledCounterpart(m_demangled)) { + if (char *d = GetMSVCDemangledStr(m_mangled.GetCString())) { + // If we got an info, we have a name. Copy to string pool and connect + // the counterparts to accelerate later access in GetDemangledName(). + m_demangled.SetStringWithMangledCounterpart(llvm::StringRef(d), + m_mangled); + ::free(d); + } else { + m_demangled.SetCString(""); + } + } + + if (m_demangled.IsEmpty()) { + // Cannot demangle it, so don't try parsing. + return false; + } else { + // Demangled successfully, we can try and parse it with + // CPlusPlusLanguage::MethodName. + return context.FromCxxMethodName(m_demangled); + } + } + } +} + +//---------------------------------------------------------------------- // Generate the demangled name on demand using this accessor. Code in this // class will need to use this accessor if it wishes to decode the demangled // name. The result is cached and will be kept until a new string value is @@ -248,8 +371,6 @@ Timer scoped_timer(func_cat, "Mangled::GetDemangledName (m_mangled = %s)", m_mangled.GetCString()); - Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE); - // Don't bother running anything that isn't mangled const char *mangled_name = m_mangled.GetCString(); ManglingScheme mangling_scheme{cstring_mangling_scheme(mangled_name)}; @@ -259,56 +380,20 @@ // add it to our map. char *demangled_name = nullptr; switch (mangling_scheme) { - case eManglingSchemeMSVC: { -#if defined(_MSC_VER) - if (log) - log->Printf("demangle msvc: %s", mangled_name); - const size_t demangled_length = 2048; - demangled_name = static_cast(::malloc(demangled_length)); - ::ZeroMemory(demangled_name, demangled_length); - DWORD result = - safeUndecorateName(mangled_name, demangled_name, demangled_length); - if (log) { - if (demangled_name && demangled_name[0]) - log->Printf("demangled msvc: %s -> \"%s\"", mangled_name, - demangled_name); - else - log->Printf("demangled msvc: %s -> error: 0x%lu", mangled_name, - result); - } - - if (result == 0) { - free(demangled_name); - demangled_name = nullptr; - } -#endif + case eManglingSchemeMSVC: + demangled_name = GetMSVCDemangledStr(mangled_name); break; - } case eManglingSchemeItanium: { - llvm::ItaniumPartialDemangler IPD; - bool demangle_err = IPD.partialDemangle(mangled_name); - if (!demangle_err) { - // Default buffer and size (realloc is used in case it's too small). - size_t demangled_size = 80; - demangled_name = static_cast(::malloc(demangled_size)); - demangled_name = IPD.finishDemangle(demangled_name, &demangled_size); - } - - if (log) { - if (demangled_name) - log->Printf("demangled itanium: %s -> \"%s\"", mangled_name, - demangled_name); - else - log->Printf("demangled itanium: %s -> error: failed to demangle", - mangled_name); - } + llvm::ItaniumPartialDemangler ipd; + demangled_name = GetItaniumDemangledStr(mangled_name, ipd); break; } case eManglingSchemeNone: - break; + llvm_unreachable("eManglingSchemeNone was handled already"); } if (demangled_name) { - m_demangled.SetStringWithMangledCounterpart(demangled_name, m_mangled); + m_demangled.SetStringWithMangledCounterpart( + llvm::StringRef(demangled_name), m_mangled); free(demangled_name); } } Index: lldb/trunk/source/Core/RichManglingContext.cpp =================================================================== --- lldb/trunk/source/Core/RichManglingContext.cpp +++ lldb/trunk/source/Core/RichManglingContext.cpp @@ -0,0 +1,178 @@ +//===-- RichManglingContext.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/RichManglingContext.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Logging.h" + +#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// RichManglingContext +//---------------------------------------------------------------------- +void RichManglingContext::ResetProvider(InfoProvider new_provider) { + // If we want to support parsers for other languages some day, we need a + // switch here to delete the correct parser type. + if (m_cxx_method_parser.hasValue()) { + assert(m_provider == PluginCxxLanguage); + delete get(m_cxx_method_parser); + m_cxx_method_parser.reset(); + } + + assert(new_provider != None && "Only reset to a valid provider"); + m_provider = new_provider; +} + +bool RichManglingContext::FromItaniumName(const ConstString &mangled) { + bool err = m_ipd.partialDemangle(mangled.GetCString()); + if (!err) { + ResetProvider(ItaniumPartialDemangler); + } + + if (Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE)) { + if (!err) { + ParseFullName(); + LLDB_LOG(log, "demangled itanium: {0} -> \"{1}\"", mangled, m_ipd_buf); + } else { + LLDB_LOG(log, "demangled itanium: {0} -> error: failed to demangle", + mangled); + } + } + + return !err; // true == success +} + +bool RichManglingContext::FromCxxMethodName(const ConstString &demangled) { + ResetProvider(PluginCxxLanguage); + m_cxx_method_parser = new CPlusPlusLanguage::MethodName(demangled); + return true; +} + +bool RichManglingContext::IsCtorOrDtor() const { + assert(m_provider != None && "Initialize a provider first"); + switch (m_provider) { + case ItaniumPartialDemangler: + return m_ipd.isCtorOrDtor(); + case PluginCxxLanguage: { + // We can only check for destructors here. + auto base_name = + get(m_cxx_method_parser)->GetBasename(); + return base_name.startswith("~"); + } + case None: + return false; + } +} + +bool RichManglingContext::IsFunction() const { + assert(m_provider != None && "Initialize a provider first"); + switch (m_provider) { + case ItaniumPartialDemangler: + return m_ipd.isFunction(); + case PluginCxxLanguage: + return get(m_cxx_method_parser)->IsValid(); + case None: + return false; + } +} + +void RichManglingContext::processIPDStrResult(char *ipd_res, size_t res_size) { + if (LLVM_UNLIKELY(ipd_res == nullptr)) { + assert(res_size == m_ipd_buf_size && + "Failed IPD queries keep the original size in the N parameter"); + + // Error case: Clear the buffer. + m_ipd_str_len = 0; + m_ipd_buf[m_ipd_str_len] = '\0'; + } else { + // IPD's res_size includes null terminator. + size_t res_len = res_size - 1; + assert(ipd_res[res_len] == '\0' && + "IPD returns null-terminated strings and we rely on that"); + + if (LLVM_UNLIKELY(ipd_res != m_ipd_buf)) { + // Realloc case: Take over the new buffer. + m_ipd_buf = ipd_res; // std::realloc freed or reused the old buffer. + m_ipd_buf_size = + res_size; // Actual buffer may be bigger, but we can't know. + m_ipd_str_len = res_len; + + Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DEMANGLE); + if (log) + log->Printf("ItaniumPartialDemangler Realloc: new buffer size %lu", + m_ipd_buf_size); + } else { + // 99% case: Just remember the string length. + m_ipd_str_len = res_len; + } + } + + m_buffer = llvm::StringRef(m_ipd_buf, m_ipd_str_len); +} + +void RichManglingContext::ParseFunctionBaseName() { + assert(m_provider != None && "Initialize a provider first"); + switch (m_provider) { + case ItaniumPartialDemangler: { + auto n = m_ipd_buf_size; + auto buf = m_ipd.getFunctionBaseName(m_ipd_buf, &n); + processIPDStrResult(buf, n); + return; + } + case PluginCxxLanguage: + m_buffer = + get(m_cxx_method_parser)->GetBasename(); + return; + case None: + return; + } +} + +void RichManglingContext::ParseFunctionDeclContextName() { + assert(m_provider != None && "Initialize a provider first"); + switch (m_provider) { + case ItaniumPartialDemangler: { + auto n = m_ipd_buf_size; + auto buf = m_ipd.getFunctionDeclContextName(m_ipd_buf, &n); + processIPDStrResult(buf, n); + return; + } + case PluginCxxLanguage: + m_buffer = + get(m_cxx_method_parser)->GetContext(); + return; + case None: + return; + } +} + +void RichManglingContext::ParseFullName() { + assert(m_provider != None && "Initialize a provider first"); + switch (m_provider) { + case ItaniumPartialDemangler: { + auto n = m_ipd_buf_size; + auto buf = m_ipd.finishDemangle(m_ipd_buf, &n); + processIPDStrResult(buf, n); + return; + } + case PluginCxxLanguage: + m_buffer = get(m_cxx_method_parser) + ->GetFullName() + .GetStringRef(); + return; + case None: + return; + } +} Index: lldb/trunk/source/Symbol/Symtab.cpp =================================================================== --- lldb/trunk/source/Symbol/Symtab.cpp +++ lldb/trunk/source/Symbol/Symtab.cpp @@ -10,9 +10,10 @@ #include #include -#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" #include "Plugins/Language/ObjC/ObjCLanguage.h" + #include "lldb/Core/Module.h" +#include "lldb/Core/RichManglingContext.h" #include "lldb/Core/STLUtils.h" #include "lldb/Core/Section.h" #include "lldb/Symbol/ObjectFile.h" @@ -23,6 +24,8 @@ #include "lldb/Utility/Stream.h" #include "lldb/Utility/Timer.h" +#include "llvm/ADT/StringRef.h" + using namespace lldb; using namespace lldb_private; @@ -215,6 +218,39 @@ //---------------------------------------------------------------------- // InitNameIndexes //---------------------------------------------------------------------- +static bool lldb_skip_name(llvm::StringRef mangled, + Mangled::ManglingScheme scheme) { + switch (scheme) { + case Mangled::eManglingSchemeItanium: { + if (mangled.size() < 3 || !mangled.startswith("_Z")) + return true; + + // Avoid the following types of symbols in the index. + switch (mangled[2]) { + case 'G': // guard variables + case 'T': // virtual tables, VTT structures, typeinfo structures + names + case 'Z': // named local entities (if we eventually handle + // eSymbolTypeData, we will want this back) + return true; + + default: + break; + } + + // Include this name in the index. + return false; + } + + // No filters for this scheme yet. Include all names in indexing. + case Mangled::eManglingSchemeMSVC: + return false; + + // Don't try and demangle things we can't categorize. + case Mangled::eManglingSchemeNone: + return true; + } +} + void Symtab::InitNameIndexes() { // Protected function, no need to lock mutex... if (!m_name_indexes_computed) { @@ -243,16 +279,19 @@ m_name_to_index.Reserve(actual_count); #endif - NameToIndexMap::Entry entry; - - // The "const char *" in "class_contexts" must come from a - // ConstString::GetCString() + // The "const char *" in "class_contexts" and backlog::value_type::second + // must come from a ConstString::GetCString() std::set class_contexts; - UniqueCStringMap mangled_name_to_index; - std::vector symbol_contexts(num_symbols, nullptr); + std::vector> backlog; + backlog.reserve(num_symbols / 2); + + // Instantiation of the demangler is expensive, so better use a single one + // for all entries during batch processing. + RichManglingContext rmc; + NameToIndexMap::Entry entry; for (entry.value = 0; entry.value < num_symbols; ++entry.value) { - const Symbol *symbol = &m_symbols[entry.value]; + Symbol *symbol = &m_symbols[entry.value]; // Don't let trampolines get into the lookup by name map If we ever need // the trampoline symbols to be searchable by name we can remove this and @@ -261,7 +300,9 @@ if (symbol->IsTrampoline()) continue; - const Mangled &mangled = symbol->GetMangled(); + // If the symbol's name string matched a Mangled::ManglingScheme, it is + // stored in the mangled field. + Mangled &mangled = symbol->GetMangled(); entry.cstring = mangled.GetMangledName(); if (entry.cstring) { m_name_to_index.Append(entry); @@ -274,70 +315,15 @@ m_name_to_index.Append(entry); } - const SymbolType symbol_type = symbol->GetType(); - if (symbol_type == eSymbolTypeCode || - symbol_type == eSymbolTypeResolver) { - llvm::StringRef entry_ref(entry.cstring.GetStringRef()); - if (entry_ref[0] == '_' && entry_ref[1] == 'Z' && - (entry_ref[2] != 'T' && // avoid virtual table, VTT structure, - // typeinfo structure, and typeinfo - // name - entry_ref[2] != 'G' && // avoid guard variables - entry_ref[2] != 'Z')) // named local entities (if we - // eventually handle eSymbolTypeData, - // we will want this back) - { - CPlusPlusLanguage::MethodName cxx_method( - mangled.GetDemangledName(lldb::eLanguageTypeC_plus_plus)); - entry.cstring = ConstString(cxx_method.GetBasename()); - if (entry.cstring) { - // ConstString objects permanently store the string in the pool - // so calling GetCString() on the value gets us a const char * - // that will never go away - const char *const_context = - ConstString(cxx_method.GetContext()).GetCString(); - - if (!const_context || const_context[0] == 0) { - // No context for this function so this has to be a basename - m_basename_to_index.Append(entry); - // If there is no context (no namespaces or class scopes that - // come before the function name) then this also could be a - // fullname. - m_name_to_index.Append(entry); - } else { - entry_ref = entry.cstring.GetStringRef(); - if (entry_ref[0] == '~' || - !cxx_method.GetQualifiers().empty()) { - // The first character of the demangled basename is '~' which - // means we have a class destructor. We can use this - // information to help us know what is a class and what - // isn't. - if (class_contexts.find(const_context) == class_contexts.end()) - class_contexts.insert(const_context); - m_method_to_index.Append(entry); - } else { - if (class_contexts.find(const_context) != - class_contexts.end()) { - // The current decl context is in our "class_contexts" - // which means this is a method on a class - m_method_to_index.Append(entry); - } else { - // We don't know if this is a function basename or a - // method, so put it into a temporary collection so once we - // are done we can look in class_contexts to see if each - // entry is a class or just a function and will put any - // remaining items into m_method_to_index or - // m_basename_to_index as needed - mangled_name_to_index.Append(entry); - symbol_contexts[entry.value] = const_context; - } - } - } - } - } + const SymbolType type = symbol->GetType(); + if (type == eSymbolTypeCode || type == eSymbolTypeResolver) { + if (mangled.DemangleWithRichManglingInfo(rmc, lldb_skip_name)) + RegisterMangledNameEntry(entry, class_contexts, backlog, rmc); } } + // Symbol name strings that didn't match a Mangled::ManglingScheme, are + // stored in the demangled field. entry.cstring = mangled.GetDemangledName(symbol->GetLanguage()); if (entry.cstring) { m_name_to_index.Append(entry); @@ -367,25 +353,10 @@ } } - size_t count; - if (!mangled_name_to_index.IsEmpty()) { - count = mangled_name_to_index.GetSize(); - for (size_t i = 0; i < count; ++i) { - if (mangled_name_to_index.GetValueAtIndex(i, entry.value)) { - entry.cstring = mangled_name_to_index.GetCStringAtIndex(i); - if (symbol_contexts[entry.value] && - class_contexts.find(symbol_contexts[entry.value]) != - class_contexts.end()) { - m_method_to_index.Append(entry); - } else { - // If we got here, we have something that had a context (was inside - // a namespace or class) yet we don't know if the entry - m_method_to_index.Append(entry); - m_basename_to_index.Append(entry); - } - } - } + for (const auto &record : backlog) { + RegisterBacklogEntry(record.first, record.second, class_contexts); } + m_name_to_index.Sort(); m_name_to_index.SizeToFit(); m_selector_to_index.Sort(); @@ -397,6 +368,71 @@ } } +void Symtab::RegisterMangledNameEntry( + NameToIndexMap::Entry &entry, std::set &class_contexts, + std::vector> &backlog, + RichManglingContext &rmc) { + // Only register functions that have a base name. + rmc.ParseFunctionBaseName(); + llvm::StringRef base_name = rmc.GetBufferRef(); + if (base_name.empty()) + return; + + // The base name will be our entry's name. + entry.cstring = ConstString(base_name); + + rmc.ParseFunctionDeclContextName(); + llvm::StringRef decl_context = rmc.GetBufferRef(); + + // Register functions with no context. + if (decl_context.empty()) { + // This has to be a basename + m_basename_to_index.Append(entry); + // If there is no context (no namespaces or class scopes that come before + // the function name) then this also could be a fullname. + m_name_to_index.Append(entry); + return; + } + + // Make sure we have a pool-string pointer and see if we already know the + // context name. + const char *decl_context_ccstr = ConstString(decl_context).GetCString(); + auto it = class_contexts.find(decl_context_ccstr); + + // Register constructors and destructors. They are methods and create + // declaration contexts. + if (rmc.IsCtorOrDtor()) { + m_method_to_index.Append(entry); + if (it == class_contexts.end()) + class_contexts.insert(it, decl_context_ccstr); + return; + } + + // Register regular methods with a known declaration context. + if (it != class_contexts.end()) { + m_method_to_index.Append(entry); + return; + } + + // Regular methods in unknown declaration contexts are put to the backlog. We + // will revisit them once we processed all remaining symbols. + backlog.push_back(std::make_pair(entry, decl_context_ccstr)); +} + +void Symtab::RegisterBacklogEntry( + const NameToIndexMap::Entry &entry, const char *decl_context, + const std::set &class_contexts) { + auto it = class_contexts.find(decl_context); + if (it != class_contexts.end()) { + m_method_to_index.Append(entry); + } else { + // If we got here, we have something that had a context (was inside + // a namespace or class) yet we don't know the entry + m_method_to_index.Append(entry); + m_basename_to_index.Append(entry); + } +} + void Symtab::PreloadSymbols() { std::lock_guard guard(m_mutex); InitNameIndexes(); Index: lldb/trunk/unittests/Core/CMakeLists.txt =================================================================== --- lldb/trunk/unittests/Core/CMakeLists.txt +++ lldb/trunk/unittests/Core/CMakeLists.txt @@ -4,6 +4,7 @@ EventTest.cpp ListenerTest.cpp MangledTest.cpp + RichManglingContextTest.cpp StreamCallbackTest.cpp LINK_LIBS Index: lldb/trunk/unittests/Core/RichManglingContextTest.cpp =================================================================== --- lldb/trunk/unittests/Core/RichManglingContextTest.cpp +++ lldb/trunk/unittests/Core/RichManglingContextTest.cpp @@ -0,0 +1,114 @@ +//===-- RichManglingContextTest.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/RichManglingContext.h" + +#include "lldb/Utility/ConstString.h" + +#include "gtest/gtest.h" + +using namespace lldb; +using namespace lldb_private; + +TEST(RichManglingContextTest, Basic) { + RichManglingContext RMC; + ConstString mangled("_ZN3foo3barEv"); + EXPECT_TRUE(RMC.FromItaniumName(mangled)); + + EXPECT_TRUE(RMC.IsFunction()); + EXPECT_FALSE(RMC.IsCtorOrDtor()); + + RMC.ParseFunctionDeclContextName(); + EXPECT_EQ("foo", RMC.GetBufferRef()); + + RMC.ParseFunctionBaseName(); + EXPECT_EQ("bar", RMC.GetBufferRef()); + + RMC.ParseFullName(); + EXPECT_EQ("foo::bar()", RMC.GetBufferRef()); +} + +TEST(RichManglingContextTest, FromCxxMethodName) { + RichManglingContext ItaniumRMC; + ConstString mangled("_ZN3foo3barEv"); + EXPECT_TRUE(ItaniumRMC.FromItaniumName(mangled)); + + RichManglingContext CxxMethodRMC; + ConstString demangled("foo::bar()"); + EXPECT_TRUE(CxxMethodRMC.FromCxxMethodName(demangled)); + + EXPECT_TRUE(ItaniumRMC.IsFunction() == CxxMethodRMC.IsFunction()); + EXPECT_TRUE(ItaniumRMC.IsCtorOrDtor() == CxxMethodRMC.IsCtorOrDtor()); + + ItaniumRMC.ParseFunctionDeclContextName(); + CxxMethodRMC.ParseFunctionDeclContextName(); + EXPECT_TRUE(ItaniumRMC.GetBufferRef() == CxxMethodRMC.GetBufferRef()); + + ItaniumRMC.ParseFunctionBaseName(); + CxxMethodRMC.ParseFunctionBaseName(); + EXPECT_TRUE(ItaniumRMC.GetBufferRef() == CxxMethodRMC.GetBufferRef()); + + ItaniumRMC.ParseFullName(); + CxxMethodRMC.ParseFullName(); + EXPECT_TRUE(ItaniumRMC.GetBufferRef() == CxxMethodRMC.GetBufferRef()); +} + +TEST(RichManglingContextTest, SwitchProvider) { + RichManglingContext RMC; + llvm::StringRef mangled = "_ZN3foo3barEv"; + llvm::StringRef demangled = "foo::bar()"; + + EXPECT_TRUE(RMC.FromItaniumName(ConstString(mangled))); + RMC.ParseFullName(); + EXPECT_EQ("foo::bar()", RMC.GetBufferRef()); + + EXPECT_TRUE(RMC.FromCxxMethodName(ConstString(demangled))); + RMC.ParseFullName(); + EXPECT_EQ("foo::bar()", RMC.GetBufferRef()); + + EXPECT_TRUE(RMC.FromItaniumName(ConstString(mangled))); + RMC.ParseFullName(); + EXPECT_EQ("foo::bar()", RMC.GetBufferRef()); +} + +TEST(RichManglingContextTest, IPDRealloc) { + // The demangled name should fit into the Itanium default buffer. + const char *short_mangled = "_ZN3foo3barEv"; + + // The demangled name for this will certainly not fit into the default buffer. + const char *long_mangled = + "_ZNK3shk6detail17CallbackPublisherIZNS_5ThrowERKNSt15__exception_" + "ptr13exception_ptrEEUlOT_E_E9SubscribeINS0_9ConcatMapINS0_" + "18CallbackSubscriberIZNS_6GetAllIiNS1_IZZNS_9ConcatMapIZNS_6ConcatIJNS1_" + "IZZNS_3MapIZZNS_7IfEmptyIS9_EEDaS7_ENKUlS6_E_clINS1_IZZNS_4TakeIiEESI_" + "S7_ENKUlS6_E_clINS1_IZZNS_6FilterIZNS_9ElementAtEmEUlS7_E_EESI_S7_" + "ENKUlS6_E_clINS1_IZZNSL_ImEESI_S7_ENKUlS6_E_clINS1_IZNS_4FromINS0_" + "22InfiniteRangeContainerIiEEEESI_S7_EUlS7_E_EEEESI_S6_EUlS7_E_EEEESI_S6_" + "EUlS7_E_EEEESI_S6_EUlS7_E_EEEESI_S6_EUlS7_E_EESI_S7_ENKUlS6_E_clIS14_" + "EESI_S6_EUlS7_E_EERNS1_IZZNSH_IS9_EESI_S7_ENKSK_IS14_EESI_S6_EUlS7_E0_" + "EEEEESI_DpOT_EUlS7_E_EESI_S7_ENKUlS6_E_clINS1_IZNS_5StartIJZNS_" + "4JustIJS19_S1C_EEESI_S1F_EUlvE_ZNS1K_IJS19_S1C_EEESI_S1F_EUlvE0_EEESI_" + "S1F_EUlS7_E_EEEESI_S6_EUlS7_E_EEEESt6vectorIS6_SaIS6_EERKT0_NS_" + "12ElementCountEbEUlS7_E_ZNSD_IiS1Q_EES1T_S1W_S1X_bEUlOS3_E_ZNSD_IiS1Q_" + "EES1T_S1W_S1X_bEUlvE_EES1G_S1O_E25ConcatMapValuesSubscriberEEEDaS7_"; + + RichManglingContext RMC; + + // Demangle the short one and remember the buffer address. + EXPECT_TRUE(RMC.FromItaniumName(ConstString(short_mangled))); + RMC.ParseFullName(); + const char *short_demangled_ptr = RMC.GetBufferRef().data(); + + // Demangle the long one and make sure the buffer address changed. + EXPECT_TRUE(RMC.FromItaniumName(ConstString(long_mangled))); + RMC.ParseFullName(); + const char *long_demangled_ptr = RMC.GetBufferRef().data(); + + EXPECT_TRUE(short_demangled_ptr != long_demangled_ptr); +}