https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/131836
>From d7e112ed0bc8d5ca3297e3c7b7d7afadd2d79e2d Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Tue, 11 Mar 2025 08:57:13 +0000 Subject: [PATCH 1/4] [llvm][ItaniumDemangle] Add function name location tracking --- libcxxabi/src/demangle/ItaniumDemangle.h | 64 +++--- libcxxabi/src/demangle/Utility.h | 7 + lldb/include/lldb/Core/Demangle.h | 90 ++++++++ lldb/source/Core/CMakeLists.txt | 1 + lldb/source/Core/Demangle.cpp | 221 +++++++++++++++++++ lldb/unittests/Core/MangledTest.cpp | 143 ++++++++++++ llvm/include/llvm/Demangle/ItaniumDemangle.h | 64 +++--- llvm/include/llvm/Demangle/Utility.h | 7 + 8 files changed, 541 insertions(+), 56 deletions(-) create mode 100644 lldb/include/lldb/Core/Demangle.h create mode 100644 lldb/source/Core/Demangle.cpp diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h index 3df41b5f4d7d0..89a24def830f2 100644 --- a/libcxxabi/src/demangle/ItaniumDemangle.h +++ b/libcxxabi/src/demangle/ItaniumDemangle.h @@ -281,9 +281,9 @@ 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. @@ -458,11 +458,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 { @@ -491,7 +491,7 @@ class PostfixQualifiedType final : public Node { template<typename Fn> void match(Fn F) const { F(Ty, Postfix); } void printLeft(OutputBuffer &OB) const override { - Ty->printLeft(OB); + OB.printLeft(*Ty); OB += Postfix; } }; @@ -577,7 +577,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 += "]"; @@ -644,7 +644,7 @@ class PointerType final : public Node { // We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>. if (Pointee->getKind() != KObjCProtoName || !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) { - Pointee->printLeft(OB); + OB.printLeft(*Pointee); if (Pointee->hasArray(OB)) OB += " "; if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) @@ -663,7 +663,7 @@ class PointerType final : public Node { !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) { if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) OB += ")"; - Pointee->printRight(OB); + OB.printRight(*Pointee); } } }; @@ -729,7 +729,7 @@ class ReferenceType : public Node { std::pair<ReferenceKind, const Node *> 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)) @@ -746,7 +746,7 @@ class ReferenceType : public Node { return; if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) OB += ")"; - Collapsed.second->printRight(OB); + OB.printRight(*Collapsed.second); } }; @@ -766,7 +766,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 @@ -778,7 +778,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); } }; @@ -798,7 +798,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() != ']') @@ -807,7 +807,7 @@ class ArrayType final : public Node { if (Dimension) Dimension->print(OB); OB += "]"; - Base->printRight(OB); + OB.printRight(*Base); } bool printInitListAsType(OutputBuffer &OB, @@ -851,7 +851,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 += " "; } @@ -859,7 +859,7 @@ class FunctionType final : public Node { OB.printOpen(); Params.printWithComma(OB); OB.printClose(); - Ret->printRight(OB); + OB.printRight(*Ret); if (CVQuals & QualConst) OB += " const"; @@ -964,6 +964,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; } @@ -972,10 +974,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); } @@ -983,8 +986,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"; @@ -1324,14 +1328,14 @@ class NonTypeTemplateParamDecl final : public Node { template<typename Fn> 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); } }; @@ -1376,11 +1380,11 @@ class TemplateParamPackDecl final : public Node { template<typename Fn> 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 @@ -1445,13 +1449,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]); } }; @@ -1609,13 +1613,13 @@ struct ForwardTemplateReference : Node { if (Printing) return; ScopedOverride<bool> SavePrinting(Printing, true); - Ref->printLeft(OB); + OB.printLeft(*Ref); } void printRight(OutputBuffer &OB) const override { if (Printing) return; ScopedOverride<bool> SavePrinting(Printing, true); - Ref->printRight(OB); + OB.printRight(*Ref); } }; @@ -1767,7 +1771,7 @@ class DtorName : public Node { void printLeft(OutputBuffer &OB) const override { OB += "~"; - Base->printLeft(OB); + OB.printLeft(*Base); } }; @@ -2047,7 +2051,7 @@ class CastExpr : public Node { { ScopedOverride<unsigned> LT(OB.GtIsGt, 0); OB += "<"; - To->printLeft(OB); + OB.printLeft(*To); OB += ">"; } OB.printOpen(); @@ -6176,6 +6180,10 @@ struct ManglingParser : AbstractManglingParser<ManglingParser<Alloc>, 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 #if defined(__clang__) diff --git a/libcxxabi/src/demangle/Utility.h b/libcxxabi/src/demangle/Utility.h index f1fad35d60d98..a38bef762dd57 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,15 @@ class OutputBuffer { OutputBuffer(const OutputBuffer &) = delete; OutputBuffer &operator=(const OutputBuffer &) = delete; + virtual ~OutputBuffer() {} + operator std::string_view() const { return std::string_view(Buffer, CurrentPosition); } + virtual void printLeft(const Node &N); + virtual void printRight(const Node &N); + /// If a ParameterPackExpansion (or similar type) is encountered, the offset /// into the pack that we're currently printing. unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max(); diff --git a/lldb/include/lldb/Core/Demangle.h b/lldb/include/lldb/Core/Demangle.h new file mode 100644 index 0000000000000..27f0cacbdd48f --- /dev/null +++ b/lldb/include/lldb/Core/Demangle.h @@ -0,0 +1,90 @@ +//===-- Demangle.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_DEMANGLE_H +#define LLDB_CORE_DEMANGLE_H + +#include "llvm/Demangle/ItaniumDemangle.h" +#include "llvm/Demangle/Utility.h" + +#include <cstddef> +#include <utility> + +namespace lldb_private { + +struct TrackingOutputBuffer; + +// Stores information about parts of a demangled function name. +struct FunctionNameInfo { + /// 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<int>::someFunc<float>(int) const && + /// ^ ^ + /// start end + /// \endcode + std::pair<size_t, size_t> BasenameLocs; + + /// A [start, end) pair for the function scope qualifiers. + /// E.g., for + /// \code{.cpp} + /// void foo::bar<int>::qux<float>(int) const && + /// ^ ^ + /// start end + /// \endcode + std::pair<size_t, size_t> ScopeLocs; + + /// Indicates the [start, end) of the function argument lits. + /// E.g., + /// \code{.cpp} + /// int (*getFunc<float>(float, double))(int, int) + /// ^ ^ + /// start end + /// \endcode + std::pair<size_t, size_t> ArgumentLocs; + + bool startedPrintingArguments() const; + bool shouldTrack(TrackingOutputBuffer &OB) const; + bool canFinalize(TrackingOutputBuffer &OB) const; + void updateBasenameEnd(TrackingOutputBuffer &OB); + void updateScopeStart(TrackingOutputBuffer &OB); + void updateScopeEnd(TrackingOutputBuffer &OB); + void finalizeArgumentEnd(TrackingOutputBuffer &OB); + void finalizeStart(TrackingOutputBuffer &OB); + void finalizeEnd(TrackingOutputBuffer &OB); + bool hasBasename() const; +}; + +struct TrackingOutputBuffer : public llvm::itanium_demangle::OutputBuffer { + using OutputBuffer::OutputBuffer; + + FunctionNameInfo FunctionInfo; + unsigned FunctionPrintingDepth = 0; + + [[nodiscard]] llvm::itanium_demangle::ScopedOverride<unsigned> + enterFunctionTypePrinting(); + + bool isPrintingTopLevelFunctionType() const; + + 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); +}; +} // namespace lldb_private + +#endif // LLDB_CORE_DEMANGLE_H diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt index d5d8a9d5088fc..f4e4108c314e0 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 + Demangle.cpp Disassembler.cpp DumpDataExtractor.cpp DumpRegisterValue.cpp diff --git a/lldb/source/Core/Demangle.cpp b/lldb/source/Core/Demangle.cpp new file mode 100644 index 0000000000000..122e006409816 --- /dev/null +++ b/lldb/source/Core/Demangle.cpp @@ -0,0 +1,221 @@ +//===-- Demangle.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/Demangle.h" + +using namespace llvm::itanium_demangle; + +namespace lldb_private { + +bool FunctionNameInfo::startedPrintingArguments() const { + return ArgumentLocs.first > 0; +} + +bool FunctionNameInfo::shouldTrack(TrackingOutputBuffer &OB) const { + if (!OB.isPrintingTopLevelFunctionType()) + return false; + + if (OB.isGtInsideTemplateArgs()) + return false; + + if (startedPrintingArguments()) + return false; + + return true; +} + +bool FunctionNameInfo::canFinalize(TrackingOutputBuffer &OB) const { + if (!OB.isPrintingTopLevelFunctionType()) + return false; + + if (OB.isGtInsideTemplateArgs()) + return false; + + if (!startedPrintingArguments()) + return false; + + return true; +} + +void FunctionNameInfo::updateBasenameEnd(TrackingOutputBuffer &OB) { + if (!shouldTrack(OB)) + return; + + BasenameLocs.second = OB.getCurrentPosition(); +} + +void FunctionNameInfo::updateScopeStart(TrackingOutputBuffer &OB) { + if (!shouldTrack(OB)) + return; + + ScopeLocs.first = OB.getCurrentPosition(); +} + +void FunctionNameInfo::updateScopeEnd(TrackingOutputBuffer &OB) { + if (!shouldTrack(OB)) + return; + + ScopeLocs.second = OB.getCurrentPosition(); +} + +void FunctionNameInfo::finalizeArgumentEnd(TrackingOutputBuffer &OB) { + if (!canFinalize(OB)) + return; + + OB.FunctionInfo.ArgumentLocs.second = OB.getCurrentPosition(); +} + +void FunctionNameInfo::finalizeStart(TrackingOutputBuffer &OB) { + if (!shouldTrack(OB)) + return; + + OB.FunctionInfo.ArgumentLocs.first = OB.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 (BasenameLocs.second == 0) + OB.FunctionInfo.BasenameLocs.second = OB.getCurrentPosition(); + + DEMANGLE_ASSERT(!shouldTrack(OB), ""); + DEMANGLE_ASSERT(canFinalize(OB), ""); +} + +void FunctionNameInfo::finalizeEnd(TrackingOutputBuffer &OB) { + if (!canFinalize(OB)) + return; + + if (ScopeLocs.first > OB.FunctionInfo.ScopeLocs.second) + ScopeLocs.second = OB.FunctionInfo.ScopeLocs.first; + BasenameLocs.first = OB.FunctionInfo.ScopeLocs.second; +} + +bool FunctionNameInfo::hasBasename() const { + return BasenameLocs.first != BasenameLocs.second && BasenameLocs.second > 0; +} + +ScopedOverride<unsigned> 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<const FunctionType &>(N)); + break; + case Node::KFunctionEncoding: + printLeftImpl(static_cast<const FunctionEncoding &>(N)); + break; + case Node::KNestedName: + printLeftImpl(static_cast<const NestedName &>(N)); + break; + case Node::KNameWithTemplateArgs: + printLeftImpl(static_cast<const NameWithTemplateArgs &>(N)); + break; + default: + N.printLeft(*this); + } +} + +void TrackingOutputBuffer::printRight(const Node &N) { + switch (N.getKind()) { + case Node::KFunctionType: + printRightImpl(static_cast<const FunctionType &>(N)); + break; + case Node::KFunctionEncoding: + printRightImpl(static_cast<const FunctionEncoding &>(N)); + break; + default: + N.printRight(*this); + } +} + +void TrackingOutputBuffer::printLeftImpl(const FunctionType &N) { + auto Scoped = enterFunctionTypePrinting(); + N.printLeft(*this); +} + +void TrackingOutputBuffer::printRightImpl(const FunctionType &N) { + auto Scoped = enterFunctionTypePrinting(); + N.printRight(*this); +} + +void TrackingOutputBuffer::printLeftImpl(const FunctionEncoding &N) { + auto Scoped = enterFunctionTypePrinting(); + + const Node *Ret = N.getReturnType(); + if (Ret) { + printLeft(*Ret); + if (!Ret->hasRHSComponent(*this)) + *this += " "; + } + + FunctionInfo.updateScopeStart(*this); + + N.getName()->print(*this); +} + +void TrackingOutputBuffer::printRightImpl(const FunctionEncoding &N) { + auto Scoped = enterFunctionTypePrinting(); + FunctionInfo.finalizeStart(*this); + + printOpen(); + N.getParams().printWithComma(*this); + printClose(); + + FunctionInfo.finalizeArgumentEnd(*this); + + const Node *Ret = N.getReturnType(); + + if (Ret) + printRight(*Ret); + + 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); + } + + FunctionInfo.finalizeEnd(*this); +} + +void TrackingOutputBuffer::printLeftImpl(const NestedName &N) { + N.Qual->print(*this); + *this += "::"; + FunctionInfo.updateScopeEnd(*this); + N.Name->print(*this); + FunctionInfo.updateBasenameEnd(*this); +} + +void TrackingOutputBuffer::printLeftImpl(const NameWithTemplateArgs &N) { + N.Name->print(*this); + FunctionInfo.updateBasenameEnd(*this); + N.TemplateArgs->print(*this); +} + +} // namespace lldb_private diff --git a/lldb/unittests/Core/MangledTest.cpp b/lldb/unittests/Core/MangledTest.cpp index a3760ba43b3c9..2f4e1755a4074 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/Demangle.h" #include "lldb/Core/Mangled.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" @@ -319,3 +320,145 @@ TEST(MangledTest, NameIndexes_FindFunctionSymbols) { EXPECT_EQ(0, Count("undemangable", eFunctionNameTypeBase)); EXPECT_EQ(0, Count("undemangable", eFunctionNameTypeMethod)); } + +struct DemanglingPartsTestCase { + const char *mangled; + FunctionNameInfo expected_info; + std::string_view basename; + std::string_view scope; + bool valid_basename = true; +}; + +DemanglingPartsTestCase g_demangling_parts_test_cases[] = { + // clang-format off + { "_ZNVKO3BarIN2ns3QuxIiEEE1CIPFi3FooIS_IiES6_EEE6methodIS6_EENS5_IT_SC_E5InnerIiEESD_SD_", + { .BasenameLocs = {92, 98}, .ScopeLocs = {36, 92}, .ArgumentLocs = { 108, 158 } }, + .basename = "method", + .scope = "Bar<ns::Qux<int>>::C<int (*)(Foo<Bar<int>, Bar<int>>)>::" + }, + { "_Z7getFuncIfEPFiiiET_", + { .BasenameLocs = {6, 13}, .ScopeLocs = {6, 6}, .ArgumentLocs = { 20, 27 } }, + .basename = "getFunc", + .scope = "" + }, + { "_ZN1f1b1c1gEv", + { .BasenameLocs = {9, 10}, .ScopeLocs = {0, 9}, .ArgumentLocs = { 10, 12 } }, + .basename = "g", + .scope = "f::b::c::" + }, + { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bEEEcvT__EES2_", + { .BasenameLocs = {45, 48}, .ScopeLocs = {38, 45}, .ArgumentLocs = { 53, 58 } }, + .basename = "fD1", + .scope = "test7::" + }, + { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_", + { .BasenameLocs = {61, 64}, .ScopeLocs = {54, 61}, .ArgumentLocs = { 69, 79 } }, + .basename = "fD1", + .scope = "test7::" + }, + { "_ZN5test7INDT1cE1dINDT1cE1dEEEE3fD1INDT1cE1dINDT1cE1dEEEEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_", + { .BasenameLocs = {120, 123}, .ScopeLocs = {81, 120}, .ArgumentLocs = { 155, 168 } }, + .basename = "fD1", + .scope = "test7<decltype(c)::d<decltype(c)::d>>::" + }, + { "_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", + { .BasenameLocs = {687, 692}, .ScopeLocs = {343, 687}, .ArgumentLocs = { 713, 1174 } }, + .basename = "parse", + .scope = "nlohmann::json_abi_v3_11_3::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>, void>::" + }, + { "_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvEC1EDn", + { .BasenameLocs = {344, 354}, .ScopeLocs = {0, 344}, .ArgumentLocs = { 354, 370 } }, + .basename = "basic_json", + .scope = "nlohmann::json_abi_v3_11_3::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>, void>::" + }, + { "_Z3fppIiEPFPFvvEiEf", + { .BasenameLocs = {10, 13}, .ScopeLocs = {10, 10}, .ArgumentLocs = { 18, 25 } }, + .basename = "fpp", + .scope = "" + }, + { "_Z3fppIiEPFPFvvEN2ns3FooIiEEEf", + { .BasenameLocs = {10, 13}, .ScopeLocs = {10, 10}, .ArgumentLocs = { 18, 25 } }, + .basename = "fpp", + .scope = "" + }, + { "_Z3fppIiEPFPFvPFN2ns3FooIiEENS2_3BarIfE3QuxEEEPFS2_S2_EEf", + { .BasenameLocs = {10, 13}, .ScopeLocs = {10, 10}, .ArgumentLocs = { 18, 25 } }, + .basename = "fpp", + .scope = "" + }, + { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvEiEf", + { .BasenameLocs = {64, 67}, .ScopeLocs = {10, 64}, .ArgumentLocs = { 72, 79 } }, + .basename = "fpp", + .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::" + }, + { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvES2_Ef", + { .BasenameLocs = {64, 67}, .ScopeLocs = {10, 64}, .ArgumentLocs = { 72, 79 } }, + .basename = "fpp", + .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::" + }, + { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvPFS2_S5_EEPFS2_S2_EEf", + { .BasenameLocs = {64, 67}, .ScopeLocs = {10, 64}, .ArgumentLocs = { 72, 79 } }, + .basename = "fpp", + .scope = "ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::" + }, + { "_ZTV11ImageLoader", + { .BasenameLocs = {0, 0}, .ScopeLocs = {0, 0}, .ArgumentLocs = { 0, 0 } }, + .basename = "", + .scope = "", + .valid_basename = false + } + // clang-format on +}; + +struct DemanglingPartsTestFixture + : public ::testing::TestWithParam<DemanglingPartsTestCase> {}; + +namespace { +class TestAllocator { + llvm::BumpPtrAllocator Alloc; + +public: + void reset() { Alloc.Reset(); } + + template <typename T, typename... Args> T *makeNode(Args &&...args) { + return new (Alloc.Allocate(sizeof(T), alignof(T))) + T(std::forward<Args>(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, valid_basename] = GetParam(); + + llvm::itanium_demangle::ManglingParser<TestAllocator> 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.FunctionInfo.hasBasename(), valid_basename); + + EXPECT_EQ(OB.FunctionInfo.BasenameLocs, info.BasenameLocs); + EXPECT_EQ(OB.FunctionInfo.ScopeLocs, info.ScopeLocs); + EXPECT_EQ(OB.FunctionInfo.ArgumentLocs, info.ArgumentLocs); + + auto get_part = [&](const std::pair<size_t, size_t> &loc) { + return demangled.substr(loc.first, loc.second - loc.first); + }; + + EXPECT_EQ(get_part(OB.FunctionInfo.BasenameLocs), basename); + EXPECT_EQ(get_part(OB.FunctionInfo.ScopeLocs), scope); +} + +INSTANTIATE_TEST_SUITE_P(DemanglingPartsTests, DemanglingPartsTestFixture, + ::testing::ValuesIn(g_demangling_parts_test_cases)); diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h index b0363c1a7a786..aab45f351e070 100644 --- a/llvm/include/llvm/Demangle/ItaniumDemangle.h +++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h @@ -281,9 +281,9 @@ 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. @@ -458,11 +458,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 { @@ -491,7 +491,7 @@ class PostfixQualifiedType final : public Node { template<typename Fn> void match(Fn F) const { F(Ty, Postfix); } void printLeft(OutputBuffer &OB) const override { - Ty->printLeft(OB); + OB.printLeft(*Ty); OB += Postfix; } }; @@ -577,7 +577,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 += "]"; @@ -644,7 +644,7 @@ class PointerType final : public Node { // We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>. if (Pointee->getKind() != KObjCProtoName || !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) { - Pointee->printLeft(OB); + OB.printLeft(*Pointee); if (Pointee->hasArray(OB)) OB += " "; if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) @@ -663,7 +663,7 @@ class PointerType final : public Node { !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) { if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) OB += ")"; - Pointee->printRight(OB); + OB.printRight(*Pointee); } } }; @@ -729,7 +729,7 @@ class ReferenceType : public Node { std::pair<ReferenceKind, const Node *> 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)) @@ -746,7 +746,7 @@ class ReferenceType : public Node { return; if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) OB += ")"; - Collapsed.second->printRight(OB); + OB.printRight(*Collapsed.second); } }; @@ -766,7 +766,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 @@ -778,7 +778,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); } }; @@ -798,7 +798,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() != ']') @@ -807,7 +807,7 @@ class ArrayType final : public Node { if (Dimension) Dimension->print(OB); OB += "]"; - Base->printRight(OB); + OB.printRight(*Base); } bool printInitListAsType(OutputBuffer &OB, @@ -851,7 +851,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 += " "; } @@ -859,7 +859,7 @@ class FunctionType final : public Node { OB.printOpen(); Params.printWithComma(OB); OB.printClose(); - Ret->printRight(OB); + OB.printRight(*Ret); if (CVQuals & QualConst) OB += " const"; @@ -964,6 +964,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; } @@ -972,10 +974,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); } @@ -983,8 +986,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"; @@ -1324,14 +1328,14 @@ class NonTypeTemplateParamDecl final : public Node { template<typename Fn> 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); } }; @@ -1376,11 +1380,11 @@ class TemplateParamPackDecl final : public Node { template<typename Fn> 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 @@ -1445,13 +1449,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]); } }; @@ -1609,13 +1613,13 @@ struct ForwardTemplateReference : Node { if (Printing) return; ScopedOverride<bool> SavePrinting(Printing, true); - Ref->printLeft(OB); + OB.printLeft(*Ref); } void printRight(OutputBuffer &OB) const override { if (Printing) return; ScopedOverride<bool> SavePrinting(Printing, true); - Ref->printRight(OB); + OB.printRight(*Ref); } }; @@ -1767,7 +1771,7 @@ class DtorName : public Node { void printLeft(OutputBuffer &OB) const override { OB += "~"; - Base->printLeft(OB); + OB.printLeft(*Base); } }; @@ -2047,7 +2051,7 @@ class CastExpr : public Node { { ScopedOverride<unsigned> LT(OB.GtIsGt, 0); OB += "<"; - To->printLeft(OB); + OB.printLeft(*To); OB += ">"; } OB.printOpen(); @@ -6176,6 +6180,10 @@ struct ManglingParser : AbstractManglingParser<ManglingParser<Alloc>, 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 #if defined(__clang__) diff --git a/llvm/include/llvm/Demangle/Utility.h b/llvm/include/llvm/Demangle/Utility.h index e893cceea2cdc..6a1bf6d0e6b63 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,15 @@ class OutputBuffer { OutputBuffer(const OutputBuffer &) = delete; OutputBuffer &operator=(const OutputBuffer &) = delete; + virtual ~OutputBuffer() {} + operator std::string_view() const { return std::string_view(Buffer, CurrentPosition); } + virtual void printLeft(const Node &N); + virtual void printRight(const Node &N); + /// If a ParameterPackExpansion (or similar type) is encountered, the offset /// into the pack that we're currently printing. unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max(); >From cde3bafd97810f13a02f7f4b652a8694bde57ab9 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Mon, 10 Mar 2025 15:39:10 +0000 Subject: [PATCH 2/4] [lldb][Mangled] Add API to force re-demangling a Mangled object --- lldb/include/lldb/Core/Mangled.h | 8 +++++++- lldb/source/Core/Mangled.cpp | 11 ++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h index 9ca28917ccffa..12a69f25b2187 100644 --- a/lldb/include/lldb/Core/Mangled.h +++ b/lldb/include/lldb/Core/Mangled.h @@ -276,7 +276,13 @@ class Mangled { void Encode(DataEncoder &encoder, ConstStringTable &strtab) const; private: - ///< The mangled version of the name. + /// 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; + + /// The mangled version of the name. ConstString m_mangled; ///< Mutable so we can get it on demand with diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index ddaaedea04183..db5aff40d34d3 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -265,19 +265,24 @@ bool Mangled::GetRichManglingInfo(RichManglingContext &context, llvm_unreachable("Fully covered switch above!"); } +ConstString Mangled::GetDemangledName() const { + return GetDemangledNameImpl(/*force=*/false); +} + // 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() const { +ConstString Mangled::GetDemangledNameImpl(bool force) const { if (!m_mangled) return m_demangled; // Re-use previously demangled names. - if (!m_demangled.IsNull()) + if (!force && !m_demangled.IsNull()) return m_demangled; - if (m_mangled.GetMangledCounterpart(m_demangled) && !m_demangled.IsNull()) + 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 >From 981cf4c78c472ab4c5645dff75759b0a46cb3e27 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Tue, 18 Mar 2025 14:02:17 +0000 Subject: [PATCH 3/4] [lldb][Mangled] Retrieve and cache demangled name info --- lldb/include/lldb/Core/Mangled.h | 11 +++++++++- lldb/include/lldb/Symbol/Function.h | 3 +++ lldb/source/Core/Mangled.cpp | 30 +++++++++++++++++++++++---- lldb/source/Symbol/Function.cpp | 4 ++++ llvm/include/llvm/Demangle/Demangle.h | 1 + llvm/lib/Demangle/ItaniumDemangle.cpp | 15 ++++++++++++++ 6 files changed, 59 insertions(+), 5 deletions(-) diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h index 12a69f25b2187..7332cc7219ce5 100644 --- a/lldb/include/lldb/Core/Mangled.h +++ b/lldb/include/lldb/Core/Mangled.h @@ -9,11 +9,13 @@ #ifndef LLDB_CORE_MANGLED_H #define LLDB_CORE_MANGLED_H +#include "lldb/Core/Demangle.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 "llvm/Demangle/Utility.h" #include <cstddef> #include <memory> @@ -275,6 +277,8 @@ class Mangled { /// table offsets in the cache data. void Encode(DataEncoder &encoder, ConstStringTable &strtab) const; + const std::optional<FunctionNameInfo> &GetDemangledInfo() const; + private: /// 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 @@ -288,6 +292,11 @@ class Mangled { ///< Mutable so we can get it on demand with ///< a const version of this object. mutable ConstString m_demangled; + + /// If available, holds details information about where + /// in \c m_demangled parts of the name begin end (e.g., + /// basename, arguments, etc.). + mutable std::optional<FunctionNameInfo> m_demangled_info = std::nullopt; }; Stream &operator<<(Stream &s, const Mangled &obj); diff --git a/lldb/include/lldb/Symbol/Function.h b/lldb/include/lldb/Symbol/Function.h index ee3a8304fc5b3..a4177a71d992d 100644 --- a/lldb/include/lldb/Symbol/Function.h +++ b/lldb/include/lldb/Symbol/Function.h @@ -11,6 +11,7 @@ #include "lldb/Core/AddressRange.h" #include "lldb/Core/Declaration.h" +#include "lldb/Core/Demangle.h" #include "lldb/Core/Mangled.h" #include "lldb/Expression/DWARFExpressionList.h" #include "lldb/Symbol/Block.h" @@ -534,6 +535,8 @@ class Function : public UserID, public SymbolContextScope { ConstString GetDisplayName() const; + const std::optional<FunctionNameInfo> &GetDemangledInfo() const; + const Mangled &GetMangled() const { return m_mangled; } /// Get the DeclContext for this function, if available. diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index db5aff40d34d3..e97ad6d81c8c0 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/Demangle.h" #include "lldb/Core/RichManglingContext.h" #include "lldb/Target/Language.h" #include "lldb/Utility/ConstString.h" @@ -22,6 +23,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/Demangle/Utility.h" #include "llvm/Support/Compiler.h" #include <mutex> @@ -152,16 +154,21 @@ static char *GetMSVCDemangledStr(llvm::StringRef M) { return demangled_cstr; } -static char *GetItaniumDemangledStr(const char *M) { +static std::pair<char *, FunctionNameInfo> +GetItaniumDemangledStr(const char *M) { char *demangled_cstr = nullptr; + FunctionNameInfo info; llvm::ItaniumPartialDemangler ipd; bool err = ipd.partialDemangle(M); if (!err) { // Default buffer and size (will realloc in case it's too small). size_t demangled_size = 80; demangled_cstr = static_cast<char *>(std::malloc(demangled_size)); - demangled_cstr = ipd.finishDemangle(demangled_cstr, &demangled_size); + + TrackingOutputBuffer OB(demangled_cstr, demangled_size); + demangled_cstr = ipd.finishDemangle(&OB, &demangled_size); + info = std::move(OB.FunctionInfo); assert(demangled_cstr && "finishDemangle must always succeed if partialDemangle did"); @@ -174,9 +181,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) { @@ -269,6 +281,13 @@ ConstString Mangled::GetDemangledName() const { return GetDemangledNameImpl(/*force=*/false); } +std::optional<FunctionNameInfo> 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 @@ -293,7 +312,10 @@ ConstString Mangled::GetDemangledNameImpl(bool force) const { demangled_name = GetMSVCDemangledStr(m_mangled); break; case eManglingSchemeItanium: { - demangled_name = GetItaniumDemangledStr(m_mangled.GetCString()); + std::pair<char *, FunctionNameInfo> demangled = + GetItaniumDemangledStr(m_mangled.GetCString()); + demangled_name = demangled.first; + m_demangled_info = std::move(demangled.second); break; } case eManglingSchemeRustV0: diff --git a/lldb/source/Symbol/Function.cpp b/lldb/source/Symbol/Function.cpp index c80f37ae68d9d..58a2b1d8080cf 100644 --- a/lldb/source/Symbol/Function.cpp +++ b/lldb/source/Symbol/Function.cpp @@ -729,3 +729,7 @@ ConstString Function::GetName() const { ConstString Function::GetNameNoArguments() const { return m_mangled.GetName(Mangled::ePreferDemangledWithoutArguments); } + +const std::optional<FunctionNameInfo> &Function::GetDemangledInfo() const { + return m_mangled.GetDemangledInfo(); +} diff --git a/llvm/include/llvm/Demangle/Demangle.h b/llvm/include/llvm/Demangle/Demangle.h index 132e5088b5514..888ad60af34bf 100644 --- a/llvm/include/llvm/Demangle/Demangle.h +++ b/llvm/include/llvm/Demangle/Demangle.h @@ -92,6 +92,7 @@ struct ItaniumPartialDemangler { /// Just print the entire mangled name into Buf. Buf and N behave like the /// second and third parameters to __cxa_demangle. char *finishDemangle(char *Buf, size_t *N) const; + char *finishDemangle(void *OB, size_t *N) const; /// Get the base name of a function. This doesn't include trailing template /// arguments, ie for "a::b<int>" this function returns "b". diff --git a/llvm/lib/Demangle/ItaniumDemangle.cpp b/llvm/lib/Demangle/ItaniumDemangle.cpp index 5c21b06a1d095..544ab67faad45 100644 --- a/llvm/lib/Demangle/ItaniumDemangle.cpp +++ b/llvm/lib/Demangle/ItaniumDemangle.cpp @@ -421,6 +421,14 @@ static char *printNode(const Node *RootNode, char *Buf, size_t *N) { return OB.getBuffer(); } +static char *printNode(const Node *RootNode, OutputBuffer &OB, size_t *N) { + RootNode->print(OB); + OB += '\0'; + if (N != nullptr) + *N = OB.getCurrentPosition(); + return OB.getBuffer(); +} + char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const { if (!isFunction()) return nullptr; @@ -540,6 +548,13 @@ char *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const { return printNode(static_cast<Node *>(RootNode), Buf, N); } +char *ItaniumPartialDemangler::finishDemangle(void *OB, size_t *N) const { + assert(RootNode != nullptr && "must call partialDemangle()"); + assert(OB != nullptr && "valid OutputBuffer argument required"); + return printNode(static_cast<Node *>(RootNode), + *static_cast<OutputBuffer *>(OB), N); +} + bool ItaniumPartialDemangler::hasFunctionQualifiers() const { assert(RootNode != nullptr && "must call partialDemangle()"); if (!isFunction()) >From 57e65ac9f287909cf4cd86c3055505b99a41fbd7 Mon Sep 17 00:00:00 2001 From: Michael Buch <michaelbuc...@gmail.com> Date: Tue, 18 Mar 2025 14:02:51 +0000 Subject: [PATCH 4/4] [lldb][Format] Add new function basename highlight option to FormatEntity --- lldb/include/lldb/Core/FormatEntity.h | 16 +++++ lldb/include/lldb/Target/Language.h | 9 +-- lldb/source/Core/FormatEntity.cpp | 67 ++++++++++++++++++- .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 53 ++++++++++++--- .../Language/CPlusPlus/CPlusPlusLanguage.h | 9 +-- lldb/source/Target/Language.cpp | 8 +-- .../Settings/TestFrameFormatHighlight.test | 55 +++++++++++++++ 7 files changed, 193 insertions(+), 24 deletions(-) create mode 100644 lldb/test/Shell/Settings/TestFrameFormatHighlight.test diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h index c9d5af1f31673..1f94da8a36aa1 100644 --- a/lldb/include/lldb/Core/FormatEntity.h +++ b/lldb/include/lldb/Core/FormatEntity.h @@ -197,6 +197,21 @@ struct Entry { return true; } + struct HighlightSettings { + std::string prefix; + std::string suffix; + + enum class Kind : uint8_t { + ///< Don't highlight. + None, + + ///< Highlight function basename + ///< (i.e., name without Scope and + ///< without template arguments). + Basename, + } kind = Kind::None; + }; + std::string string; std::string printf_format; std::vector<Entry> children; @@ -204,6 +219,7 @@ struct Entry { lldb::Format fmt = lldb::eFormatDefault; lldb::addr_t number = 0; bool deref = false; + HighlightSettings highlight; }; bool Format(const Entry &entry, Stream &s, const SymbolContext *sc, diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h index b699a90aff8e4..088aa1a964ec7 100644 --- a/lldb/include/lldb/Target/Language.h +++ b/lldb/include/lldb/Target/Language.h @@ -268,10 +268,11 @@ class Language : public PluginInterface { // the reference has never been assigned virtual bool IsUninitializedReference(ValueObject &valobj); - virtual bool GetFunctionDisplayName(const SymbolContext *sc, - const ExecutionContext *exe_ctx, - FunctionNameRepresentation representation, - Stream &s); + virtual bool + GetFunctionDisplayName(const SymbolContext *sc, + const ExecutionContext *exe_ctx, + FunctionNameRepresentation representation, Stream &s, + const FormatEntity::Entry::HighlightSettings &); virtual ConstString GetDemangledFunctionNameWithoutArguments(Mangled mangled) const { diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 7fe22994d7f7e..17311cecf01ec 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -1654,7 +1654,8 @@ 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, + entry.highlight); if (language_plugin_handled) { s << ss.GetString(); @@ -1690,7 +1691,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (language_plugin) language_plugin_handled = language_plugin->GetFunctionDisplayName( sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs, - ss); + ss, entry.highlight); if (language_plugin_handled) { s << ss.GetString(); @@ -1724,7 +1725,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (language_plugin) language_plugin_handled = language_plugin->GetFunctionDisplayName( - sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss); + sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss, + entry.highlight); if (language_plugin_handled) { s << ss.GetString(); @@ -2046,6 +2048,54 @@ static const Definition *FindEntry(const llvm::StringRef &format_str, return parent; } +static llvm::Expected<Entry::HighlightSettings> +ParseHighlightSettings(const Entry &entry) { + // FIXME: support other function.name-XXX types as well + if (entry.type != Entry::Type::FunctionNameWithArgs) + return llvm::createStringError( + "The 'highlight_basename' format can only be used on " + "${function.name-with-args}"); + + llvm::StringRef format = entry.printf_format; + if (!format.consume_front("highlight_")) + return llvm::createStringError( + "Expected 'highlight_' prefix not found in: %s.", + entry.printf_format.c_str()); + + Entry::HighlightSettings settings; + if (format.consume_front("basename")) { + settings.kind = Entry::HighlightSettings::Kind::Basename; + } else { + return llvm::createStringError( + "Unsupported highlight kind detected in: %s. " + "Currently supported: basename", + entry.printf_format.c_str()); + } + + llvm::SmallVector<llvm::StringRef, 1> matches; + // TODO: support ${ansi.XXX} syntax. ExtractVariableInfo needs + // to be adjusted to support nested '{}'. + llvm::Regex color_pattern{R"(^\(([a-z\.]+)\)$)"}; + if (!color_pattern.match(format, &matches)) + return llvm::createStringError("Couldn't find valid color variable in: %s. " + "Expected format: (ansi.some-color)", + entry.printf_format.c_str()); + + assert(matches.size() == 2); + + std::string color_format = ("${" + matches[1] + "}").str(); + std::string terminal_code = ansi::FormatAnsiTerminalCodes(color_format); + if (terminal_code.empty()) + return llvm::createStringError("Invalid color variable '%s' found in: %s", + color_format.c_str(), + entry.printf_format.c_str()); + + settings.prefix = std::move(terminal_code); + settings.suffix = ansi::FormatAnsiTerminalCodes("${ansi.normal}"); + + return settings; +} + static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry, uint32_t depth) { Status error; @@ -2201,6 +2251,7 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry, if (error.Fail()) return error; bool verify_is_thread_id = false; + bool parse_highlight_settings = false; Entry entry; if (!variable_format.empty()) { entry.printf_format = variable_format.str(); @@ -2266,6 +2317,8 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry, clear_printf = true; } else if (entry.printf_format == "tid") { verify_is_thread_id = true; + } else if (entry.printf_format.find("highlight_") == 0) { + parse_highlight_settings = true; } else { error = Status::FromErrorStringWithFormat( "invalid format: '%s'", entry.printf_format.c_str()); @@ -2307,6 +2360,14 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry, "the 'tid' format can only be used on " "${thread.id} and ${thread.protocol_id}"); } + } else if (parse_highlight_settings) { + auto highlight_or_err = ParseHighlightSettings(entry); + if (highlight_or_err) { + entry.highlight = std::move(*highlight_or_err); + entry.printf_format.clear(); + } else { + error = Status::FromError(highlight_or_err.takeError()); + } } switch (entry.type) { diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 4b045d12ad494..10247389df530 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Demangle/ItaniumDemangle.h" +#include "lldb/Core/Demangle.h" #include "lldb/Core/Mangled.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" @@ -178,7 +179,7 @@ static bool IsTrivialBasename(const llvm::StringRef &basename) { /// but replaces each argument type with the variable name /// and the corresponding pretty-printed value static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream, - char const *full_name, + llvm::StringRef full_name, ExecutionContextScope *exe_scope, VariableList const &args) { CPlusPlusLanguage::MethodName cpp_method{ConstString(full_name)}; @@ -208,6 +209,42 @@ static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream, return true; } +static bool PrettyPrintFunctionNameWithArgs( + Stream &out_stream, llvm::StringRef full_name, + ExecutionContextScope *exe_scope, VariableList const &args, + const std::optional<FunctionNameInfo> &demangled_info, + const FormatEntity::Entry::HighlightSettings &settings) { + if (settings.kind == FormatEntity::Entry::HighlightSettings::Kind::None || + !demangled_info || !demangled_info->hasBasename()) + return PrettyPrintFunctionNameWithArgs(out_stream, full_name, exe_scope, + args); + + auto [base_start, base_end] = demangled_info->BasenameLocs; + + // Dump anything before the basename. + out_stream.PutCString(full_name.substr(0, base_start)); + + // Highlight the basename. + out_stream.PutCString(settings.prefix); + out_stream.PutCString(full_name.substr(base_start, base_end - base_start)); + out_stream.PutCString(settings.suffix); + + // Dump anything between the basename and the argument list. + if (demangled_info->ArgumentLocs.first > base_end) + out_stream.PutCString(full_name.substr( + base_end, demangled_info->ArgumentLocs.first - base_end)); + + // Dump arguments. + out_stream.PutChar('('); + FormatEntity::PrettyPrintFunctionArguments(out_stream, args, exe_scope); + out_stream.PutChar(')'); + + // Dump anything after the argument list. + out_stream.PutCString(full_name.substr(demangled_info->ArgumentLocs.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,7 +1736,8 @@ bool CPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const { bool CPlusPlusLanguage::GetFunctionDisplayName( const SymbolContext *sc, const ExecutionContext *exe_ctx, - FunctionNameRepresentation representation, Stream &s) { + FunctionNameRepresentation representation, Stream &s, + const FormatEntity::Entry::HighlightSettings &settings) { switch (representation) { case FunctionNameRepresentation::eNameWithArgs: { // Print the function name with arguments in it @@ -1737,13 +1775,10 @@ bool CPlusPlusLanguage::GetFunctionDisplayName( 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; + + return PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args, + sc->function->GetDemangledInfo(), + settings); } } else if (sc->symbol) { const char *cstr = sc->symbol->GetName().AsCString(nullptr); diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h index 623d481bf117f..593432ba19dc2 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -15,6 +15,7 @@ #include "llvm/ADT/StringRef.h" #include "Plugins/Language/ClangCommon/ClangHighlighter.h" +#include "lldb/Core/FormatEntity.h" #include "lldb/Target/Language.h" #include "lldb/Utility/ConstString.h" #include "lldb/lldb-private.h" @@ -138,10 +139,10 @@ class CPlusPlusLanguage : public Language { ConstString GetDemangledFunctionNameWithoutArguments(Mangled mangled) const override; - bool GetFunctionDisplayName(const SymbolContext *sc, - const ExecutionContext *exe_ctx, - FunctionNameRepresentation representation, - Stream &s) override; + bool GetFunctionDisplayName( + const SymbolContext *sc, const ExecutionContext *exe_ctx, + FunctionNameRepresentation representation, Stream &s, + const FormatEntity::Entry::HighlightSettings &) override; static bool IsCPPMangledName(llvm::StringRef name); diff --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp index a75894ffa4b3b..e01165ee875f5 100644 --- a/lldb/source/Target/Language.cpp +++ b/lldb/source/Target/Language.cpp @@ -510,10 +510,10 @@ bool Language::IsNilReference(ValueObject &valobj) { return false; } bool Language::IsUninitializedReference(ValueObject &valobj) { return false; } -bool Language::GetFunctionDisplayName(const SymbolContext *sc, - const ExecutionContext *exe_ctx, - FunctionNameRepresentation representation, - Stream &s) { +bool Language::GetFunctionDisplayName( + const SymbolContext *sc, const ExecutionContext *exe_ctx, + FunctionNameRepresentation representation, Stream &s, + const FormatEntity::Entry::HighlightSettings &) { return false; } diff --git a/lldb/test/Shell/Settings/TestFrameFormatHighlight.test b/lldb/test/Shell/Settings/TestFrameFormatHighlight.test new file mode 100644 index 0000000000000..1ca9a7ed9fc81 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatHighlight.test @@ -0,0 +1,55 @@ +# UNSUPPORTED: system-windows +# Test highlighting of function basenames. + +# RUN: split-file %s %t +# RUN: %build %t/main.cpp -o %t.out +# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 | FileCheck %s --check-prefix=CHECK-COLOR + +#--- main.cpp +namespace ns { +template<typename T> +struct Bar { + template<typename K> + T bar(K k) const & { return 1.0f; } +}; + +template<typename T> +struct Foo { + template<typename K> + void foo() const volatile && { + Bar<float> b; + b.bar(b); + } +}; + +template<typename T> +T func() { + ns::Foo<int>{}.foo<int>(); + return T{}; +} +} // namespace ns + +int main() { + ns::func<ns::Foo<int>>(); + return 0; +} + +#--- commands.input +settings set use-color true +settings set -f frame-format "frame ${function.name-with-args:%highlight_basename(ansi.fg.cyan)}\n" +break set -n bar + +run +bt +# CHECK-COLOR: frame float ns::Bar<float>::[36mbar[0m<ns::Bar<float>>(this={{.*}}, k=Bar<float> @ {{.*}}) const & +# CHECK-COLOR: frame void ns::Foo<int>::[36mfoo[0m<int>(this={{.*}}) const volatile && +# CHECK-COLOR: frame ns::Foo<int> ns::[36mfunc[0m<ns::Foo<int>>() +# CHECK-COLOR: frame main + +settings set -f frame-format "frame ${function.name-with-args:%highlight_basename(ansi.bg.green)}\n" +bt + +# CHECK-COLOR: frame float ns::Bar<float>::[42mbar[0m<ns::Bar<float>>(this={{.*}}, k=Bar<float> @ {{.*}}) const & +# CHECK-COLOR: frame void ns::Foo<int>::[42mfoo[0m<int>(this={{.*}}) const volatile && +# CHECK-COLOR: frame ns::Foo<int> ns::[42mfunc[0m<ns::Foo<int>>() +# CHECK-COLOR: frame main _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits