Index: include/lldb/Core/Mangled.h =================================================================== --- include/lldb/Core/Mangled.h +++ 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,37 @@ //---------------------------------------------------------------------- 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 + /// The rich mangling info on success, null otherwise. Expect the pointer + /// to be valid only until the next call to this funtion. + //---------------------------------------------------------------------- + bool DemangleWithRichManglingInfo(RichManglingContext &context, + SkipMangledNameFn *skip_mangled_name); + private: //---------------------------------------------------------------------- /// Mangled member variables. Index: include/lldb/Core/RichManglingContext.h =================================================================== --- /dev/null +++ include/lldb/Core/RichManglingContext.h @@ -0,0 +1,129 @@ +//===-- 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 and can be obtained via GetBuffer() and 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 and can be obtained via GetBuffer() and GetBufferRef(). + void ParseFunctionDeclContextName(); + + /// Get the entire demangled name. The result will overwrite the + /// internal buffer and can be obtained via GetBuffer() and 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"); + switch (m_provider) { + case ItaniumPartialDemangler: + return llvm::StringRef(m_IPD_buf, m_IPD_str_len); + case PluginCxxLanguage: + return m_cxx_method_str.GetStringRef(); + case None: + return llvm::StringRef(); + } + } + + /// Obtain a ConstString that points to a permanent copy of the internal + /// buffer, which holds the result of the most recent ParseXy() operation. + ConstString GetBuffer() const { + assert(m_provider != None && "Initialize a provider first"); + switch (m_provider) { + case ItaniumPartialDemangler: + return ConstString(m_IPD_buf); + case PluginCxxLanguage: + return m_cxx_method_str; + case None: + return ConstString(); + } + } + +private: + enum InfoProvider { None, ItaniumPartialDemangler, PluginCxxLanguage }; + + /// Selects the rich mangling info provider. + InfoProvider m_provider; + + /// 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; + ConstString m_cxx_method_str; + + /// 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 had 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: include/lldb/Symbol/Symtab.h =================================================================== --- include/lldb/Symbol/Symtab.h +++ 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 &MC); + + void RegisterBacklogEntry(const NameToIndexMap::Entry &entry, + const char *decl_context, + const std::set &class_contexts); + DISALLOW_COPY_AND_ASSIGN(Symtab); }; Index: include/lldb/lldb-forward.h =================================================================== --- include/lldb/lldb-forward.h +++ 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.xcodeproj/project.pbxproj =================================================================== --- lldb.xcodeproj/project.pbxproj +++ lldb.xcodeproj/project.pbxproj @@ -489,6 +489,8 @@ 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 */; }; + 4FBC04ED211A06200015A814 /* RichManglingContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4FBC04EC211A06200015A814 /* RichManglingContext.cpp */; }; + 4FBC04EF211A06820015A814 /* RichManglingContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 4FBC04EE211A06820015A814 /* RichManglingContext.h */; }; 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 */; }; @@ -2196,6 +2198,8 @@ 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 = ""; }; + 4FBC04EC211A06200015A814 /* RichManglingContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RichManglingContext.cpp; path = source/Core/RichManglingContext.cpp; sourceTree = ""; }; + 4FBC04EE211A06820015A814 /* RichManglingContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RichManglingContext.h; path = include/lldb/Core/RichManglingContext.h; 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 = ""; }; @@ -5033,6 +5037,8 @@ 2626B6AD143E1BEA00EF935C /* RangeMap.h */, 26C6886D137880B900407EDF /* RegisterValue.h */, 26C6886E137880C400407EDF /* RegisterValue.cpp */, + 4FBC04EE211A06820015A814 /* RichManglingContext.h */, + 4FBC04EC211A06200015A814 /* RichManglingContext.cpp */, 26BC7D7410F1B77400F91463 /* Scalar.h */, 26BC7E8D10F1B85900F91463 /* Scalar.cpp */, 26BC7CF910F1B71400F91463 /* SearchFilter.h */, @@ -6943,6 +6949,7 @@ 267F68541CC02E920086832B /* RegisterContextLinux_s390x.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 */, @@ -7578,6 +7585,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: source/Core/CMakeLists.txt =================================================================== --- source/Core/CMakeLists.txt +++ source/Core/CMakeLists.txt @@ -35,6 +35,7 @@ Opcode.cpp PluginManager.cpp RegisterValue.cpp + RichManglingContext.cpp Scalar.cpp SearchFilter.cpp Section.cpp Index: source/Core/Mangled.cpp =================================================================== --- source/Core/Mangled.cpp +++ 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" @@ -232,6 +233,132 @@ } } +//---------------------------------------------------------------------- +// Local helpers for different demangling implementations. +//---------------------------------------------------------------------- +namespace { + +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 +} + +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; +} +} // namespace + +//---------------------------------------------------------------------- +// 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. + llvm::StringRef M = m_mangled.GetStringRef(); + ManglingScheme S = cstring_mangling_scheme(M.data()); + if (skip_mangled_name && skip_mangled_name(M, S)) + return false; + + switch (S) { + 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. Connect the counterparts in the + // string pool to accelerate subsequent 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.data())) { + // Connect the counterparts in the string pool to accelerate + // subsequent 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 @@ -248,8 +375,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 +384,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); - } + 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: source/Core/RichManglingContext.cpp =================================================================== --- /dev/null +++ source/Core/RichManglingContext.cpp @@ -0,0 +1,175 @@ +//===-- 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, GetBuffer()); + } 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; + } + } +} + +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_cxx_method_str = ConstString( + 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_cxx_method_str = ConstString( + 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_cxx_method_str = ConstString( + get(m_cxx_method_parser)->GetFullName()); + return; + case None: + return; + } +} Index: source/Symbol/Symtab.cpp =================================================================== --- source/Symbol/Symtab.cpp +++ 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 MC; + 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(MC, lldb_skip_name)) + RegisterMangledNameEntry(entry, class_contexts, backlog, MC); } } + // 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,69 @@ } } +void Symtab::RegisterMangledNameEntry( + NameToIndexMap::Entry &entry, std::set &class_contexts, + std::vector> &backlog, + RichManglingContext &MC) { + // Only register functions that have a base name. + MC.ParseFunctionBaseName(); + llvm::StringRef base_name = MC.GetBufferRef(); + if (base_name.empty()) + return; + + // The base name will be our entry's name. + entry.cstring = ConstString(base_name); + + MC.ParseFunctionDeclContextName(); + ConstString decl_context = MC.GetBuffer(); + + // Register functions with no context. + if (decl_context.IsEmpty()) { + // 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; + } + + // See if we already know the context name. + auto it = class_contexts.find(decl_context.GetCString()); + + // Register constructors and destructors. They are methods and create + // declaration contexts. + if (MC.IsCtorOrDtor()) { + m_method_to_index.Append(entry); + if (it == class_contexts.end()) + class_contexts.insert(it, decl_context.GetCString()); + 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.GetCString())); +} + +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();