diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h index 36bf454636366..eca8b88788643 100644 --- a/libcxxabi/src/demangle/ItaniumDemangle.h +++ b/libcxxabi/src/demangle/ItaniumDemangle.h @@ -278,20 +278,11 @@ class Node { } void print(OutputBuffer &OB) const { - printLeft(OB); + OB.printLeft(*this); if (RHSComponentCache != Cache::No) - printRight(OB); + OB.printRight(*this); } - // Print the "left" side of this Node into OutputBuffer. - virtual void printLeft(OutputBuffer &) const = 0; - - // Print the "right". This distinction is necessary to represent C++ types - // that appear on the RHS of their subtype, such as arrays or functions. - // Since most types don't have such a component, provide a default - // implementation. - virtual void printRight(OutputBuffer &) const {} - virtual std::string_view getBaseName() const { return {}; } // Silence compiler warnings, this dtor will never be called. @@ -300,6 +291,24 @@ class Node { #ifndef NDEBUG DEMANGLE_DUMP_METHOD void dump() const; #endif + +private: + friend class OutputBuffer; + + // Print the "left" side of this Node into OutputBuffer. + // + // Note, should only be called from OutputBuffer implementations. + // Call \ref OutputBuffer::printLeft instead. + virtual void printLeft(OutputBuffer &) const = 0; + + // Print the "right". This distinction is necessary to represent C++ types + // that appear on the RHS of their subtype, such as arrays or functions. + // Since most types don't have such a component, provide a default + // implementation. + // + // Note, should only be called from OutputBuffer implementations. + // Call \ref OutputBuffer::printRight instead. + virtual void printRight(OutputBuffer &) const {} }; class NodeArray { @@ -444,11 +453,11 @@ class QualType final : public Node { } void printLeft(OutputBuffer &OB) const override { - Child->printLeft(OB); + OB.printLeft(*Child); printQuals(OB); } - void printRight(OutputBuffer &OB) const override { Child->printRight(OB); } + void printRight(OutputBuffer &OB) const override { OB.printRight(*Child); } }; class ConversionOperatorType final : public Node { @@ -477,7 +486,7 @@ class PostfixQualifiedType final : public Node { template void match(Fn F) const { F(Ty, Postfix); } void printLeft(OutputBuffer &OB) const override { - Ty->printLeft(OB); + OB.printLeft(*Ty); OB += Postfix; } }; @@ -563,7 +572,7 @@ struct AbiTagAttr : Node { std::string_view getBaseName() const override { return Base->getBaseName(); } void printLeft(OutputBuffer &OB) const override { - Base->printLeft(OB); + OB.printLeft(*Base); OB += "[abi:"; OB += Tag; OB += "]"; @@ -589,8 +598,6 @@ class ObjCProtoName : public Node { const Node *Ty; std::string_view Protocol; - friend class PointerType; - public: ObjCProtoName(const Node *Ty_, std::string_view Protocol_) : Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {} @@ -602,6 +609,8 @@ class ObjCProtoName : public Node { static_cast(Ty)->getName() == "objc_object"; } + std::string_view getProtocol() const { return Protocol; } + void printLeft(OutputBuffer &OB) const override { Ty->print(OB); OB += "<"; @@ -630,7 +639,7 @@ class PointerType final : public Node { // We rewrite objc_object* into id. if (Pointee->getKind() != KObjCProtoName || !static_cast(Pointee)->isObjCObject()) { - Pointee->printLeft(OB); + OB.printLeft(*Pointee); if (Pointee->hasArray(OB)) OB += " "; if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) @@ -639,7 +648,7 @@ class PointerType final : public Node { } else { const auto *objcProto = static_cast(Pointee); OB += "id<"; - OB += objcProto->Protocol; + OB += objcProto->getProtocol(); OB += ">"; } } @@ -649,7 +658,7 @@ class PointerType final : public Node { !static_cast(Pointee)->isObjCObject()) { if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) OB += ")"; - Pointee->printRight(OB); + OB.printRight(*Pointee); } } }; @@ -715,7 +724,7 @@ class ReferenceType : public Node { std::pair Collapsed = collapse(OB); if (!Collapsed.second) return; - Collapsed.second->printLeft(OB); + OB.printLeft(*Collapsed.second); if (Collapsed.second->hasArray(OB)) OB += " "; if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) @@ -732,7 +741,7 @@ class ReferenceType : public Node { return; if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) OB += ")"; - Collapsed.second->printRight(OB); + OB.printRight(*Collapsed.second); } }; @@ -752,7 +761,7 @@ class PointerToMemberType final : public Node { } void printLeft(OutputBuffer &OB) const override { - MemberType->printLeft(OB); + OB.printLeft(*MemberType); if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) OB += "("; else @@ -764,7 +773,7 @@ class PointerToMemberType final : public Node { void printRight(OutputBuffer &OB) const override { if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) OB += ")"; - MemberType->printRight(OB); + OB.printRight(*MemberType); } }; @@ -784,7 +793,7 @@ class ArrayType final : public Node { bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } bool hasArraySlow(OutputBuffer &) const override { return true; } - void printLeft(OutputBuffer &OB) const override { Base->printLeft(OB); } + void printLeft(OutputBuffer &OB) const override { OB.printLeft(*Base); } void printRight(OutputBuffer &OB) const override { if (OB.back() != ']') @@ -793,7 +802,7 @@ class ArrayType final : public Node { if (Dimension) Dimension->print(OB); OB += "]"; - Base->printRight(OB); + OB.printRight(*Base); } }; @@ -828,7 +837,7 @@ class FunctionType final : public Node { // by printing out the return types's left, then print our parameters, then // finally print right of the return type. void printLeft(OutputBuffer &OB) const override { - Ret->printLeft(OB); + OB.printLeft(*Ret); OB += " "; } @@ -836,7 +845,7 @@ class FunctionType final : public Node { OB.printOpen(); Params.printWithComma(OB); OB.printClose(); - Ret->printRight(OB); + OB.printRight(*Ret); if (CVQuals & QualConst) OB += " const"; @@ -941,6 +950,8 @@ class FunctionEncoding final : public Node { FunctionRefQual getRefQual() const { return RefQual; } NodeArray getParams() const { return Params; } const Node *getReturnType() const { return Ret; } + const Node *getAttrs() const { return Attrs; } + const Node *getRequires() const { return Requires; } bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } bool hasFunctionSlow(OutputBuffer &) const override { return true; } @@ -949,10 +960,11 @@ class FunctionEncoding final : public Node { void printLeft(OutputBuffer &OB) const override { if (Ret) { - Ret->printLeft(OB); + OB.printLeft(*Ret); if (!Ret->hasRHSComponent(OB)) OB += " "; } + Name->print(OB); } @@ -960,8 +972,9 @@ class FunctionEncoding final : public Node { OB.printOpen(); Params.printWithComma(OB); OB.printClose(); + if (Ret) - Ret->printRight(OB); + OB.printRight(*Ret); if (CVQuals & QualConst) OB += " const"; @@ -1301,14 +1314,14 @@ class NonTypeTemplateParamDecl final : public Node { template void match(Fn F) const { F(Name, Type); } void printLeft(OutputBuffer &OB) const override { - Type->printLeft(OB); + OB.printLeft(*Type); if (!Type->hasRHSComponent(OB)) OB += " "; } void printRight(OutputBuffer &OB) const override { Name->print(OB); - Type->printRight(OB); + OB.printRight(*Type); } }; @@ -1353,11 +1366,11 @@ class TemplateParamPackDecl final : public Node { template void match(Fn F) const { F(Param); } void printLeft(OutputBuffer &OB) const override { - Param->printLeft(OB); + OB.printLeft(*Param); OB += "..."; } - void printRight(OutputBuffer &OB) const override { Param->printRight(OB); } + void printRight(OutputBuffer &OB) const override { OB.printRight(*Param); } }; /// An unexpanded parameter pack (either in the expression or type context). If @@ -1424,13 +1437,13 @@ class ParameterPack final : public Node { initializePackExpansion(OB); size_t Idx = OB.CurrentPackIndex; if (Idx < Data.size()) - Data[Idx]->printLeft(OB); + OB.printLeft(*Data[Idx]); } void printRight(OutputBuffer &OB) const override { initializePackExpansion(OB); size_t Idx = OB.CurrentPackIndex; if (Idx < Data.size()) - Data[Idx]->printRight(OB); + OB.printRight(*Data[Idx]); } }; @@ -1588,13 +1601,13 @@ struct ForwardTemplateReference : Node { if (Printing) return; ScopedOverride SavePrinting(Printing, true); - Ref->printLeft(OB); + OB.printLeft(*Ref); } void printRight(OutputBuffer &OB) const override { if (Printing) return; ScopedOverride SavePrinting(Printing, true); - Ref->printRight(OB); + OB.printRight(*Ref); } }; @@ -1746,7 +1759,7 @@ class DtorName : public Node { void printLeft(OutputBuffer &OB) const override { OB += "~"; - Base->printLeft(OB); + OB.printLeft(*Base); } }; @@ -2026,7 +2039,7 @@ class CastExpr : public Node { { ScopedOverride LT(OB.GtIsGt, 0); OB += "<"; - To->printLeft(OB); + OB.printLeft(*To); OB += ">"; } OB.printOpen(); @@ -5946,6 +5959,10 @@ struct ManglingParser : AbstractManglingParser, Alloc> { Alloc>::AbstractManglingParser; }; +inline void OutputBuffer::printLeft(const Node &N) { N.printLeft(*this); } + +inline void OutputBuffer::printRight(const Node &N) { N.printRight(*this); } + DEMANGLE_NAMESPACE_END #ifdef _LIBCXXABI_COMPILER_CLANG diff --git a/libcxxabi/src/demangle/Utility.h b/libcxxabi/src/demangle/Utility.h index f1fad35d60d98..511983ad40f7a 100644 --- a/libcxxabi/src/demangle/Utility.h +++ b/libcxxabi/src/demangle/Utility.h @@ -27,6 +27,8 @@ DEMANGLE_NAMESPACE_BEGIN +class Node; + // Stream that AST nodes write their string representation into after the AST // has been parsed. class OutputBuffer { @@ -79,10 +81,24 @@ class OutputBuffer { OutputBuffer(const OutputBuffer &) = delete; OutputBuffer &operator=(const OutputBuffer &) = delete; + virtual ~OutputBuffer() {} + operator std::string_view() const { return std::string_view(Buffer, CurrentPosition); } + /// Called by the demangler when printing the demangle tree. By + /// default calls into \c Node::print{Left|Right} but can be overriden + /// by clients to track additional state when printing the demangled name. + virtual void printLeft(const Node &N); + virtual void printRight(const Node &N); + + /// Called when we write to this object anywhere other than the end. + virtual void notifyInsertion(size_t /*Position*/, size_t /*Count*/) {} + + /// Called when we make the \c CurrentPosition of this object smaller. + virtual void notifyDeletion(size_t /*OldPos*/, size_t /*NewPos*/) {} + /// If a ParameterPackExpansion (or similar type) is encountered, the offset /// into the pack that we're currently printing. unsigned CurrentPackIndex = std::numeric_limits::max(); @@ -126,6 +142,8 @@ class OutputBuffer { std::memcpy(Buffer, &*R.begin(), Size); CurrentPosition += Size; + notifyInsertion(/*Position=*/0, /*Count=*/Size); + return *this; } @@ -161,14 +179,20 @@ class OutputBuffer { DEMANGLE_ASSERT(Pos <= CurrentPosition, ""); if (N == 0) return; + grow(N); std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos); std::memcpy(Buffer + Pos, S, N); CurrentPosition += N; + + notifyInsertion(Pos, N); } size_t getCurrentPosition() const { return CurrentPosition; } - void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; } + void setCurrentPosition(size_t NewPos) { + notifyDeletion(CurrentPosition, NewPos); + CurrentPosition = NewPos; + } char back() const { DEMANGLE_ASSERT(CurrentPosition, ""); diff --git a/libcxxabi/test/test_demangle.pass.cpp b/libcxxabi/test/test_demangle.pass.cpp index fe5598991b831..546082c7b8668 100644 --- a/libcxxabi/test/test_demangle.pass.cpp +++ b/libcxxabi/test/test_demangle.pass.cpp @@ -29662,6 +29662,8 @@ const char* cases[][2] = {"_ZNKO1X1hEv", "X::h() const &&"}, // {"_Z1fM1XVKFivEMS_VFivEMS_KOFivE", "f(int (X::*)() const volatile, int (X::*)() volatile, int (X::*)() const &&)"}, // {"_Z1fM1XRFivEMS_OFivEMS_KOFivE", "f(int (X::*)() &, int (X::*)() &&, int (X::*)() const &&)"}, + {"_Z1fM1XKFivOE", "f(int (X::*)() const &&)"}, + {"_Z1fM1XKFivOEMS_FOKivEMS_VFS3_RS2_E", "f(int (X::*)() const &&, int const&& (X::*)(), int const&& (X::*)(int const&) volatile)"}, {"_ZN5test12f0ENS_1TILZNS_1xEEEE", "test1::f0(test1::T)"}, {"_ZN5test12f1ENS_2t1ILZNS_2f0EfEEE", "test1::f1(test1::t1)"}, {"_ZN5test22f1ENS_2t1IXadL_ZNS_2f0EfEEEE", "test2::f1(test2::t1<&test2::f0(float)>)"}, diff --git a/lldb/include/lldb/Core/DemangledNameInfo.h b/lldb/include/lldb/Core/DemangledNameInfo.h new file mode 100644 index 0000000000000..11d3bb58871b8 --- /dev/null +++ b/lldb/include/lldb/Core/DemangledNameInfo.h @@ -0,0 +1,153 @@ +//===-- DemangledNameInfo.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_CORE_DEMANGLEDNAMEINFO_H +#define LLDB_CORE_DEMANGLEDNAMEINFO_H + +#include "llvm/Demangle/ItaniumDemangle.h" +#include "llvm/Demangle/Utility.h" + +#include +#include + +namespace lldb_private { + +/// Stores information about where certain portions of a demangled +/// function name begin and end. +struct DemangledNameInfo { + /// A [start, end) pair for the function basename. + /// The basename is the name without scope qualifiers + /// and without template parameters. E.g., + /// \code{.cpp} + /// void foo::bar::someFunc(int) const && + /// ^ ^ + /// start end + /// \endcode + std::pair BasenameRange; + + /// A [start, end) pair for the function scope qualifiers. + /// E.g., for + /// \code{.cpp} + /// void foo::bar::qux(int) const && + /// ^ ^ + /// start end + /// \endcode + std::pair ScopeRange; + + /// Indicates the [start, end) of the function argument lits. + /// E.g., + /// \code{.cpp} + /// int (*getFunc(float, double))(int, int) + /// ^ ^ + /// start end + /// \endcode + std::pair ArgumentsRange; + + /// Indicates the [start, end) of the function qualifiers + /// (e.g., CV-qualifiers, reference qualifiers, requires clauses). + /// + /// E.g., + /// \code{.cpp} + /// void foo::bar::qux(int) const && + /// ^ ^ + /// start end + /// \endcode + std::pair QualifiersRange; + + /// Returns \c true if this object holds a valid basename range. + bool hasBasename() const { + return BasenameRange.second > BasenameRange.first && + BasenameRange.second > 0; + } +}; + +/// An OutputBuffer which keeps a record of where certain parts of a +/// demangled name begin/end (e.g., basename, scope, argument list, etc.). +/// The tracking occurs during printing of the Itanium demangle tree. +/// +/// Usage: +/// \code{.cpp} +/// +/// Node *N = mangling_parser.parseType(); +/// +/// TrackingOutputBuffer buffer; +/// N->printLeft(OB); +/// +/// assert (buffer.NameInfo.hasBasename()); +/// +/// \endcode +struct TrackingOutputBuffer : public llvm::itanium_demangle::OutputBuffer { + using OutputBuffer::OutputBuffer; + + /// Holds information about the demangled name that is + /// being printed into this buffer. + DemangledNameInfo NameInfo; + + void printLeft(const llvm::itanium_demangle::Node &N) override; + void printRight(const llvm::itanium_demangle::Node &N) override; + +private: + void printLeftImpl(const llvm::itanium_demangle::FunctionType &N); + void printRightImpl(const llvm::itanium_demangle::FunctionType &N); + + void printLeftImpl(const llvm::itanium_demangle::FunctionEncoding &N); + void printRightImpl(const llvm::itanium_demangle::FunctionEncoding &N); + + void printLeftImpl(const llvm::itanium_demangle::NestedName &N); + void printLeftImpl(const llvm::itanium_demangle::NameWithTemplateArgs &N); + + /// Called whenever we start printing a function type in the Itanium + /// mangling scheme. Examples include \ref FunctionEncoding, \ref + /// FunctionType, etc. + /// + /// \returns A ScopedOverride which will update the nesting depth of + /// currently printed function types on destruction. + [[nodiscard]] llvm::itanium_demangle::ScopedOverride + enterFunctionTypePrinting(); + + /// Returns \c true if we're not printing any nested function types, + /// just a \ref FunctionEncoding in the Itanium mangling scheme. + bool isPrintingTopLevelFunctionType() const; + + /// If this object \ref shouldTrack, then update the end of + /// the basename range to the current \c OB position. + void updateBasenameEnd(); + + /// If this object \ref shouldTrack, then update the beginning + /// of the scope range to the current \c OB position. + void updateScopeStart(); + + /// If this object \ref shouldTrack, then update the end of + /// the scope range to the current \c OB position. + void updateScopeEnd(); + + /// Returns \c true if the members of this object can be + /// updated. E.g., when we're printing nested template + /// arguments, we don't need to be tracking basename + /// locations. + bool shouldTrack() const; + + /// Helpers called to track beginning and end of the function + /// arguments. + void finalizeArgumentEnd(); + void finalizeStart(); + void finalizeEnd(); + void finalizeQualifiersStart(); + void finalizeQualifiersEnd(); + + /// Helper used in the finalize APIs. + bool canFinalize() const; + + /// Incremented each time we start printing a function type node + /// in the Itanium mangling scheme (e.g., \ref FunctionEncoding + /// or \ref FunctionType). + unsigned FunctionPrintingDepth = 0; +}; +} // namespace lldb_private + +#endif // LLDB_CORE_DEMANGLEDNAMEINFO_H diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h index bc51df726fc7a..31a08b3e4add0 100644 --- a/lldb/include/lldb/Core/FormatEntity.h +++ b/lldb/include/lldb/Core/FormatEntity.h @@ -88,6 +88,13 @@ struct Entry { FunctionNameWithArgs, FunctionNameNoArgs, FunctionMangledName, + FunctionScope, + FunctionBasename, + FunctionTemplateArguments, + FunctionFormattedArguments, + FunctionReturnLeft, + FunctionReturnRight, + FunctionQualifiers, FunctionAddrOffset, FunctionAddrOffsetConcrete, FunctionLineOffset, @@ -218,11 +225,6 @@ bool FormatStringRef(const llvm::StringRef &format, Stream &s, const Address *addr, ValueObject *valobj, bool function_changed, bool initial_function); -bool FormatCString(const char *format, Stream &s, const SymbolContext *sc, - const ExecutionContext *exe_ctx, const Address *addr, - ValueObject *valobj, bool function_changed, - bool initial_function); - Status Parse(const llvm::StringRef &format, Entry &entry); Status ExtractVariableInfo(llvm::StringRef &format_str, diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h index 11f2ac08da675..b65583ce23cb0 100644 --- a/lldb/include/lldb/Core/Mangled.h +++ b/lldb/include/lldb/Core/Mangled.h @@ -9,10 +9,11 @@ #ifndef LLDB_CORE_MANGLED_H #define LLDB_CORE_MANGLED_H +#include "lldb/Core/DemangledNameInfo.h" +#include "lldb/Utility/ConstString.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-types.h" -#include "lldb/Utility/ConstString.h" #include "llvm/ADT/StringRef.h" #include @@ -134,9 +135,15 @@ class Mangled { /// A const reference to the display demangled name string object. ConstString GetDisplayDemangledName(const SymbolContext *sc = nullptr) const; - void SetDemangledName(ConstString name) { m_demangled = name; } + void SetDemangledName(ConstString name) { + m_demangled = name; + m_demangled_info.reset(); + } - void SetMangledName(ConstString name) { m_mangled = name; } + void SetMangledName(ConstString name) { + m_mangled = name; + m_demangled_info.reset(); + } /// Mangled name get accessor. /// @@ -276,11 +283,27 @@ class Mangled { /// table offsets in the cache data. void Encode(DataEncoder &encoder, ConstStringTable &strtab) const; + /// Retrieve \c DemangledNameInfo of the demangled name held by this object. + const std::optional &GetDemangledInfo() const; + private: - /// Mangled member variables. - ConstString m_mangled; ///< The mangled version of the name - mutable ConstString m_demangled; ///< Mutable so we can get it on demand with - ///a const version of this object + /// If \c force is \c false, this function will re-use the previously + /// demangled name (if any). If \c force is \c true (or the mangled name + /// on this object was not previously demangled), demangle and cache the + /// name. + ConstString GetDemangledNameImpl(bool force, + const SymbolContext *sc = nullptr) const; + + /// The mangled version of the name. + ConstString m_mangled; + + /// Mutable so we can get it on demand with + /// a const version of this object. + mutable ConstString m_demangled; + + /// If available, holds information about where in \c m_demangled certain + /// parts of the name (e.g., basename, arguments, etc.) begin and end. + mutable std::optional m_demangled_info = std::nullopt; }; Stream &operator<<(Stream &s, const Mangled &obj); diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h index e4e0c3eea67f8..c0213e3f8d3f6 100644 --- a/lldb/include/lldb/Core/PluginManager.h +++ b/lldb/include/lldb/Core/PluginManager.h @@ -134,8 +134,10 @@ class PluginManager { GetOperatingSystemCreateCallbackForPluginName(llvm::StringRef name); // Language - static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, - LanguageCreateInstance create_callback); + static bool + RegisterPlugin(llvm::StringRef name, llvm::StringRef description, + LanguageCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback = nullptr); static bool UnregisterPlugin(LanguageCreateInstance create_callback); @@ -600,6 +602,14 @@ class PluginManager { static bool CreateSettingForStructuredDataPlugin( Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, llvm::StringRef description, bool is_global_property); + + static lldb::OptionValuePropertiesSP + GetSettingForCPlusPlusLanguagePlugin(Debugger &debugger, + llvm::StringRef setting_name); + + static bool CreateSettingForCPlusPlusLanguagePlugin( + Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, + llvm::StringRef description, bool is_global_property); }; } // namespace lldb_private diff --git a/lldb/include/lldb/Symbol/SymbolContext.h b/lldb/include/lldb/Symbol/SymbolContext.h index 0bc707070f850..0fa74f27faab9 100644 --- a/lldb/include/lldb/Symbol/SymbolContext.h +++ b/lldb/include/lldb/Symbol/SymbolContext.h @@ -309,6 +309,12 @@ class SymbolContext { SymbolContext &next_frame_sc, Address &inlined_frame_addr) const; + /// If available, will return the function name according to the specified + /// mangling preference. If this object represents an inlined function, + /// returns the name of the inlined function. Returns nullptr if no function + /// name could be determined. + Mangled GetPossiblyInlinedFunctionName() const; + // Member variables lldb::TargetSP target_sp; ///< The Target for a given query lldb::ModuleSP module_sp; ///< The Module for a given query diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h index 6a1487a251aa1..9533f9c99b0ea 100644 --- a/lldb/include/lldb/Target/Language.h +++ b/lldb/include/lldb/Target/Language.h @@ -15,6 +15,7 @@ #include #include +#include "lldb/Core/FormatEntity.h" #include "lldb/Core/Highlighter.h" #include "lldb/Core/PluginInterface.h" #include "lldb/DataFormatters/DumpValueObjectOptions.h" @@ -270,11 +271,18 @@ class Language : public PluginInterface { // the reference has never been assigned virtual bool IsUninitializedReference(ValueObject &valobj); - virtual bool GetFunctionDisplayName(const SymbolContext *sc, + virtual bool GetFunctionDisplayName(const SymbolContext &sc, const ExecutionContext *exe_ctx, FunctionNameRepresentation representation, Stream &s); + virtual bool HandleFrameFormatVariable(const SymbolContext &sc, + const ExecutionContext *exe_ctx, + FormatEntity::Entry::Type type, + Stream &s) { + return false; + } + virtual ConstString GetDemangledFunctionNameWithoutArguments(Mangled mangled) const { if (ConstString demangled = mangled.GetDemangledName()) @@ -391,6 +399,10 @@ class Language : public PluginInterface { /// Python uses \b except. Defaults to \b catch. virtual llvm::StringRef GetCatchKeyword() const { return "catch"; } + virtual const FormatEntity::Entry *GetFunctionNameFormat() const { + return nullptr; + } + protected: // Classes that inherit from Language can see and modify these diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt index 00b9f2390c15f..c6bb3cded801a 100644 --- a/lldb/source/Core/CMakeLists.txt +++ b/lldb/source/Core/CMakeLists.txt @@ -28,6 +28,7 @@ add_lldb_library(lldbCore Debugger.cpp DebuggerEvents.cpp Declaration.cpp + DemangledNameInfo.cpp Disassembler.cpp DumpDataExtractor.cpp DumpRegisterValue.cpp diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td index 2e941fc116807..b6ad8e9f0e0b9 100644 --- a/lldb/source/Core/CoreProperties.td +++ b/lldb/source/Core/CoreProperties.td @@ -112,7 +112,7 @@ let Definition = "debugger" in { Desc<"The default disassembly format string to use when disassembling instruction sequences.">; def FrameFormat: Property<"frame-format", "FormatEntity">, Global, - DefaultStringValue<"frame #${frame.index}: ${ansi.fg.yellow}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\\\\n">, + DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\\\\n">, Desc<"The default frame format string to use when displaying stack frame information for threads.">; def NotiftVoid: Property<"notify-void", "Boolean">, Global, @@ -268,7 +268,7 @@ let Definition = "debugger" in { Desc<"If true, LLDB will automatically escape non-printable and escape characters when formatting strings.">; def FrameFormatUnique: Property<"frame-format-unique", "FormatEntity">, Global, - DefaultStringValue<"frame #${frame.index}: ${ansi.fg.yellow}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\\\\n">, + DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\\\\n">, Desc<"The default frame format string to use when displaying stack frame information for threads from thread backtrace unique.">; def ShowAutosuggestion: Property<"show-autosuggestion", "Boolean">, Global, diff --git a/lldb/source/Core/DemangledNameInfo.cpp b/lldb/source/Core/DemangledNameInfo.cpp new file mode 100644 index 0000000000000..54a06edc5ec1d --- /dev/null +++ b/lldb/source/Core/DemangledNameInfo.cpp @@ -0,0 +1,230 @@ +//===-- DemangledNameInfo.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DemangledNameInfo.h" + +using namespace llvm::itanium_demangle; + +namespace lldb_private { + +bool TrackingOutputBuffer::shouldTrack() const { + if (!isPrintingTopLevelFunctionType()) + return false; + + if (isGtInsideTemplateArgs()) + return false; + + if (NameInfo.ArgumentsRange.first > 0) + return false; + + return true; +} + +bool TrackingOutputBuffer::canFinalize() const { + if (!isPrintingTopLevelFunctionType()) + return false; + + if (isGtInsideTemplateArgs()) + return false; + + if (NameInfo.ArgumentsRange.first == 0) + return false; + + return true; +} + +void TrackingOutputBuffer::updateBasenameEnd() { + if (!shouldTrack()) + return; + + NameInfo.BasenameRange.second = getCurrentPosition(); +} + +void TrackingOutputBuffer::updateScopeStart() { + if (!shouldTrack()) + return; + + NameInfo.ScopeRange.first = getCurrentPosition(); +} + +void TrackingOutputBuffer::updateScopeEnd() { + if (!shouldTrack()) + return; + + NameInfo.ScopeRange.second = getCurrentPosition(); +} + +void TrackingOutputBuffer::finalizeArgumentEnd() { + if (!canFinalize()) + return; + + NameInfo.ArgumentsRange.second = getCurrentPosition(); +} + +void TrackingOutputBuffer::finalizeQualifiersStart() { + if (!canFinalize()) + return; + + NameInfo.QualifiersRange.first = getCurrentPosition(); +} + +void TrackingOutputBuffer::finalizeQualifiersEnd() { + if (!canFinalize()) + return; + + NameInfo.QualifiersRange.second = getCurrentPosition(); +} + +void TrackingOutputBuffer::finalizeStart() { + if (!shouldTrack()) + return; + + NameInfo.ArgumentsRange.first = getCurrentPosition(); + + // If nothing has set the end of the basename yet (for example when + // printing templates), then the beginning of the arguments is the end of + // the basename. + if (NameInfo.BasenameRange.second == 0) + NameInfo.BasenameRange.second = getCurrentPosition(); + + assert(!shouldTrack()); + assert(canFinalize()); +} + +void TrackingOutputBuffer::finalizeEnd() { + if (!canFinalize()) + return; + + if (NameInfo.ScopeRange.first > NameInfo.ScopeRange.second) + NameInfo.ScopeRange.second = NameInfo.ScopeRange.first; + NameInfo.BasenameRange.first = NameInfo.ScopeRange.second; +} + +ScopedOverride TrackingOutputBuffer::enterFunctionTypePrinting() { + return {FunctionPrintingDepth, FunctionPrintingDepth + 1}; +} + +bool TrackingOutputBuffer::isPrintingTopLevelFunctionType() const { + return FunctionPrintingDepth == 1; +} + +void TrackingOutputBuffer::printLeft(const Node &N) { + switch (N.getKind()) { + case Node::KFunctionType: + printLeftImpl(static_cast(N)); + break; + case Node::KFunctionEncoding: + printLeftImpl(static_cast(N)); + break; + case Node::KNestedName: + printLeftImpl(static_cast(N)); + break; + case Node::KNameWithTemplateArgs: + printLeftImpl(static_cast(N)); + break; + default: + OutputBuffer::printLeft(N); + } +} + +void TrackingOutputBuffer::printRight(const Node &N) { + switch (N.getKind()) { + case Node::KFunctionType: + printRightImpl(static_cast(N)); + break; + case Node::KFunctionEncoding: + printRightImpl(static_cast(N)); + break; + default: + OutputBuffer::printRight(N); + } +} + +void TrackingOutputBuffer::printLeftImpl(const FunctionType &N) { + auto Scoped = enterFunctionTypePrinting(); + OutputBuffer::printLeft(N); +} + +void TrackingOutputBuffer::printRightImpl(const FunctionType &N) { + auto Scoped = enterFunctionTypePrinting(); + OutputBuffer::printRight(N); +} + +void TrackingOutputBuffer::printLeftImpl(const FunctionEncoding &N) { + auto Scoped = enterFunctionTypePrinting(); + + const Node *Ret = N.getReturnType(); + if (Ret) { + printLeft(*Ret); + if (!Ret->hasRHSComponent(*this)) + *this += " "; + } + + updateScopeStart(); + + N.getName()->print(*this); +} + +void TrackingOutputBuffer::printRightImpl(const FunctionEncoding &N) { + auto Scoped = enterFunctionTypePrinting(); + finalizeStart(); + + printOpen(); + N.getParams().printWithComma(*this); + printClose(); + + finalizeArgumentEnd(); + + const Node *Ret = N.getReturnType(); + + if (Ret) + printRight(*Ret); + + finalizeQualifiersStart(); + + auto CVQuals = N.getCVQuals(); + auto RefQual = N.getRefQual(); + auto *Attrs = N.getAttrs(); + auto *Requires = N.getRequires(); + + if (CVQuals & QualConst) + *this += " const"; + if (CVQuals & QualVolatile) + *this += " volatile"; + if (CVQuals & QualRestrict) + *this += " restrict"; + if (RefQual == FrefQualLValue) + *this += " &"; + else if (RefQual == FrefQualRValue) + *this += " &&"; + if (Attrs != nullptr) + Attrs->print(*this); + if (Requires != nullptr) { + *this += " requires "; + Requires->print(*this); + } + + finalizeQualifiersEnd(); + finalizeEnd(); +} + +void TrackingOutputBuffer::printLeftImpl(const NestedName &N) { + N.Qual->print(*this); + *this += "::"; + updateScopeEnd(); + N.Name->print(*this); + updateBasenameEnd(); +} + +void TrackingOutputBuffer::printLeftImpl(const NameWithTemplateArgs &N) { + N.Name->print(*this); + updateBasenameEnd(); + N.TemplateArgs->print(*this); +} + +} // namespace lldb_private diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 1ca34aaa222a3..f1470657b01ed 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -122,7 +122,15 @@ constexpr Definition g_function_child_entries[] = { Definition("pc-offset", EntryType::FunctionPCOffset), Definition("initial-function", EntryType::FunctionInitial), Definition("changed", EntryType::FunctionChanged), - Definition("is-optimized", EntryType::FunctionIsOptimized)}; + Definition("is-optimized", EntryType::FunctionIsOptimized), + Definition("scope", EntryType::FunctionScope), + Definition("basename", EntryType::FunctionBasename), + Definition("template-arguments", EntryType::FunctionTemplateArguments), + Definition("formatted-arguments", EntryType::FunctionFormattedArguments), + Definition("return-left", EntryType::FunctionReturnLeft), + Definition("return-right", EntryType::FunctionReturnRight), + Definition("qualifiers", EntryType::FunctionQualifiers), +}; constexpr Definition g_line_child_entries[] = { Entry::DefinitionWithChildren("file", EntryType::LineEntryFile, @@ -353,6 +361,13 @@ const char *FormatEntity::Entry::TypeToCString(Type t) { ENUM_TO_CSTR(FunctionNameWithArgs); ENUM_TO_CSTR(FunctionNameNoArgs); ENUM_TO_CSTR(FunctionMangledName); + ENUM_TO_CSTR(FunctionScope); + ENUM_TO_CSTR(FunctionBasename); + ENUM_TO_CSTR(FunctionTemplateArguments); + ENUM_TO_CSTR(FunctionFormattedArguments); + ENUM_TO_CSTR(FunctionReturnLeft); + ENUM_TO_CSTR(FunctionReturnRight); + ENUM_TO_CSTR(FunctionQualifiers); ENUM_TO_CSTR(FunctionAddrOffset); ENUM_TO_CSTR(FunctionAddrOffsetConcrete); ENUM_TO_CSTR(FunctionLineOffset); @@ -1149,17 +1164,106 @@ static void PrettyPrintFunctionNameWithArgs(Stream &out_stream, out_stream.PutChar(')'); } -static void FormatInlinedBlock(Stream &out_stream, Block *block) { - if (!block) - return; - Block *inline_block = block->GetContainingInlinedBlock(); - if (inline_block) { - if (const InlineFunctionInfo *inline_info = - inline_block->GetInlinedFunctionInfo()) { - out_stream.PutCString(" [inlined] "); - inline_info->GetName().Dump(&out_stream); - } +static VariableListSP GetFunctionVariableList(const SymbolContext &sc) { + assert(sc.function); + + if (sc.block) + if (Block *inline_block = sc.block->GetContainingInlinedBlock()) + return inline_block->GetBlockVariableList(true); + + return sc.function->GetBlock(true).GetBlockVariableList(true); +} + +static bool PrintFunctionNameWithArgs(Stream &s, + const ExecutionContext *exe_ctx, + const SymbolContext &sc) { + assert(sc.function); + + ExecutionContextScope *exe_scope = + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; + + const char *cstr = sc.GetPossiblyInlinedFunctionName() + .GetName(Mangled::ePreferDemangled) + .AsCString(); + if (!cstr) + return false; + + VariableList args; + if (auto variable_list_sp = GetFunctionVariableList(sc)) + variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, + args); + + if (args.GetSize() > 0) { + PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args); + } else { + s.PutCString(cstr); } + + return true; +} + +static bool HandleFunctionNameWithArgs(Stream &s, + const ExecutionContext *exe_ctx, + const SymbolContext &sc) { + Language *language_plugin = nullptr; + bool language_plugin_handled = false; + StreamString ss; + if (sc.function) + language_plugin = Language::FindPlugin(sc.function->GetLanguage()); + else if (sc.symbol) + language_plugin = Language::FindPlugin(sc.symbol->GetLanguage()); + + if (language_plugin) + language_plugin_handled = language_plugin->GetFunctionDisplayName( + sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss); + + if (language_plugin_handled) { + s << ss.GetString(); + return true; + } + + if (sc.function) + return PrintFunctionNameWithArgs(s, exe_ctx, sc); + + if (!sc.symbol) + return false; + + const char *cstr = sc.symbol->GetName().AsCString(nullptr); + if (!cstr) + return false; + + s.PutCString(cstr); + + return true; +} + +static bool FormatFunctionNameForLanguage(Stream &s, + const ExecutionContext *exe_ctx, + const SymbolContext *sc) { + assert(sc); + + Language *language_plugin = nullptr; + if (sc->function) + language_plugin = Language::FindPlugin(sc->function->GetLanguage()); + else if (sc->symbol) + language_plugin = Language::FindPlugin(sc->symbol->GetLanguage()); + + if (!language_plugin) + return false; + + const auto *format = language_plugin->GetFunctionNameFormat(); + if (!format) + return false; + + StreamString name_stream; + const bool success = + FormatEntity::Format(*format, name_stream, sc, exe_ctx, /*addr=*/nullptr, + /*valobj=*/nullptr, /*function_changed=*/false, + /*initial_function=*/false); + if (success) + s << name_stream.GetString(); + + return success; } bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s, @@ -1179,23 +1283,6 @@ bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s, return false; } -bool FormatEntity::FormatCString(const char *format, Stream &s, - const SymbolContext *sc, - const ExecutionContext *exe_ctx, - const Address *addr, ValueObject *valobj, - bool function_changed, bool initial_function) { - if (format && format[0]) { - FormatEntity::Entry root; - llvm::StringRef format_str(format); - Status error = FormatEntity::Parse(format_str, root); - if (error.Success()) { - return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj, - function_changed, initial_function); - } - } - return false; -} - bool FormatEntity::Format(const Entry &entry, Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const Address *addr, @@ -1671,26 +1758,23 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (language_plugin) language_plugin_handled = language_plugin->GetFunctionDisplayName( - sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss); + *sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss); if (language_plugin_handled) { s << ss.GetString(); return true; - } else { - const char *name = nullptr; - if (sc->function) - name = sc->function->GetName().AsCString(nullptr); - else if (sc->symbol) - name = sc->symbol->GetName().AsCString(nullptr); - - if (name) { - s.PutCString(name); - FormatInlinedBlock(s, sc->block); - return true; - } } + + const char *name = sc->GetPossiblyInlinedFunctionName() + .GetName(Mangled::NamePreference::ePreferDemangled) + .AsCString(); + if (!name) + return false; + + s.PutCString(name); + + return true; } - return false; case Entry::Type::FunctionNameNoArgs: { if (!sc) @@ -1706,117 +1790,67 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (language_plugin) language_plugin_handled = language_plugin->GetFunctionDisplayName( - sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs, + *sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs, ss); if (language_plugin_handled) { s << ss.GetString(); return true; - } else { - ConstString name; - if (sc->function) - name = sc->function->GetNameNoArguments(); - else if (sc->symbol) - name = sc->symbol->GetNameNoArguments(); - if (name) { - s.PutCString(name.GetCString()); - FormatInlinedBlock(s, sc->block); - return true; - } } - } - return false; - case Entry::Type::FunctionNameWithArgs: { - if (!sc) + const char *name = + sc->GetPossiblyInlinedFunctionName() + .GetName(Mangled::NamePreference::ePreferDemangledWithoutArguments) + .AsCString(); + if (!name) return false; + s.PutCString(name); + + return true; + } + + case Entry::Type::FunctionScope: + case Entry::Type::FunctionBasename: + case Entry::Type::FunctionTemplateArguments: + case Entry::Type::FunctionFormattedArguments: + case Entry::Type::FunctionReturnRight: + case Entry::Type::FunctionReturnLeft: + case Entry::Type::FunctionQualifiers: { Language *language_plugin = nullptr; - bool language_plugin_handled = false; - StreamString ss; if (sc->function) language_plugin = Language::FindPlugin(sc->function->GetLanguage()); else if (sc->symbol) language_plugin = Language::FindPlugin(sc->symbol->GetLanguage()); - if (language_plugin) - language_plugin_handled = language_plugin->GetFunctionDisplayName( - sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss); + if (!language_plugin) + return false; - if (language_plugin_handled) { - s << ss.GetString(); - return true; - } else { - // Print the function name with arguments in it - if (sc->function) { - ExecutionContextScope *exe_scope = - exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; - const char *cstr = sc->function->GetName().AsCString(nullptr); - if (cstr) { - const InlineFunctionInfo *inline_info = nullptr; - VariableListSP variable_list_sp; - bool get_function_vars = true; - if (sc->block) { - Block *inline_block = sc->block->GetContainingInlinedBlock(); - - if (inline_block) { - get_function_vars = false; - inline_info = inline_block->GetInlinedFunctionInfo(); - if (inline_info) - variable_list_sp = inline_block->GetBlockVariableList(true); - } - } + return language_plugin->HandleFrameFormatVariable(*sc, exe_ctx, entry.type, + s); + } - if (get_function_vars) { - variable_list_sp = - sc->function->GetBlock(true).GetBlockVariableList(true); - } + case Entry::Type::FunctionNameWithArgs: { + if (!sc) + return false; - if (inline_info) { - s.PutCString(cstr); - s.PutCString(" [inlined] "); - cstr = inline_info->GetName().GetCString(); - } + if (FormatFunctionNameForLanguage(s, exe_ctx, sc)) + return true; - VariableList args; - if (variable_list_sp) - variable_list_sp->AppendVariablesWithScope( - eValueTypeVariableArgument, args); - if (args.GetSize() > 0) { - PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args); - } else { - s.PutCString(cstr); - } - return true; - } - } else if (sc->symbol) { - const char *cstr = sc->symbol->GetName().AsCString(nullptr); - if (cstr) { - s.PutCString(cstr); - return true; - } - } - } + return HandleFunctionNameWithArgs(s, exe_ctx, *sc); } - return false; - case Entry::Type::FunctionMangledName: { if (!sc) return false; - const char *name = nullptr; - if (sc->symbol) - name = - sc->symbol->GetMangled().GetName(Mangled::ePreferMangled).AsCString(); - else if (sc->function) - name = sc->function->GetMangled() - .GetName(Mangled::ePreferMangled) - .AsCString(); - + const char *name = sc->GetPossiblyInlinedFunctionName() + .GetName(Mangled::NamePreference::ePreferMangled) + .AsCString(); if (!name) return false; + s.PutCString(name); - FormatInlinedBlock(s, sc->block); + return true; } case Entry::Type::FunctionAddrOffset: diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index ccc6af2a83e4a..093451f070e70 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -9,6 +9,7 @@ #include "lldb/Core/Mangled.h" #include "lldb/Core/DataFileCache.h" +#include "lldb/Core/DemangledNameInfo.h" #include "lldb/Core/RichManglingContext.h" #include "lldb/Target/Language.h" #include "lldb/Utility/ConstString.h" @@ -120,6 +121,7 @@ Mangled::operator bool() const { return m_mangled || m_demangled; } void Mangled::Clear() { m_mangled.Clear(); m_demangled.Clear(); + m_demangled_info.reset(); } // Compare the string values. @@ -133,13 +135,16 @@ void Mangled::SetValue(ConstString name) { if (cstring_is_mangled(name.GetStringRef())) { m_demangled.Clear(); m_mangled = name; + m_demangled_info.reset(); } else { m_demangled = name; m_mangled.Clear(); + m_demangled_info.reset(); } } else { m_demangled.Clear(); m_mangled.Clear(); + m_demangled_info.reset(); } } @@ -161,20 +166,26 @@ static char *GetMSVCDemangledStr(llvm::StringRef M) { return demangled_cstr; } -static char *GetItaniumDemangledStr(const char *M) { +static std::pair +GetItaniumDemangledStr(const char *M) { char *demangled_cstr = nullptr; + DemangledNameInfo info; llvm::ItaniumPartialDemangler ipd; bool err = ipd.partialDemangle(M); if (!err) { - // Default buffer and size (will realloc in case it's too small). + // Default buffer and size (OutputBuffer 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); + demangled_cstr = static_cast(std::malloc(80)); + + TrackingOutputBuffer OB(demangled_cstr, demangled_size); + demangled_cstr = ipd.finishDemangle(&OB); + info = std::move(OB.NameInfo); assert(demangled_cstr && "finishDemangle must always succeed if partialDemangle did"); - assert(demangled_cstr[demangled_size - 1] == '\0' && + assert(demangled_cstr[OB.getCurrentPosition() - 1] == '\0' && "Expected demangled_size to return length including trailing null"); } @@ -183,9 +194,14 @@ static char *GetItaniumDemangledStr(const char *M) { LLDB_LOGF(log, "demangled itanium: %s -> \"%s\"", M, demangled_cstr); else LLDB_LOGF(log, "demangled itanium: %s -> error: failed to demangle", M); + + if (!info.hasBasename()) + LLDB_LOGF(log, + "demangled itanium: %s -> error: failed to retrieve name info", + M); } - return demangled_cstr; + return {demangled_cstr, std::move(info)}; } static char *GetRustV0DemangledStr(llvm::StringRef M) { @@ -274,81 +290,101 @@ bool Mangled::GetRichManglingInfo(RichManglingContext &context, llvm_unreachable("Fully covered switch above!"); } +ConstString Mangled::GetDemangledName( // BEGIN SWIFT + const SymbolContext *sc + // END SWIFT +) const { + return GetDemangledNameImpl(/*force=*/false, sc); +} + +std::optional const &Mangled::GetDemangledInfo() const { + if (!m_demangled_info) + GetDemangledNameImpl(/*force=*/true); + + return m_demangled_info; +} + // 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 // supplied to this object, or until the end of the object's lifetime. -ConstString Mangled::GetDemangledName(// BEGIN SWIFT - const SymbolContext *sc - // END SWIFT - ) const { - // Check to make sure we have a valid mangled name and that we haven't - // already decoded our mangled name. - if (m_mangled && m_demangled.IsNull()) { - // Don't bother running anything that isn't mangled - const char *mangled_name = m_mangled.GetCString(); - ManglingScheme mangling_scheme = - GetManglingScheme(m_mangled.GetStringRef()); - if (mangling_scheme != eManglingSchemeNone && - !m_mangled.GetMangledCounterpart(m_demangled)) { - // We didn't already mangle this name, demangle it and if all goes well - // add it to our map. - char *demangled_name = nullptr; - switch (mangling_scheme) { - case eManglingSchemeMSVC: - demangled_name = GetMSVCDemangledStr(mangled_name); - break; - case eManglingSchemeItanium: { - demangled_name = GetItaniumDemangledStr(mangled_name); - break; - } - case eManglingSchemeRustV0: - demangled_name = GetRustV0DemangledStr(m_mangled); - break; - case eManglingSchemeD: - demangled_name = GetDLangDemangledStr(m_mangled); - break; - case eManglingSchemeSwift: - // Demangling a swift name requires the swift compiler. This is - // explicitly unsupported on llvm.org. +ConstString Mangled::GetDemangledNameImpl(bool force, // BEGIN SWIFT + const SymbolContext *sc + // END SWIFT +) const { + if (!m_mangled) + return m_demangled; + + // Re-use previously demangled names. + if (!force && !m_demangled.IsNull()) + return m_demangled; + + if (!force && m_mangled.GetMangledCounterpart(m_demangled) && + !m_demangled.IsNull()) + return m_demangled; + + // We didn't already mangle this name, demangle it and if all goes well + // add it to our map. + char *demangled_name = nullptr; + switch (GetManglingScheme(m_mangled.GetStringRef())) { + case eManglingSchemeMSVC: + demangled_name = GetMSVCDemangledStr(m_mangled); + break; + case eManglingSchemeItanium: { + std::pair demangled = + GetItaniumDemangledStr(m_mangled.GetCString()); + demangled_name = demangled.first; + m_demangled_info.emplace(std::move(demangled.second)); + break; + } + case eManglingSchemeRustV0: + demangled_name = GetRustV0DemangledStr(m_mangled); + break; + case eManglingSchemeD: + demangled_name = GetDLangDemangledStr(m_mangled); + break; + case eManglingSchemeSwift: + // Demangling a swift name requires the swift compiler. This is + // explicitly unsupported on llvm.org. #ifdef LLDB_ENABLE_SWIFT - { - Log *log = GetLog(LLDBLog::Demangle); - LLDB_LOGF(log, "demangle swift: %s", mangled_name); - std::string demangled(SwiftLanguageRuntime::DemangleSymbolAsString( - mangled_name, SwiftLanguageRuntime::eTypeName, sc)); - // Don't cache the demangled name the function isn't available yet. - if (!sc || !sc->function) { - LLDB_LOGF(log, "demangle swift: %s -> \"%s\" (not cached)", - mangled_name, demangled.c_str()); - return ConstString(demangled); - } - if (demangled.empty()) { - LLDB_LOGF(log, "demangle swift: %s -> error: failed to demangle", - mangled_name); - } else { - LLDB_LOGF(log, "demangle swift: %s -> \"%s\"", mangled_name, - demangled.c_str()); - m_demangled.SetStringWithMangledCounterpart(demangled, m_mangled); - } - return m_demangled; - } -#endif // LLDB_ENABLE_SWIFT - break; - case eManglingSchemeNone: - llvm_unreachable("eManglingSchemeNone was handled already"); - } - if (demangled_name) { - m_demangled.SetStringWithMangledCounterpart( - llvm::StringRef(demangled_name), m_mangled); - free(demangled_name); - } + { + const char *mangled_name = m_mangled.GetCString(); + Log *log = GetLog(LLDBLog::Demangle); + LLDB_LOGF(log, "demangle swift: %s", mangled_name); + std::string demangled(SwiftLanguageRuntime::DemangleSymbolAsString( + mangled_name, SwiftLanguageRuntime::eTypeName, sc)); + // Don't cache the demangled name the function isn't available yet. + if (!sc || !sc->function) { + LLDB_LOGF(log, "demangle swift: %s -> \"%s\" (not cached)", mangled_name, + demangled.c_str()); + return ConstString(demangled); } - if (m_demangled.IsNull()) { - // Set the demangled string to the empty string to indicate we tried to - // parse it once and failed. - m_demangled.SetCString(""); + if (demangled.empty()) { + LLDB_LOGF(log, "demangle swift: %s -> error: failed to demangle", + mangled_name); + } else { + LLDB_LOGF(log, "demangle swift: %s -> \"%s\"", mangled_name, + demangled.c_str()); + m_demangled.SetStringWithMangledCounterpart(demangled, m_mangled); } + return m_demangled; + } +#endif // LLDB_ENABLE_SWIFT + break; + case eManglingSchemeNone: + // Don't bother demangling anything that isn't mangled. + break; + } + + if (demangled_name) { + m_demangled.SetStringWithMangledCounterpart(demangled_name, m_mangled); + free(demangled_name); + } + + if (m_demangled.IsNull()) { + // Set the demangled string to the empty string to indicate we tried to + // parse it once and failed. + m_demangled.SetCString(""); } return m_demangled; @@ -496,6 +532,7 @@ bool Mangled::Decode(const DataExtractor &data, lldb::offset_t *offset_ptr, const StringTableReader &strtab) { m_mangled.Clear(); m_demangled.Clear(); + m_demangled_info.reset(); MangledEncoding encoding = (MangledEncoding)data.GetU8(offset_ptr); switch (encoding) { case Empty: diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp index 952b6ff3b9a3a..c7ef83d82d6c7 100644 --- a/lldb/source/Core/PluginManager.cpp +++ b/lldb/source/Core/PluginManager.cpp @@ -508,11 +508,12 @@ static LanguageInstances &GetLanguageInstances() { return g_instances; } -bool PluginManager::RegisterPlugin(llvm::StringRef name, - llvm::StringRef description, - LanguageCreateInstance create_callback) { - return GetLanguageInstances().RegisterPlugin(name, description, - create_callback); +bool PluginManager::RegisterPlugin( + llvm::StringRef name, llvm::StringRef description, + LanguageCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback) { + return GetLanguageInstances().RegisterPlugin( + name, description, create_callback, debugger_init_callback); } bool PluginManager::UnregisterPlugin(LanguageCreateInstance create_callback) { @@ -1635,6 +1636,7 @@ void PluginManager::DebuggerInitialize(Debugger &debugger) { GetTracePluginInstances().PerformDebuggerCallback(debugger); GetTypeSystemInstances().PerformDebuggerCallback(debugger); GetScriptedInterfaceInstances().PerformDebuggerCallback(debugger); + GetLanguageInstances().PerformDebuggerCallback(debugger); } // This is the preferred new way to register plugin specific settings. e.g. @@ -1763,6 +1765,7 @@ static constexpr llvm::StringLiteral kSymbolLocatorPluginName("symbol-locator"); static constexpr llvm::StringLiteral kJITLoaderPluginName("jit-loader"); static constexpr llvm::StringLiteral kStructuredDataPluginName("structured-data"); +static constexpr llvm::StringLiteral kCPlusPlusLanguagePlugin("cplusplus"); lldb::OptionValuePropertiesSP PluginManager::GetSettingForDynamicLoaderPlugin(Debugger &debugger, @@ -1920,3 +1923,17 @@ bool PluginManager::CreateSettingForStructuredDataPlugin( "Settings for structured data plug-ins", properties_sp, description, is_global_property); } + +lldb::OptionValuePropertiesSP +PluginManager::GetSettingForCPlusPlusLanguagePlugin( + Debugger &debugger, llvm::StringRef setting_name) { + return GetSettingForPlugin(debugger, setting_name, kCPlusPlusLanguagePlugin); +} + +bool PluginManager::CreateSettingForCPlusPlusLanguagePlugin( + Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, + llvm::StringRef description, bool is_global_property) { + return CreateSettingForPlugin(debugger, kCPlusPlusLanguagePlugin, + "Settings for CPlusPlus language plug-ins", + properties_sp, description, is_global_property); +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index ccdc4d0ae99b3..9bb10c2a792a9 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -1,3 +1,11 @@ +lldb_tablegen(LanguageCPlusPlusProperties.inc -gen-lldb-property-defs + SOURCE LanguageCPlusPlusProperties.td + TARGET LLDBPluginLanguageCPlusPlusPropertiesGen) + +lldb_tablegen(LanguageCPlusPlusPropertiesEnum.inc -gen-lldb-property-enum-defs + SOURCE LanguageCPlusPlusProperties.td + TARGET LLDBPluginLanguageCPlusPlusPropertiesEnumGen) + add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN BlockPointer.cpp Coroutines.cpp @@ -41,3 +49,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN LINK_COMPONENTS Support ) + +add_dependencies(lldbPluginCPlusPlusLanguage + LLDBPluginLanguageCPlusPlusPropertiesGen + LLDBPluginLanguageCPlusPlusPropertiesEnumGen) diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 0198ceb8149e1..0d62d48f6995c 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -27,6 +27,7 @@ #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/DataFormatters/FormattersHelpers.h" #include "lldb/DataFormatters/VectorType.h" +#include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Utility/ConstString.h" @@ -55,7 +56,7 @@ LLDB_PLUGIN_DEFINE(CPlusPlusLanguage) void CPlusPlusLanguage::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), "C++ Language", - CreateInstance); + CreateInstance, &DebuggerInitialize); } void CPlusPlusLanguage::Terminate() { @@ -208,6 +209,179 @@ static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream, return true; } +static std::optional +GetDemangledBasename(const SymbolContext &sc) { + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return std::nullopt; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return std::nullopt; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return std::nullopt; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return std::nullopt; + + return demangled_name.slice(info->BasenameRange.first, + info->BasenameRange.second); +} + +static std::optional +GetDemangledTemplateArguments(const SymbolContext &sc) { + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return std::nullopt; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return std::nullopt; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return std::nullopt; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return std::nullopt; + + if (info->ArgumentsRange.first < info->BasenameRange.second) + return std::nullopt; + + return demangled_name.slice(info->BasenameRange.second, + info->ArgumentsRange.first); +} + +static std::optional +GetDemangledReturnTypeLHS(const SymbolContext &sc) { + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return std::nullopt; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return std::nullopt; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return std::nullopt; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return std::nullopt; + + if (info->ScopeRange.first >= demangled_name.size()) + return std::nullopt; + + return demangled_name.substr(0, info->ScopeRange.first); +} + +static std::optional +GetDemangledFunctionQualifiers(const SymbolContext &sc) { + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return std::nullopt; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return std::nullopt; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return std::nullopt; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return std::nullopt; + + if (info->QualifiersRange.second < info->QualifiersRange.first) + return std::nullopt; + + return demangled_name.slice(info->QualifiersRange.first, + info->QualifiersRange.second); +} + +static std::optional +GetDemangledReturnTypeRHS(const SymbolContext &sc) { + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return std::nullopt; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return std::nullopt; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return std::nullopt; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return std::nullopt; + + if (info->QualifiersRange.first < info->ArgumentsRange.second) + return std::nullopt; + + return demangled_name.slice(info->ArgumentsRange.second, + info->QualifiersRange.first); +} + +static std::optional +GetDemangledScope(const SymbolContext &sc) { + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return std::nullopt; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return std::nullopt; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return std::nullopt; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return std::nullopt; + + if (info->ScopeRange.second < info->ScopeRange.first) + return std::nullopt; + + return demangled_name.slice(info->ScopeRange.first, info->ScopeRange.second); +} + +static bool PrintDemangledArgumentList(Stream &s, const SymbolContext &sc) { + assert(sc.symbol); + + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return false; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return false; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return false; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return false; + + if (info->ArgumentsRange.second < info->ArgumentsRange.first) + return false; + + s << demangled_name.slice(info->ArgumentsRange.first, + info->ArgumentsRange.second); + + return true; +} + bool CPlusPlusLanguage::MethodName::TrySimplifiedParse() { // This method tries to parse simple method definitions which are presumably // most comman in user programs. Definitions that can be parsed by this @@ -1699,65 +1873,201 @@ bool CPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const { return file_path.contains("/usr/include/c++/"); } +static VariableListSP GetFunctionVariableList(const SymbolContext &sc) { + assert(sc.function); + + if (sc.block) + if (Block *inline_block = sc.block->GetContainingInlinedBlock()) + return inline_block->GetBlockVariableList(true); + + return sc.function->GetBlock(true).GetBlockVariableList(true); +} + +static bool PrintFunctionNameWithArgs(Stream &s, + const ExecutionContext *exe_ctx, + const SymbolContext &sc) { + assert(sc.function); + + ExecutionContextScope *exe_scope = + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; + + const char *cstr = sc.GetPossiblyInlinedFunctionName() + .GetName(Mangled::NamePreference::ePreferDemangled) + .AsCString(); + if (!cstr) + return false; + + VariableList args; + if (auto variable_list_sp = GetFunctionVariableList(sc)) + variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, + args); + + if (args.GetSize() > 0) + return PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args); + + // FIXME: can we just unconditionally call PrettyPrintFunctionNameWithArgs? + // It should be able to handle the "no arguments" case. + s.PutCString(cstr); + + return true; +} + bool CPlusPlusLanguage::GetFunctionDisplayName( - const SymbolContext *sc, const ExecutionContext *exe_ctx, + const SymbolContext &sc, const ExecutionContext *exe_ctx, FunctionNameRepresentation representation, Stream &s) { switch (representation) { case FunctionNameRepresentation::eNameWithArgs: { // Print the function name with arguments in it - if (sc->function) { - ExecutionContextScope *exe_scope = - exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; - const char *cstr = sc->function->GetName().AsCString(nullptr); - if (cstr) { - const InlineFunctionInfo *inline_info = nullptr; - VariableListSP variable_list_sp; - bool get_function_vars = true; - if (sc->block) { - Block *inline_block = sc->block->GetContainingInlinedBlock(); - - if (inline_block) { - get_function_vars = false; - inline_info = inline_block->GetInlinedFunctionInfo(); - if (inline_info) - variable_list_sp = inline_block->GetBlockVariableList(true); - } - } - - if (get_function_vars) { - variable_list_sp = - sc->function->GetBlock(true).GetBlockVariableList(true); - } - - if (inline_info) { - s.PutCString(cstr); - s.PutCString(" [inlined] "); - cstr = inline_info->GetName().GetCString(); - } - - VariableList args; - if (variable_list_sp) - variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, - args); - if (args.GetSize() > 0) { - if (!PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args)) - return false; - } else { - s.PutCString(cstr); - } - return true; - } - } else if (sc->symbol) { - const char *cstr = sc->symbol->GetName().AsCString(nullptr); - if (cstr) { - s.PutCString(cstr); - return true; - } - } - } break; + if (sc.function) + return PrintFunctionNameWithArgs(s, exe_ctx, sc); + + if (!sc.symbol) + return false; + + const char *cstr = sc.symbol->GetName().AsCString(nullptr); + if (!cstr) + return false; + + s.PutCString(cstr); + + return true; + } + case FunctionNameRepresentation::eNameWithNoArgs: + case FunctionNameRepresentation::eName: + return false; + } +} + +bool CPlusPlusLanguage::HandleFrameFormatVariable( + const SymbolContext &sc, const ExecutionContext *exe_ctx, + FormatEntity::Entry::Type type, Stream &s) { + switch (type) { + case FormatEntity::Entry::Type::FunctionScope: { + std::optional scope = GetDemangledScope(sc); + if (!scope) + return false; + + s << *scope; + + return true; + } + + case FormatEntity::Entry::Type::FunctionBasename: { + std::optional name = GetDemangledBasename(sc); + if (!name) + return false; + + s << *name; + + return true; + } + + case FormatEntity::Entry::Type::FunctionTemplateArguments: { + std::optional template_args = + GetDemangledTemplateArguments(sc); + if (!template_args) + return false; + + s << *template_args; + + return true; + } + + case FormatEntity::Entry::Type::FunctionFormattedArguments: { + // This ensures we print the arguments even when no debug-info is available. + // + // FIXME: we should have a Entry::Type::FunctionArguments and + // use it in the plugin.cplusplus.display.function-name-format + // once we have a "fallback operator" in the frame-format language. + if (!sc.function && sc.symbol) + return PrintDemangledArgumentList(s, sc); + + VariableList args; + if (auto variable_list_sp = GetFunctionVariableList(sc)) + variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, + args); + + ExecutionContextScope *exe_scope = + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; + + s << '('; + FormatEntity::PrettyPrintFunctionArguments(s, args, exe_scope); + s << ')'; + + return true; + } + case FormatEntity::Entry::Type::FunctionReturnRight: { + std::optional return_rhs = GetDemangledReturnTypeRHS(sc); + if (!return_rhs) + return false; + + s << *return_rhs; + + return true; + } + case FormatEntity::Entry::Type::FunctionReturnLeft: { + std::optional return_lhs = GetDemangledReturnTypeLHS(sc); + if (!return_lhs) + return false; + + s << *return_lhs; + + return true; + } + case FormatEntity::Entry::Type::FunctionQualifiers: { + std::optional quals = GetDemangledFunctionQualifiers(sc); + if (!quals) + return false; + + s << *quals; + + return true; + } default: return false; } +} + +#define LLDB_PROPERTIES_language_cplusplus +#include "LanguageCPlusPlusProperties.inc" + +enum { +#define LLDB_PROPERTIES_language_cplusplus +#include "LanguageCPlusPlusPropertiesEnum.inc" +}; + +namespace { +class PluginProperties : public Properties { +public: + static llvm::StringRef GetSettingName() { return "display"; } + + PluginProperties() { + m_collection_sp = std::make_shared(GetSettingName()); + m_collection_sp->Initialize(g_language_cplusplus_properties); + } - return false; + const FormatEntity::Entry *GetFunctionNameFormat() const { + return GetPropertyAtIndexAs( + ePropertyFunctionNameFormat); + } +}; +} // namespace + +static PluginProperties &GetGlobalPluginProperties() { + static PluginProperties g_settings; + return g_settings; +} + +const FormatEntity::Entry *CPlusPlusLanguage::GetFunctionNameFormat() const { + return GetGlobalPluginProperties().GetFunctionNameFormat(); +} + +void CPlusPlusLanguage::DebuggerInitialize(Debugger &debugger) { + if (!PluginManager::GetSettingForCPlusPlusLanguagePlugin( + debugger, PluginProperties::GetSettingName())) { + PluginManager::CreateSettingForCPlusPlusLanguagePlugin( + debugger, GetGlobalPluginProperties().GetValueProperties(), + "Properties for the CPlusPlus language plug-in.", + /*is_global_property=*/true); + } } diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h index 623d481bf117f..8dd487d54cc7d 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -138,11 +138,16 @@ class CPlusPlusLanguage : public Language { ConstString GetDemangledFunctionNameWithoutArguments(Mangled mangled) const override; - bool GetFunctionDisplayName(const SymbolContext *sc, + bool GetFunctionDisplayName(const SymbolContext &sc, const ExecutionContext *exe_ctx, FunctionNameRepresentation representation, Stream &s) override; + bool HandleFrameFormatVariable(const SymbolContext &sc, + const ExecutionContext *exe_ctx, + FormatEntity::Entry::Type type, + Stream &s) override; + static bool IsCPPMangledName(llvm::StringRef name); // Extract C++ context and identifier from a string using heuristic matching @@ -169,8 +174,13 @@ class CPlusPlusLanguage : public Language { llvm::StringRef GetInstanceVariableName() override { return "this"; } + const FormatEntity::Entry *GetFunctionNameFormat() const override; + // PluginInterface protocol llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + +private: + static void DebuggerInitialize(Debugger &); }; } // namespace lldb_private diff --git a/lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td b/lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td new file mode 100644 index 0000000000000..348de256b154a --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td @@ -0,0 +1,8 @@ +include "../../../../include/lldb/Core/PropertiesBase.td" + +let Definition = "language_cplusplus" in { + def FunctionNameFormat: Property<"function-name-format", "FormatEntity">, + Global, + DefaultStringValue<"${function.return-left}${function.scope}${ansi.fg.yellow}${function.basename}${ansi.normal}${function.template-arguments}${function.formatted-arguments}${function.return-right}${function.qualifiers}">, + Desc<"C++ specific frame format string to use when displaying stack frame information for threads.">; +} diff --git a/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp b/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp index b793c41a6ac96..edfe3b07e30fc 100644 --- a/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp +++ b/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp @@ -1699,33 +1699,33 @@ bool SwiftLanguage::IsUninitializedReference(ValueObject &valobj) { } bool SwiftLanguage::GetFunctionDisplayName( - const SymbolContext *sc, const ExecutionContext *exe_ctx, + const SymbolContext &sc, const ExecutionContext *exe_ctx, FunctionNameRepresentation representation, Stream &s) { switch (representation) { case Language::FunctionNameRepresentation::eName: // No need to customize this. return false; case Language::FunctionNameRepresentation::eNameWithNoArgs: { - if (!sc->function) + if (!sc.function) return false; - if (sc->function->GetLanguage() != eLanguageTypeSwift) + if (sc.function->GetLanguage() != eLanguageTypeSwift) return false; std::string display_name = SwiftLanguageRuntime::DemangleSymbolAsString( - sc->function->GetMangled().GetMangledName().GetStringRef(), - SwiftLanguageRuntime::eSimplified, sc, exe_ctx); + sc.function->GetMangled().GetMangledName().GetStringRef(), + SwiftLanguageRuntime::eSimplified, &sc, exe_ctx); if (display_name.empty()) return false; s << display_name; return true; } case Language::FunctionNameRepresentation::eNameWithArgs: { - if (!sc->function) + if (!sc.function) return false; - if (sc->function->GetLanguage() != eLanguageTypeSwift) + if (sc.function->GetLanguage() != eLanguageTypeSwift) return false; std::string display_name = SwiftLanguageRuntime::DemangleSymbolAsString( - sc->function->GetMangled().GetMangledName().GetStringRef(), - SwiftLanguageRuntime::eSimplified, sc, exe_ctx); + sc.function->GetMangled().GetMangledName().GetStringRef(), + SwiftLanguageRuntime::eSimplified, &sc, exe_ctx); if (display_name.empty()) return false; ExecutionContextScope *exe_scope = @@ -1733,12 +1733,12 @@ bool SwiftLanguage::GetFunctionDisplayName( const InlineFunctionInfo *inline_info = NULL; VariableListSP variable_list_sp; bool get_function_vars = true; - if (sc->block) { - Block *inline_block = sc->block->GetContainingInlinedBlock(); + if (sc.block) { + Block *inline_block = sc.block->GetContainingInlinedBlock(); if (inline_block) { get_function_vars = false; - inline_info = sc->block->GetInlinedFunctionInfo(); + inline_info = sc.block->GetInlinedFunctionInfo(); if (inline_info) variable_list_sp = inline_block->GetBlockVariableList(true); } @@ -1746,7 +1746,7 @@ bool SwiftLanguage::GetFunctionDisplayName( if (get_function_vars) { variable_list_sp = - sc->function->GetBlock(true).GetBlockVariableList(true); + sc.function->GetBlock(true).GetBlockVariableList(true); } if (inline_info) { diff --git a/lldb/source/Plugins/Language/Swift/SwiftLanguage.h b/lldb/source/Plugins/Language/Swift/SwiftLanguage.h index b6da165402b5e..c93743f595661 100644 --- a/lldb/source/Plugins/Language/Swift/SwiftLanguage.h +++ b/lldb/source/Plugins/Language/Swift/SwiftLanguage.h @@ -64,7 +64,7 @@ class SwiftLanguage : public Language { bool IsUninitializedReference(ValueObject &valobj) override; - bool GetFunctionDisplayName(const SymbolContext *sc, + bool GetFunctionDisplayName(const SymbolContext &sc, const ExecutionContext *exe_ctx, FunctionNameRepresentation representation, Stream &s) override; diff --git a/lldb/source/Symbol/SymbolContext.cpp b/lldb/source/Symbol/SymbolContext.cpp index 29b0b3c177ec1..afdd5699355e3 100644 --- a/lldb/source/Symbol/SymbolContext.cpp +++ b/lldb/source/Symbol/SymbolContext.cpp @@ -10,6 +10,7 @@ #include "lldb/Core/Address.h" #include "lldb/Core/Debugger.h" +#include "lldb/Core/DemangledNameInfo.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Host/Host.h" @@ -925,6 +926,38 @@ const Symbol *SymbolContext::FindBestGlobalDataSymbol(ConstString name, return nullptr; // no error; we just didn't find anything } +Mangled SymbolContext::GetPossiblyInlinedFunctionName() const { + auto get_mangled = [this]() { + if (function) + return function->GetMangled(); + + if (symbol) + return symbol->GetMangled(); + + return Mangled{}; + }; + + if (!block) + return get_mangled(); + + const Block *inline_block = block->GetContainingInlinedBlock(); + if (!inline_block) + return get_mangled(); + + const InlineFunctionInfo *inline_info = + inline_block->GetInlinedFunctionInfo(); + if (!inline_info) + return get_mangled(); + + // If we do have an inlined frame name, return that. + if (const Mangled &inline_name = inline_info->GetMangled()) + return inline_name; + + // Sometimes an inline frame may not have mangling information, + // but does have a valid name. + return Mangled{inline_info->GetName().AsCString()}; +} + // // SymbolContextSpecifier // diff --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp index a75894ffa4b3b..86754c251cd93 100644 --- a/lldb/source/Target/Language.cpp +++ b/lldb/source/Target/Language.cpp @@ -510,7 +510,7 @@ bool Language::IsNilReference(ValueObject &valobj) { return false; } bool Language::IsUninitializedReference(ValueObject &valobj) { return false; } -bool Language::GetFunctionDisplayName(const SymbolContext *sc, +bool Language::GetFunctionDisplayName(const SymbolContext &sc, const ExecutionContext *exe_ctx, FunctionNameRepresentation representation, Stream &s) { diff --git a/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp b/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp index 7ad72b4880d79..64e2a5b479675 100644 --- a/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp +++ b/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp @@ -70,8 +70,8 @@ __attribute__((noinline)) void func6(int &sink, int x) { __attribute__((noinline)) void func7(int &sink, int x) { //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC7-BT") // FUNC7-BT: func7 - // FUNC7-BT-NEXT: [inlined] func8_inlined - // FUNC7-BT-NEXT: [inlined] func9_inlined + // FUNC7-BT-NEXT: func8_inlined + // FUNC7-BT-NEXT: func9_inlined // FUNC7-BT-NEXT: func10 use(sink, x); use(dummy, 0); diff --git a/lldb/test/API/functionalities/tail_call_frames/inlining_and_tail_calls/main.cpp b/lldb/test/API/functionalities/tail_call_frames/inlining_and_tail_calls/main.cpp index 9829e0246fc2b..0a7d365d776c6 100644 --- a/lldb/test/API/functionalities/tail_call_frames/inlining_and_tail_calls/main.cpp +++ b/lldb/test/API/functionalities/tail_call_frames/inlining_and_tail_calls/main.cpp @@ -1,13 +1,13 @@ volatile int x; +// clang-format off void __attribute__((noinline)) tail_call_sink() { x++; //% self.filecheck("bt", "main.cpp", "-check-prefix=TAIL-CALL-SINK") // TAIL-CALL-SINK: frame #0: 0x{{[0-9a-f]+}} a.out`tail_call_sink() at main.cpp:[[@LINE-1]]:4 - // TAIL-CALL-SINK-NEXT: func3{{.*}} [artificial] + // TAIL-CALL-SINK-NEXT: inlinable_function_which_tail_calls() at main.cpp{{.*}} [artificial] // TAIL-CALL-SINK-NEXT: main{{.*}} - - // TODO: The backtrace should include inlinable_function_which_tail_calls. } +// clang-format on void __attribute__((always_inline)) inlinable_function_which_tail_calls() { tail_call_sink(); @@ -17,13 +17,15 @@ void __attribute__((noinline)) func3() { inlinable_function_which_tail_calls(); } +// clang-format off void __attribute__((always_inline)) inline_sink() { x++; //% self.filecheck("bt", "main.cpp", "-check-prefix=INLINE-SINK") - // INLINE-SINK: frame #0: 0x{{[0-9a-f]+}} a.out`func2() [inlined] inline_sink() at main.cpp:[[@LINE-1]]:4 + // INLINE-SINK: frame #0: 0x{{[0-9a-f]+}} a.out`inline_sink() at main.cpp:[[@LINE-1]]:4 // INLINE-SINK-NEXT: func2{{.*}} // INLINE-SINK-NEXT: func1{{.*}} [artificial] // INLINE-SINK-NEXT: main{{.*}} } +// clang-format on void __attribute__((noinline)) func2() { inline_sink(); /* inlined */ } diff --git a/lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test index 0c3275c571b3d..2ea6594643c9c 100644 --- a/lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test +++ b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test @@ -12,5 +12,5 @@ run frame recognizer info 0 # CHECK: frame 0 is recognized by Verbose Trap StackFrame Recognizer frame info -# CHECK: frame #0: {{.*}}`std::recursively_aborts(int) {{.*}} at verbose_trap-in-stl-max-depth.cpp +# CHECK: frame #0: {{.*}}`__clang_trap_msg$Error$max depth at verbose_trap-in-stl-max-depth.cpp q diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormat.test b/lldb/test/Shell/Settings/TestCxxFrameFormat.test new file mode 100644 index 0000000000000..0db3bfa1b4a10 --- /dev/null +++ b/lldb/test/Shell/Settings/TestCxxFrameFormat.test @@ -0,0 +1,34 @@ +# XFAIL: target-windows + +# Test the plugin.cplusplus.display.function-name-format setting. + +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out +# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.cpp +namespace ns::ns2 { +void custom(int x) asm("_Zinvalid_mangling"); +void custom(int x) {} + +void bar() { custom(5); } +void foo() { bar(); } +} + +int main(int argc, char const *argv[]) { + ns::ns2::foo(); + return 0; +} + +#--- commands.input +settings set plugin.cplusplus.display.function-name-format "${function.scope}${function.basename}" +settings set -f frame-format "custom-frame '${function.name-with-args}'\n" +break set -l 3 + +run +bt + +# CHECK: custom-frame '_Zinvalid_mangling(x=5)' +# CHECK: custom-frame 'ns::ns2::bar' +# CHECK: custom-frame 'ns::ns2::foo' diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test b/lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test new file mode 100644 index 0000000000000..bafd36f5ae177 --- /dev/null +++ b/lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test @@ -0,0 +1,53 @@ +# UNSUPPORTED: system-windows + +# Test the plugin.cplusplus.display.function-name-format setting +# when interoperating multiple languages. + +# RUN: split-file %s %t +# RUN: %clangxx_host -x c -c -g %t/lib.c -o %t.clib.o +# RUN: %clangxx_host -c -g %t/lib.cpp -o %t.cxxlib.o +# RUN: %clangxx_host %t/main.m %t.cxxlib.o %t.clib.o -o %t.out +# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 | FileCheck %s + +#--- lib.c + +void foo(); + +void func() { + foo(); +} + +#--- lib.cpp + +namespace ns { +struct Foo { + void method() {} +}; +} + +extern "C" { +void foo() { + ns::Foo{}.method(); +} +} + +#--- main.m + +void func(); + +int main() { + func(); +} + +#--- commands.input +settings set plugin.cplusplus.display.function-name-format "this affects C++ only" +settings set -f frame-format "custom-frame '${function.name-with-args}'\n" +break set -n method + +run +bt + +# CHECK: custom-frame 'this affects C++ only' +# CHECK: custom-frame 'this affects C++ only' +# CHECK: custom-frame 'func' +# CHECK: custom-frame 'main' diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test b/lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test new file mode 100644 index 0000000000000..ba574444bc8a8 --- /dev/null +++ b/lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test @@ -0,0 +1,26 @@ +# UNSUPPORTED: system-windows + +# Test the plugin.cplusplus.display.function-name-format setting. + +# RUN: split-file %s %t +# RUN: %build %t/main.m -o %t.objc.out +# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.m + +int func(int x) {} +int bar(int y) { func(y); } + +int main() { return bar(10); } + +#--- commands.input +settings set plugin.cplusplus.display.function-name-format "this affects C++ only" +settings set -f frame-format "custom-frame '${function.name-with-args}'\n" +break set -l 3 +run + +bt + +# CHECK: bt +# CHECK-NOT: this affects C++ only diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test b/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test new file mode 100644 index 0000000000000..f10de878b8d88 --- /dev/null +++ b/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test @@ -0,0 +1,31 @@ +# XFAIL: target-windows + +# Test that the plugin.cplusplus.display.function-name-format setting +# doesn't print into the frame-format setting unless all its format variables +# were successful. + +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out +# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.cpp +template T gunc(int x = 10) { + return T{}; +} + +int main(int argc, const char *argv[]) { + gunc(); + return 0; +} + +#--- commands.input +settings set plugin.cplusplus.display.function-name-format "${function.basename}${script.target:invalid_func}" +settings set -f frame-format "custom-frame '${function.name-with-args}'\n" +break set -l 2 + +run +bt + +# CHECK: custom-frame 'int gunc(x=10)' +# CHECK: custom-frame 'main(argc=1, argv={{.*}})' diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test b/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test new file mode 100644 index 0000000000000..887de882886ed --- /dev/null +++ b/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test @@ -0,0 +1,25 @@ +# XFAIL: * + +# Test disallowed variables inside the +# plugin.cplusplus.display.function-name-format setting. + +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out +# RUN: %lldb -o "settings set interpreter.stop-command-source-on-error false" \ +# RUN: -x -b -s %t/commands.input %t.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.cpp +int main(int argc, char const *argv[]) { return 0; } + +#--- commands.input +settings set plugin.cplusplus.display.function-name-format "${function.name-with-args}" +settings set -f frame-format "custom-frame '${function.name-with-args}'\n" +b main +run + +bt + +# CHECK: bt +# CHECK-NOT: custom-frame +# CHECK: main diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test new file mode 100644 index 0000000000000..7e34fbd3855d0 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test @@ -0,0 +1,52 @@ +# XFAIL: target-windows + +# Test the ${function.basename} frame-format variable. + +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out +# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ +# RUN: | FileCheck %s +# +# RUN: %clang_host -O0 %t/main.cpp -o %t-nodebug.out +# RUN: %lldb -x -b -s %t/commands.input %t-nodebug.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.cpp +namespace ns { +template +struct Bar { + template + T bar(K k) const & { return 1.0f; } +}; + +template +struct Foo { + template + [[gnu::abi_tag("Test")]] void foo() const volatile && { + Bar b; + b.bar(b); + } +}; + +template +T func() { + ns::Foo{}.foo(); + return T{}; +} +} // namespace ns + +int main() { + ns::func>(); + return 0; +} + +#--- commands.input +settings set -f frame-format "custom-frame '${function.basename}'\n" +break set -l 5 + +run +bt + +# CHECK: custom-frame 'bar' +# CHECK: custom-frame 'foo[abi:Test]' +# CHECK: custom-frame 'func' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test new file mode 100644 index 0000000000000..e41ba49f112a9 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test @@ -0,0 +1,24 @@ +# Check that we have an appropriate fallback for ${function.basename} in languages that +# don't implement this frame format variable (in this case Objective-C). +# +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.m -o %t.objc.out +# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.m + +int func() {} +int bar() { func(); } + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.basename}'\n" +break set -n bar + +run +bt + +# CHECK: bt +# CHECK-NOT: custom-frame diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test new file mode 100644 index 0000000000000..04f51701a2a2d --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test @@ -0,0 +1,53 @@ +# XFAIL: target-windows + +# Test the ${function.formatted-arguments} frame-format variable. + +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out +# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ +# RUN: | FileCheck %s +# +# RUN: %clang_host -O0 %t/main.cpp -o %t-nodebug.out +# RUN: %lldb -x -b -s %t/commands.input %t-nodebug.out -o exit 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-NODEBUG + +#--- main.cpp +struct Foo { + void func() {} +}; + +void bar() { + Foo{}.func(); +} + +void foo(int, int x) { + bar(); +} + +void myFunc(char const * str, + void (*fptr)(int, int)) { + fptr(5, 10); +} + +int main(int argc, char const *argv[]) { + myFunc("hello", &foo); + return 0; +} + +#--- commands.input +settings set -f frame-format "custom-frame '${function.formatted-arguments}'\n" +break set -n func + +run +bt + +# CHECK: custom-frame '(this={{.*}})' +# CHECK: custom-frame '()' +# CHECK: custom-frame '({{.*}}=5, x=10)' +# CHECK: custom-frame '(str="hello", fptr=({{.*}}.out`{{.*}}foo(int,{{.*}}int) at main.cpp:{{[0-9]+}}))' +# CHECK: custom-frame '(argc=1, argv={{.*}})' + +# CHECK-NODEBUG: custom-frame '()' +# CHECK-NODEBUG: custom-frame '()' +# CHECK-NODEBUG: custom-frame '(int, int)' +# CHECK-NODEBUG: custom-frame '(char const*, void (*)(int, int))' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test new file mode 100644 index 0000000000000..c9da1d4bd7772 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test @@ -0,0 +1,26 @@ +# UNSUPPORTED: system-windows + +# Check that we have an appropriate fallback for ${function.formatted-arguments} in languages that +# don't implement this frame format variable (in this case Objective-C). +# +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.m -o %t.objc.out +# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.m + +int func() {} +int bar() { func(); } + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.formatted-arguments}'\n" +break set -n func + +run +bt + +# CHECK: bt +# CHECK-NOT: custom-frame diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test new file mode 100644 index 0000000000000..b1dfe834c1deb --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test @@ -0,0 +1,30 @@ +# XFAIL: target-windows + +# Test the ${function.qualifiers} frame-format variable. + +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out +# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ +# RUN: | FileCheck %s +# +# RUN: %clang_host -O0 %t/main.cpp -o %t-nodebug.out +# RUN: %lldb -x -b -s %t/commands.input %t-nodebug.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.cpp +struct Foo { + void foo() const volatile && {} + void bar() { Foo{}.foo(); } +}; + +int main() { Foo{}.bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.qualifiers}'\n" +break set -n foo + +run +bt + +# CHECK: custom-frame ' const volatile &&' +# CHECK: custom-frame '' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test new file mode 100644 index 0000000000000..f3e2b8ea9a6b9 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test @@ -0,0 +1,24 @@ +# Check that we have an appropriate fallback for ${function.qualifiers} in +# languages that don't implement this frame format variable (in this case Objective-C). + +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.m -o %t.objc.out +# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.m + +int foo() {} +int bar() { foo(); } + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.qualifiers}'\n" +break set -n foo + +run +bt + +# CHECK: bt +# CHECK-NOT: custom-frame diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test new file mode 100644 index 0000000000000..f913162a1aa66 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test @@ -0,0 +1,61 @@ +# XFAIL: target-windows + +# Test the ${function.return-left} and ${function.return-right} +# frame-format variables. + +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out +# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ +# RUN: | FileCheck %s +# +# RUN: %clang_host -O0 %t/main.cpp -o %t-nodebug.out +# RUN: %lldb -x -b -s %t/commands.input %t-nodebug.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.cpp +namespace ns::ns2 { +template +struct Foo {}; + +template +Foo qux(int) { + return {}; +} + +template +Foo (*bar(Foo))(int) { + qux(5); + return qux; +} + +struct Bar { + template + Foo (* (*foo(int) const &&)(Foo))(int) { + bar(Foo{}); + return bar; + } +}; +} + +int main(int argc, char const *argv[]) { + ns::ns2::Bar{}.foo(5); + return 0; +} + +#--- commands.input +settings set -f frame-format "custom-frame '${function.return-left}'\n" +break set -n qux + +run +bt + +# CHECK: custom-frame 'ns::ns2::Foo ' +# CHECK: custom-frame 'ns::ns2::Foo (*' +# CHECK: custom-frame 'ns::ns2::Foo (* (*' + +settings set -f frame-format "other-frame '${function.return-right}'\n" +bt + +# CHECK: other-frame '' +# CHECK: other-frame ')(int)' +# CHECK: other-frame ')(ns::ns2::Foo))(int)' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test new file mode 100644 index 0000000000000..2692c3d9c3e70 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test @@ -0,0 +1,31 @@ +# Check that we have an appropriate fallback for ${function.return-left} and +# ${function.return-right} in languages that don't implement this frame +# format variable (in this case Objective-C). +# +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.m -o %t.objc.out +# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.m + +int qux() {} +int bar() { qux(); } + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.return-left}'\n" +break set -n qux + +run +bt + +# CHECK: bt +# CHECK-NOT: custom-frame + +settings set -f frame-format "other-frame '${function.return-right}'\n" +bt + +# CHECK: bt +# CHECK-NOT: other-frame diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test new file mode 100644 index 0000000000000..a28c16f95a9e2 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test @@ -0,0 +1,46 @@ +# XFAIL: target-windows + +# Test the ${function.scope} frame-format variable. + +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out +# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ +# RUN: | FileCheck %s +# +# RUN: %clang_host -O0 %t/main.cpp -o %t-nodebug.out +# RUN: %lldb -x -b -s %t/commands.input %t-nodebug.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.cpp +namespace ns::ns2 { +inline namespace ins { +template +struct Foo { + void func() {} +}; + +int foo() { + Foo{}.func(); + return 5; +} +} // namespace ins +} // namespace ns::ns2 + +using namespace ns::ns2; + +int bar() { + return ns::ns2::foo(); +} + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.scope}'\n" +break set -n func + +run +bt + +# CHECK: frame 'ns::ns2::ins::Foo::' +# CHECK: frame 'ns::ns2::ins::' +# CHECK: frame '' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test new file mode 100644 index 0000000000000..be6ed7f1a3e04 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test @@ -0,0 +1,24 @@ +# Check that we have an appropriate fallback for ${function.scope} in languages that +# don't implement this frame format variable (in this case Objective-C). +# +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.m -o %t.objc.out +# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.m + +int func() {} +int bar() { func(); } + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.scope}'\n" +break set -n func + +run +bt + +# CHECK: bt +# CHECK-NOT: custom-frame diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test new file mode 100644 index 0000000000000..1421c8e918a03 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test @@ -0,0 +1,43 @@ +# XFAIL: target-windows + +# Test the ${function.template-arguments} frame-format variable. + +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out +# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ +# RUN: | FileCheck %s +# +# RUN: %clang_host -O0 %t/main.cpp -o %t-nodebug.out +# RUN: %lldb -x -b -s %t/commands.input %t-nodebug.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.cpp +template +struct Foo { + template + void func() {} +}; + +template class K, + typename M> +int foo() { + Foo{}.func(); + return 5; +} + +int bar() { + return foo>(); +} + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.template-arguments}'\n" +break set -l 4 + +run +bt + +# CHECK: custom-frame '' +# CHECK: custom-frame '>' +# CHECK: custom-frame '' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test new file mode 100644 index 0000000000000..3f9872965e8c2 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test @@ -0,0 +1,24 @@ +# Check that we have an appropriate fallback for ${function.template-arguments} in +# languages that don't implement this frame format variable (in this case Objective-C). +# +# RUN: split-file %s %t +# RUN: %clang_host -g -gdwarf %t/main.m -o %t.objc.out +# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.m + +int func() {} +int bar() { func(); } + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.template-arguments}'\n" +break set -n func + +run +bt + +# CHECK: bt +# CHECK-NOT: custom-frame diff --git a/lldb/test/Shell/Settings/TestFrameFormatName.test b/lldb/test/Shell/Settings/TestFrameFormatName.test index caa3242527c6e..34eb2f4975315 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatName.test +++ b/lldb/test/Shell/Settings/TestFrameFormatName.test @@ -22,15 +22,15 @@ c c # NAME_WITH_ARGS: frame int ns::foo(str="method") c -# NAME_WITH_ARGS: frame ns::returns_func_ptr((null)={{.*}}) +# NAME_WITH_ARGS: frame detail::Quux (* (*ns::returns_func_ptr((null)={{.*}}))(int))(float) c # NAME_WITH_ARGS: frame void Foo::foo(this={{.*}}, arg=({{.*}}`(anonymous namespace)::anon_bar() at {{.*}})) c # NAME_WITH_ARGS: frame void Foo::operator<<<1>(this={{.*}}, (null)=0) c -# NAME_WITH_ARGS: frame Foo::returns_func_ptr(this={{.*}}, (null)={{.*}}) +# NAME_WITH_ARGS: frame detail::Quux (* (*Foo::returns_func_ptr(this={{.*}}, (null)={{.*}}))(int))(float) const c -# NAME_WITH_ARGS: frame main [inlined] inlined_foo(str="bar") +# NAME_WITH_ARGS: frame inlined_foo(str="bar") q #--- name.input @@ -38,18 +38,18 @@ q settings set -f frame-format "frame ${function.name}\n" break set -n inlined_foo run -# NAME: frame main [inlined] inlined_foo(char const*) +# NAME: frame inlined_foo(char const*) #--- name_without_args.input # RUN: %lldb -b -s %t/name_without_args.input %t.out | FileCheck %s --check-prefix=NAME_WITHOUT_ARGS settings set -f frame-format "frame ${function.name-without-args}\n" break set -n inlined_foo run -# NAME_WITHOUT_ARGS: frame main [inlined] inlined_foo(char const*) +# NAME_WITHOUT_ARGS: frame inlined_foo #--- mangled_name.input # RUN: %lldb -b -s %t/mangled_name.input %t.out | FileCheck %s --check-prefix=MANGLED_NAME settings set -f frame-format "frame ${function.mangled-name}\n" break set -n inlined_foo run -# MANGLED_NAME: frame main [inlined] inlined_foo(char const*) +# MANGLED_NAME: frame _Z11inlined_fooPKc diff --git a/lldb/test/Shell/SymbolFile/NativePDB/inline_sites_live.cpp b/lldb/test/Shell/SymbolFile/NativePDB/inline_sites_live.cpp index 767149ea18c46..6b66e5cbceafa 100644 --- a/lldb/test/Shell/SymbolFile/NativePDB/inline_sites_live.cpp +++ b/lldb/test/Shell/SymbolFile/NativePDB/inline_sites_live.cpp @@ -22,12 +22,12 @@ int main(int argc, char** argv) { foo(argc); } -// CHECK: * thread #1, stop reason = breakpoint 1 -// CHECK-NEXT: frame #0: {{.*}}`main [inlined] bar(param=2) +// CHECK: * thread #1, {{.*}}stop reason = breakpoint 1 +// CHECK-NEXT: frame #0: {{.*}}`bar(param=2) // CHECK: (lldb) expression param // CHECK-NEXT: (int) $0 = 2 -// CHECK: * thread #1, stop reason = breakpoint 2 -// CHECK-NEXT: frame #0: {{.*}}`main [inlined] foo(param=1) +// CHECK: * thread #1, {{.*}}stop reason = breakpoint 2 +// CHECK-NEXT: frame #0: {{.*}}`foo(param=1) // CHECK: (lldb) expression param // CHECK-NEXT: (int) $1 = 1 // CHECK-NEXT: (lldb) expression local diff --git a/lldb/unittests/Core/MangledTest.cpp b/lldb/unittests/Core/MangledTest.cpp index a3760ba43b3c9..8a452a84614ef 100644 --- a/lldb/unittests/Core/MangledTest.cpp +++ b/lldb/unittests/Core/MangledTest.cpp @@ -11,6 +11,7 @@ #include "TestingSupport/SubsystemRAII.h" #include "TestingSupport/TestUtilities.h" +#include "lldb/Core/DemangledNameInfo.h" #include "lldb/Core/Mangled.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" @@ -319,3 +320,264 @@ TEST(MangledTest, NameIndexes_FindFunctionSymbols) { EXPECT_EQ(0, Count("undemangable", eFunctionNameTypeBase)); EXPECT_EQ(0, Count("undemangable", eFunctionNameTypeMethod)); } + +static bool NameInfoEquals(const DemangledNameInfo &lhs, + const DemangledNameInfo &rhs) { + return std::tie(lhs.BasenameRange, lhs.ArgumentsRange, lhs.ScopeRange, + lhs.QualifiersRange) == + std::tie(rhs.BasenameRange, rhs.ArgumentsRange, rhs.ScopeRange, + rhs.QualifiersRange); +} + +TEST(MangledTest, DemangledNameInfo_SetMangledResets) { + Mangled mangled; + EXPECT_EQ(mangled.GetDemangledInfo(), std::nullopt); + + mangled.SetMangledName(ConstString("_Z3foov")); + ASSERT_TRUE(mangled); + + auto info1 = mangled.GetDemangledInfo(); + EXPECT_NE(info1, std::nullopt); + EXPECT_TRUE(info1->hasBasename()); + + mangled.SetMangledName(ConstString("_Z4funcv")); + + // Should have re-calculated demangled-info since mangled name changed. + auto info2 = mangled.GetDemangledInfo(); + ASSERT_NE(info2, std::nullopt); + EXPECT_TRUE(info2->hasBasename()); + + EXPECT_FALSE(NameInfoEquals(info1.value(), info2.value())); + EXPECT_EQ(mangled.GetDemangledName(), "func()"); +} + +TEST(MangledTest, DemangledNameInfo_SetDemangledResets) { + Mangled mangled("_Z3foov"); + ASSERT_TRUE(mangled); + + mangled.SetDemangledName(ConstString("")); + + // Mangled name hasn't changed, so GetDemangledInfo causes re-demangling + // of previously set mangled name. + EXPECT_NE(mangled.GetDemangledInfo(), std::nullopt); + EXPECT_EQ(mangled.GetDemangledName(), "foo()"); +} + +TEST(MangledTest, DemangledNameInfo_Clear) { + Mangled mangled("_Z3foov"); + ASSERT_TRUE(mangled); + EXPECT_NE(mangled.GetDemangledInfo(), std::nullopt); + + mangled.Clear(); + + EXPECT_EQ(mangled.GetDemangledInfo(), std::nullopt); +} + +TEST(MangledTest, DemangledNameInfo_SetValue) { + Mangled mangled("_Z4funcv"); + ASSERT_TRUE(mangled); + + auto demangled_func = mangled.GetDemangledInfo(); + + // SetValue(mangled) resets demangled-info + mangled.SetValue(ConstString("_Z3foov")); + auto demangled_foo = mangled.GetDemangledInfo(); + EXPECT_NE(demangled_foo, std::nullopt); + EXPECT_FALSE(NameInfoEquals(demangled_foo.value(), demangled_func.value())); + + // SetValue(demangled) resets demangled-info + mangled.SetValue(ConstString("_Z4funcv")); + EXPECT_TRUE(NameInfoEquals(mangled.GetDemangledInfo().value(), + demangled_func.value())); + + // SetValue(empty) resets demangled-info + mangled.SetValue(ConstString()); + EXPECT_EQ(mangled.GetDemangledInfo(), std::nullopt); + + // Demangling invalid mangled name will set demangled-info + // (without a valid basename). + mangled.SetValue(ConstString("_Zinvalid")); + ASSERT_NE(mangled.GetDemangledInfo(), std::nullopt); + EXPECT_FALSE(mangled.GetDemangledInfo()->hasBasename()); +} + +struct DemanglingPartsTestCase { + const char *mangled; + DemangledNameInfo expected_info; + std::string_view basename; + std::string_view scope; + std::string_view qualifiers; + bool valid_basename = true; +}; + +DemanglingPartsTestCase g_demangling_parts_test_cases[] = { + // clang-format off + { "_ZNVKO3BarIN2ns3QuxIiEEE1CIPFi3FooIS_IiES6_EEE6methodIS6_EENS5_IT_SC_E5InnerIiEESD_SD_", + { /*.BasenameRange=*/{92, 98}, /*.ScopeRange=*/{36, 92}, /*.ArgumentsRange=*/{ 108, 158 }, + /*.QualifiersRange=*/{158, 176} }, + /*.basename=*/"method", + /*.scope=*/"Bar>::C, Bar>)>::", + /*.qualifiers=*/" const volatile &&" + }, + { "_Z7getFuncIfEPFiiiET_", + { /*.BasenameRange=*/{6, 13}, /*.ScopeRange=*/{6, 6}, /*.ArgumentsRange=*/{ 20, 27 }, /*.QualifiersRange=*/{38, 38} }, + /*.basename=*/"getFunc", + /*.scope=*/"", + /*.qualifiers=*/"" + }, + { "_ZN1f1b1c1gEv", + { /*.BasenameRange=*/{9, 10}, /*.ScopeRange=*/{0, 9}, /*.ArgumentsRange=*/{ 10, 12 }, + /*.QualifiersRange=*/{12, 12} }, + /*.basename=*/"g", + /*.scope=*/"f::b::c::", + /*.qualifiers=*/"" + }, + { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bEEEcvT__EES2_", + { /*.BasenameRange=*/{45, 48}, /*.ScopeRange=*/{38, 45}, /*.ArgumentsRange=*/{ 53, 58 }, + /*.QualifiersRange=*/{58, 58} }, + /*.basename=*/"fD1", + /*.scope=*/"test7::", + /*.qualifiers=*/"" + }, + { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_", + { /*.BasenameRange=*/{61, 64}, /*.ScopeRange=*/{54, 61}, /*.ArgumentsRange=*/{ 69, 79 }, + /*.QualifiersRange=*/{79, 79} }, + /*.basename=*/"fD1", + /*.scope=*/"test7::", + /*.qualifiers=*/"" + }, + { "_ZN5test7INDT1cE1dINDT1cE1dEEEE3fD1INDT1cE1dINDT1cE1dEEEEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_", + { /*.BasenameRange=*/{120, 123}, /*.ScopeRange=*/{81, 120}, /*.ArgumentsRange=*/{ 155, 168 }, + /*.QualifiersRange=*/{168, 168} }, + /*.basename=*/"fD1", + /*.scope=*/"test7>::", + /*.qualifiers=*/"" + }, + { "_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvE5parseIRA29_KcEESE_OT_NS2_8functionIFbiNS0_6detail13parse_event_tERSE_EEEbb", + { /*.BasenameRange=*/{687, 692}, /*.ScopeRange=*/{343, 687}, /*.ArgumentsRange=*/{ 713, 1174 }, + /*.QualifiersRange=*/{1174, 1174} }, + /*.basename=*/"parse", + /*.scope=*/"nlohmann::json_abi_v3_11_3::basic_json, std::__1::allocator>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector>, void>::", + /*.qualifiers=*/"" + }, + { "_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvEC1EDn", + { /*.BasenameRange=*/{344, 354}, /*.ScopeRange=*/{0, 344}, /*.ArgumentsRange=*/{ 354, 370 }, + /*.QualifiersRange=*/{370, 370} }, + /*.basename=*/"basic_json", + /*.scope=*/"nlohmann::json_abi_v3_11_3::basic_json, std::__1::allocator>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector>, void>::", + /*.qualifiers=*/"" + }, + { "_Z3fppIiEPFPFvvEiEf", + { /*.BasenameRange=*/{10, 13}, /*.ScopeRange=*/{10, 10}, /*.ArgumentsRange=*/{ 18, 25 }, /*.QualifiersRange=*/{34,34} }, + /*.basename=*/"fpp", + /*.scope=*/"", + /*.qualifiers=*/"" + }, + { "_Z3fppIiEPFPFvvEN2ns3FooIiEEEf", + { /*.BasenameRange=*/{10, 13}, /*.ScopeRange=*/{10, 10}, /*.ArgumentsRange=*/{ 18, 25 }, + /*.QualifiersRange=*/{43, 43} }, + /*.basename=*/"fpp", + /*.scope=*/"", + /*.qualifiers=*/"" + }, + { "_Z3fppIiEPFPFvPFN2ns3FooIiEENS2_3BarIfE3QuxEEEPFS2_S2_EEf", + { /*.BasenameRange=*/{10, 13}, /*.ScopeRange=*/{10, 10}, /*.ArgumentsRange=*/{ 18, 25 }, + /*.QualifiersRange=*/{108, 108} }, + /*.basename=*/"fpp", + /*.scope=*/"", + /*.qualifiers=*/"" + }, + { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvEiEf", + { /*.BasenameRange=*/{64, 67}, /*.ScopeRange=*/{10, 64}, /*.ArgumentsRange=*/{ 72, 79 }, + /*.QualifiersRange=*/{88, 88} }, + /*.basename=*/"fpp", + /*.scope=*/"ns::HasFuncs::Bar::Qux>>::", + /*.qualifiers=*/"" + }, + { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvES2_Ef", + { /*.BasenameRange=*/{64, 67}, /*.ScopeRange=*/{10, 64}, /*.ArgumentsRange=*/{ 72, 79 }, + /*.QualifiersRange=*/{97, 97} }, + /*.basename=*/"fpp", + /*.scope=*/"ns::HasFuncs::Bar::Qux>>::", + /*.qualifiers=*/"", + }, + { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvPFS2_S5_EEPFS2_S2_EEf", + { /*.BasenameRange=*/{64, 67}, /*.ScopeRange=*/{10, 64}, /*.ArgumentsRange=*/{ 72, 79 }, + /*.QualifiersRange=*/{162, 162} }, + /*.basename=*/"fpp", + /*.scope=*/"ns::HasFuncs::Bar::Qux>>::", + /*.qualifiers=*/"", + }, + { "_ZNKO2ns3ns23Bar3fooIiEEPFPFNS0_3FooIiEEiENS3_IfEEEi", + { /*.BasenameRange=*/{37, 40}, /*.ScopeRange=*/{23, 37}, /*.ArgumentsRange=*/{ 45, 50 }, + /*.QualifiersRange=*/{78, 87} }, + /*.basename=*/"foo", + /*.scope=*/"ns::ns2::Bar::", + /*.qualifiers=*/" const &&", + }, + { "_ZTV11ImageLoader", + { /*.BasenameRange=*/{0, 0}, /*.ScopeRange=*/{0, 0}, /*.ArgumentsRange=*/{ 0, 0 }, + /*.QualifiersRange=*/{0, 0} }, + /*.basename=*/"", + /*.scope=*/"", + /*.qualifiers=*/"", + /*.valid_basename=*/false + } + // clang-format on +}; + +struct DemanglingPartsTestFixture + : public ::testing::TestWithParam {}; + +namespace { +class TestAllocator { + llvm::BumpPtrAllocator Alloc; + +public: + void reset() { Alloc.Reset(); } + + template T *makeNode(Args &&...args) { + return new (Alloc.Allocate(sizeof(T), alignof(T))) + T(std::forward(args)...); + } + + void *allocateNodeArray(size_t sz) { + return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz, + alignof(llvm::itanium_demangle::Node *)); + } +}; +} // namespace + +TEST_P(DemanglingPartsTestFixture, DemanglingParts) { + const auto &[mangled, info, basename, scope, qualifiers, valid_basename] = + GetParam(); + + llvm::itanium_demangle::ManglingParser Parser( + mangled, mangled + ::strlen(mangled)); + + const auto *Root = Parser.parse(); + + ASSERT_NE(nullptr, Root); + + TrackingOutputBuffer OB; + Root->print(OB); + auto demangled = std::string_view(OB); + + ASSERT_EQ(OB.NameInfo.hasBasename(), valid_basename); + + EXPECT_EQ(OB.NameInfo.BasenameRange, info.BasenameRange); + EXPECT_EQ(OB.NameInfo.ScopeRange, info.ScopeRange); + EXPECT_EQ(OB.NameInfo.ArgumentsRange, info.ArgumentsRange); + EXPECT_EQ(OB.NameInfo.QualifiersRange, info.QualifiersRange); + + auto get_part = [&](const std::pair &loc) { + return demangled.substr(loc.first, loc.second - loc.first); + }; + + EXPECT_EQ(get_part(OB.NameInfo.BasenameRange), basename); + EXPECT_EQ(get_part(OB.NameInfo.ScopeRange), scope); + EXPECT_EQ(get_part(OB.NameInfo.QualifiersRange), qualifiers); +} + +INSTANTIATE_TEST_SUITE_P(DemanglingPartsTests, DemanglingPartsTestFixture, + ::testing::ValuesIn(g_demangling_parts_test_cases)); diff --git a/llvm/include/llvm/Demangle/Demangle.h b/llvm/include/llvm/Demangle/Demangle.h index 132e5088b5514..21e7457b6336f 100644 --- a/llvm/include/llvm/Demangle/Demangle.h +++ b/llvm/include/llvm/Demangle/Demangle.h @@ -93,6 +93,13 @@ struct ItaniumPartialDemangler { /// second and third parameters to __cxa_demangle. char *finishDemangle(char *Buf, size_t *N) const; + /// See \ref finishDemangle + /// + /// \param[in] OB A llvm::itanium_demangle::OutputBuffer that the demangled + /// name will be printed into. + /// + char *finishDemangle(void *OB) const; + /// Get the base name of a function. This doesn't include trailing template /// arguments, ie for "a::b" this function returns "b". char *getFunctionBaseName(char *Buf, size_t *N) const; diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h index e7c008be32f9e..a398e778b037a 100644 --- a/llvm/include/llvm/Demangle/ItaniumDemangle.h +++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h @@ -277,20 +277,11 @@ class Node { } void print(OutputBuffer &OB) const { - printLeft(OB); + OB.printLeft(*this); if (RHSComponentCache != Cache::No) - printRight(OB); + OB.printRight(*this); } - // Print the "left" side of this Node into OutputBuffer. - virtual void printLeft(OutputBuffer &) const = 0; - - // Print the "right". This distinction is necessary to represent C++ types - // that appear on the RHS of their subtype, such as arrays or functions. - // Since most types don't have such a component, provide a default - // implementation. - virtual void printRight(OutputBuffer &) const {} - virtual std::string_view getBaseName() const { return {}; } // Silence compiler warnings, this dtor will never be called. @@ -299,6 +290,24 @@ class Node { #ifndef NDEBUG DEMANGLE_DUMP_METHOD void dump() const; #endif + +private: + friend class OutputBuffer; + + // Print the "left" side of this Node into OutputBuffer. + // + // Note, should only be called from OutputBuffer implementations. + // Call \ref OutputBuffer::printLeft instead. + virtual void printLeft(OutputBuffer &) const = 0; + + // Print the "right". This distinction is necessary to represent C++ types + // that appear on the RHS of their subtype, such as arrays or functions. + // Since most types don't have such a component, provide a default + // implementation. + // + // Note, should only be called from OutputBuffer implementations. + // Call \ref OutputBuffer::printRight instead. + virtual void printRight(OutputBuffer &) const {} }; class NodeArray { @@ -443,11 +452,11 @@ class QualType final : public Node { } void printLeft(OutputBuffer &OB) const override { - Child->printLeft(OB); + OB.printLeft(*Child); printQuals(OB); } - void printRight(OutputBuffer &OB) const override { Child->printRight(OB); } + void printRight(OutputBuffer &OB) const override { OB.printRight(*Child); } }; class ConversionOperatorType final : public Node { @@ -476,7 +485,7 @@ class PostfixQualifiedType final : public Node { template void match(Fn F) const { F(Ty, Postfix); } void printLeft(OutputBuffer &OB) const override { - Ty->printLeft(OB); + OB.printLeft(*Ty); OB += Postfix; } }; @@ -562,7 +571,7 @@ struct AbiTagAttr : Node { std::string_view getBaseName() const override { return Base->getBaseName(); } void printLeft(OutputBuffer &OB) const override { - Base->printLeft(OB); + OB.printLeft(*Base); OB += "[abi:"; OB += Tag; OB += "]"; @@ -588,8 +597,6 @@ class ObjCProtoName : public Node { const Node *Ty; std::string_view Protocol; - friend class PointerType; - public: ObjCProtoName(const Node *Ty_, std::string_view Protocol_) : Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {} @@ -601,6 +608,8 @@ class ObjCProtoName : public Node { static_cast(Ty)->getName() == "objc_object"; } + std::string_view getProtocol() const { return Protocol; } + void printLeft(OutputBuffer &OB) const override { Ty->print(OB); OB += "<"; @@ -629,7 +638,7 @@ class PointerType final : public Node { // We rewrite objc_object* into id. if (Pointee->getKind() != KObjCProtoName || !static_cast(Pointee)->isObjCObject()) { - Pointee->printLeft(OB); + OB.printLeft(*Pointee); if (Pointee->hasArray(OB)) OB += " "; if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) @@ -638,7 +647,7 @@ class PointerType final : public Node { } else { const auto *objcProto = static_cast(Pointee); OB += "id<"; - OB += objcProto->Protocol; + OB += objcProto->getProtocol(); OB += ">"; } } @@ -648,7 +657,7 @@ class PointerType final : public Node { !static_cast(Pointee)->isObjCObject()) { if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) OB += ")"; - Pointee->printRight(OB); + OB.printRight(*Pointee); } } }; @@ -714,7 +723,7 @@ class ReferenceType : public Node { std::pair Collapsed = collapse(OB); if (!Collapsed.second) return; - Collapsed.second->printLeft(OB); + OB.printLeft(*Collapsed.second); if (Collapsed.second->hasArray(OB)) OB += " "; if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) @@ -731,7 +740,7 @@ class ReferenceType : public Node { return; if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) OB += ")"; - Collapsed.second->printRight(OB); + OB.printRight(*Collapsed.second); } }; @@ -751,7 +760,7 @@ class PointerToMemberType final : public Node { } void printLeft(OutputBuffer &OB) const override { - MemberType->printLeft(OB); + OB.printLeft(*MemberType); if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) OB += "("; else @@ -763,7 +772,7 @@ class PointerToMemberType final : public Node { void printRight(OutputBuffer &OB) const override { if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) OB += ")"; - MemberType->printRight(OB); + OB.printRight(*MemberType); } }; @@ -783,7 +792,7 @@ class ArrayType final : public Node { bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } bool hasArraySlow(OutputBuffer &) const override { return true; } - void printLeft(OutputBuffer &OB) const override { Base->printLeft(OB); } + void printLeft(OutputBuffer &OB) const override { OB.printLeft(*Base); } void printRight(OutputBuffer &OB) const override { if (OB.back() != ']') @@ -792,7 +801,7 @@ class ArrayType final : public Node { if (Dimension) Dimension->print(OB); OB += "]"; - Base->printRight(OB); + OB.printRight(*Base); } }; @@ -827,7 +836,7 @@ class FunctionType final : public Node { // by printing out the return types's left, then print our parameters, then // finally print right of the return type. void printLeft(OutputBuffer &OB) const override { - Ret->printLeft(OB); + OB.printLeft(*Ret); OB += " "; } @@ -835,7 +844,7 @@ class FunctionType final : public Node { OB.printOpen(); Params.printWithComma(OB); OB.printClose(); - Ret->printRight(OB); + OB.printRight(*Ret); if (CVQuals & QualConst) OB += " const"; @@ -940,6 +949,8 @@ class FunctionEncoding final : public Node { FunctionRefQual getRefQual() const { return RefQual; } NodeArray getParams() const { return Params; } const Node *getReturnType() const { return Ret; } + const Node *getAttrs() const { return Attrs; } + const Node *getRequires() const { return Requires; } bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } bool hasFunctionSlow(OutputBuffer &) const override { return true; } @@ -948,10 +959,11 @@ class FunctionEncoding final : public Node { void printLeft(OutputBuffer &OB) const override { if (Ret) { - Ret->printLeft(OB); + OB.printLeft(*Ret); if (!Ret->hasRHSComponent(OB)) OB += " "; } + Name->print(OB); } @@ -959,8 +971,9 @@ class FunctionEncoding final : public Node { OB.printOpen(); Params.printWithComma(OB); OB.printClose(); + if (Ret) - Ret->printRight(OB); + OB.printRight(*Ret); if (CVQuals & QualConst) OB += " const"; @@ -1300,14 +1313,14 @@ class NonTypeTemplateParamDecl final : public Node { template void match(Fn F) const { F(Name, Type); } void printLeft(OutputBuffer &OB) const override { - Type->printLeft(OB); + OB.printLeft(*Type); if (!Type->hasRHSComponent(OB)) OB += " "; } void printRight(OutputBuffer &OB) const override { Name->print(OB); - Type->printRight(OB); + OB.printRight(*Type); } }; @@ -1352,11 +1365,11 @@ class TemplateParamPackDecl final : public Node { template void match(Fn F) const { F(Param); } void printLeft(OutputBuffer &OB) const override { - Param->printLeft(OB); + OB.printLeft(*Param); OB += "..."; } - void printRight(OutputBuffer &OB) const override { Param->printRight(OB); } + void printRight(OutputBuffer &OB) const override { OB.printRight(*Param); } }; /// An unexpanded parameter pack (either in the expression or type context). If @@ -1423,13 +1436,13 @@ class ParameterPack final : public Node { initializePackExpansion(OB); size_t Idx = OB.CurrentPackIndex; if (Idx < Data.size()) - Data[Idx]->printLeft(OB); + OB.printLeft(*Data[Idx]); } void printRight(OutputBuffer &OB) const override { initializePackExpansion(OB); size_t Idx = OB.CurrentPackIndex; if (Idx < Data.size()) - Data[Idx]->printRight(OB); + OB.printRight(*Data[Idx]); } }; @@ -1587,13 +1600,13 @@ struct ForwardTemplateReference : Node { if (Printing) return; ScopedOverride SavePrinting(Printing, true); - Ref->printLeft(OB); + OB.printLeft(*Ref); } void printRight(OutputBuffer &OB) const override { if (Printing) return; ScopedOverride SavePrinting(Printing, true); - Ref->printRight(OB); + OB.printRight(*Ref); } }; @@ -1745,7 +1758,7 @@ class DtorName : public Node { void printLeft(OutputBuffer &OB) const override { OB += "~"; - Base->printLeft(OB); + OB.printLeft(*Base); } }; @@ -2025,7 +2038,7 @@ class CastExpr : public Node { { ScopedOverride LT(OB.GtIsGt, 0); OB += "<"; - To->printLeft(OB); + OB.printLeft(*To); OB += ">"; } OB.printOpen(); @@ -5945,6 +5958,10 @@ struct ManglingParser : AbstractManglingParser, Alloc> { Alloc>::AbstractManglingParser; }; +inline void OutputBuffer::printLeft(const Node &N) { N.printLeft(*this); } + +inline void OutputBuffer::printRight(const Node &N) { N.printRight(*this); } + DEMANGLE_NAMESPACE_END #ifdef _LIBCXXABI_COMPILER_CLANG diff --git a/llvm/include/llvm/Demangle/Utility.h b/llvm/include/llvm/Demangle/Utility.h index e893cceea2cdc..d59d74511dd4f 100644 --- a/llvm/include/llvm/Demangle/Utility.h +++ b/llvm/include/llvm/Demangle/Utility.h @@ -27,6 +27,8 @@ DEMANGLE_NAMESPACE_BEGIN +class Node; + // Stream that AST nodes write their string representation into after the AST // has been parsed. class OutputBuffer { @@ -79,10 +81,24 @@ class OutputBuffer { OutputBuffer(const OutputBuffer &) = delete; OutputBuffer &operator=(const OutputBuffer &) = delete; + virtual ~OutputBuffer() {} + operator std::string_view() const { return std::string_view(Buffer, CurrentPosition); } + /// Called by the demangler when printing the demangle tree. By + /// default calls into \c Node::print{Left|Right} but can be overriden + /// by clients to track additional state when printing the demangled name. + virtual void printLeft(const Node &N); + virtual void printRight(const Node &N); + + /// Called when we write to this object anywhere other than the end. + virtual void notifyInsertion(size_t /*Position*/, size_t /*Count*/) {} + + /// Called when we make the \c CurrentPosition of this object smaller. + virtual void notifyDeletion(size_t /*OldPos*/, size_t /*NewPos*/) {} + /// If a ParameterPackExpansion (or similar type) is encountered, the offset /// into the pack that we're currently printing. unsigned CurrentPackIndex = std::numeric_limits::max(); @@ -126,6 +142,8 @@ class OutputBuffer { std::memcpy(Buffer, &*R.begin(), Size); CurrentPosition += Size; + notifyInsertion(/*Position=*/0, /*Count=*/Size); + return *this; } @@ -161,14 +179,20 @@ class OutputBuffer { DEMANGLE_ASSERT(Pos <= CurrentPosition, ""); if (N == 0) return; + grow(N); std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos); std::memcpy(Buffer + Pos, S, N); CurrentPosition += N; + + notifyInsertion(Pos, N); } size_t getCurrentPosition() const { return CurrentPosition; } - void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; } + void setCurrentPosition(size_t NewPos) { + notifyDeletion(CurrentPosition, NewPos); + CurrentPosition = NewPos; + } char back() const { DEMANGLE_ASSERT(CurrentPosition, ""); diff --git a/llvm/lib/Demangle/ItaniumDemangle.cpp b/llvm/lib/Demangle/ItaniumDemangle.cpp index 5c21b06a1d095..1009cc91ca12a 100644 --- a/llvm/lib/Demangle/ItaniumDemangle.cpp +++ b/llvm/lib/Demangle/ItaniumDemangle.cpp @@ -411,9 +411,7 @@ bool ItaniumPartialDemangler::partialDemangle(const char *MangledName) { RootNode = Parser->parse(); return RootNode == nullptr; } - -static char *printNode(const Node *RootNode, char *Buf, size_t *N) { - OutputBuffer OB(Buf, N); +static char *printNode(const Node *RootNode, OutputBuffer &OB, size_t *N) { RootNode->print(OB); OB += '\0'; if (N != nullptr) @@ -421,6 +419,11 @@ static char *printNode(const Node *RootNode, char *Buf, size_t *N) { return OB.getBuffer(); } +static char *printNode(const Node *RootNode, char *Buf, size_t *N) { + OutputBuffer OB(Buf, N); + return printNode(RootNode, OB, N); +} + char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const { if (!isFunction()) return nullptr; @@ -540,6 +543,14 @@ char *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const { return printNode(static_cast(RootNode), Buf, N); } +char *ItaniumPartialDemangler::finishDemangle(void *OB) const { + assert(RootNode != nullptr && "must call partialDemangle()"); + assert(OB != nullptr && "valid OutputBuffer argument required"); + return printNode(static_cast(RootNode), + *static_cast(OB), + /*N=*/nullptr); +} + bool ItaniumPartialDemangler::hasFunctionQualifiers() const { assert(RootNode != nullptr && "must call partialDemangle()"); if (!isFunction()) diff --git a/llvm/unittests/Demangle/ItaniumDemangleTest.cpp b/llvm/unittests/Demangle/ItaniumDemangleTest.cpp index bc6ccc2e16e65..329f33215817a 100644 --- a/llvm/unittests/Demangle/ItaniumDemangleTest.cpp +++ b/llvm/unittests/Demangle/ItaniumDemangleTest.cpp @@ -98,7 +98,7 @@ TEST(ItaniumDemangle, HalfType) { Node *parseType() { OutputBuffer OB; Node *N = AbstractManglingParser::parseType(); - N->printLeft(OB); + OB.printLeft(*N); std::string_view Name = N->getBaseName(); if (!Name.empty()) Types.push_back(std::string(Name.begin(), Name.end())); diff --git a/llvm/unittests/Demangle/OutputBufferTest.cpp b/llvm/unittests/Demangle/OutputBufferTest.cpp index 76031e523d781..4a30e66eee48e 100644 --- a/llvm/unittests/Demangle/OutputBufferTest.cpp +++ b/llvm/unittests/Demangle/OutputBufferTest.cpp @@ -93,3 +93,40 @@ TEST(OutputBufferTest, Extend) { std::free(OB.getBuffer()); } + +TEST(OutputBufferTest, Notifications) { + struct MyOutputBuffer : public OutputBuffer { + size_t Inserted = 0; + size_t LatestPos = 0; + + void notifyDeletion(size_t OldPos, size_t NewPos) override { + LatestPos = NewPos; + } + + void notifyInsertion(size_t Position, size_t Count) override { + Inserted += Count; + LatestPos = Position; + } + } OB; + + OB.prepend("n"); + EXPECT_EQ(OB.Inserted, 1U); + EXPECT_EQ(OB.LatestPos, 0U); + + OB.prepend(""); + EXPECT_EQ(OB.Inserted, 1U); + EXPECT_EQ(OB.LatestPos, 0U); + + OB.prepend("abc"); + EXPECT_EQ(OB.Inserted, 4U); + EXPECT_EQ(OB.LatestPos, 0U); + + OB.insert(2, "abc", 3U); + EXPECT_EQ(OB.Inserted, 7U); + EXPECT_EQ(OB.LatestPos, 2U); + + OB.setCurrentPosition(3U); + EXPECT_EQ(OB.LatestPos, 3U); + + std::free(OB.getBuffer()); +}