Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion lldb/docs/use/formatting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ A complete list of currently supported format string variables is listed below:
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``function.basename`` | The basename of the current function depending on the frame's language. E.g., for C++ the basename for ``void ns::foo<float>::bar<int>(int) const`` is ``bar``. |
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``function.scope`` | The scope qualifiers of the current function depending on the frame's language. E.g., for C++ the scope for ``void ns::foo<float>::bar<int>(int) const`` is ``ns::foo<float>``. |
| ``function.prefix`` | Any prefix added to the demangled function name of the current function. This depends on the frame's language. E.g., for C++ the suffix will always be empty. |
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``function.scope`` | The scope qualifiers of the current function depending on the frame's language. E.g., for C++ the scope for ``void ns::foo<float>::bar<int>(int) const`` is ``ns::foo<float>``. |
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``function.template-arguments`` | The template arguments of the current function depending on the frame's language. E.g., for C++ the template arguments for ``void ns::foo<float>::bar<int>(int) const`` are ``<float>``. |
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Expand Down Expand Up @@ -325,6 +327,7 @@ _____________________
The function names displayed in backtraces/``frame info``/``thread info`` are the demangled names of functions. On some platforms (like ones using Itanium the mangling scheme), LLDB supports decomposing these names into fine-grained components. These are currently:

- ``${function.return-left}``
- ``${function.prefix}``
- ``${function.scope}``
- ``${function.basename}``
- ``${function.template-arguments}``
Expand Down
12 changes: 11 additions & 1 deletion lldb/include/lldb/Core/DemangledNameInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ struct DemangledNameInfo {
/// \endcode
std::pair<size_t, size_t> ScopeRange;

/// Indicates the [start, end) of the function argument lits.
/// Indicates the [start, end) of the function argument list.
/// E.g.,
/// \code{.cpp}
/// int (*getFunc<float>(float, double))(int, int)
Expand All @@ -59,6 +59,16 @@ struct DemangledNameInfo {
/// \endcode
std::pair<size_t, size_t> QualifiersRange;

/// Indicates the [start, end) of the function's prefix. This is a
/// catch-all range for anything that is not tracked by the rest of
/// the pairs.
std::pair<size_t, size_t> PrefixRange;

/// Indicates the [start, end) of the function's suffix. This is a
/// catch-all range for anything that is not tracked by the rest of
/// the pairs.
std::pair<size_t, size_t> SuffixRange;

/// Returns \c true if this object holds a valid basename range.
bool hasBasename() const {
return BasenameRange.second > BasenameRange.first &&
Expand Down
1 change: 1 addition & 0 deletions lldb/include/lldb/Core/FormatEntity.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ struct Entry {
FunctionNameWithArgs,
FunctionNameNoArgs,
FunctionMangledName,
FunctionPrefix,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we add a new variable here we will need to update the documentation under lldb/docs/use/formatting.rst

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks 👍

FunctionScope,
FunctionBasename,
FunctionTemplateArguments,
Expand Down
3 changes: 3 additions & 0 deletions lldb/source/Core/FormatEntity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ constexpr Definition g_function_child_entries[] = {
Definition("initial-function", EntryType::FunctionInitial),
Definition("changed", EntryType::FunctionChanged),
Definition("is-optimized", EntryType::FunctionIsOptimized),
Definition("prefix", EntryType::FunctionPrefix),
Definition("scope", EntryType::FunctionScope),
Definition("basename", EntryType::FunctionBasename),
Definition("template-arguments", EntryType::FunctionTemplateArguments),
Expand Down Expand Up @@ -385,6 +386,7 @@ const char *FormatEntity::Entry::TypeToCString(Type t) {
ENUM_TO_CSTR(FunctionNameWithArgs);
ENUM_TO_CSTR(FunctionNameNoArgs);
ENUM_TO_CSTR(FunctionMangledName);
ENUM_TO_CSTR(FunctionPrefix);
ENUM_TO_CSTR(FunctionScope);
ENUM_TO_CSTR(FunctionBasename);
ENUM_TO_CSTR(FunctionTemplateArguments);
Expand Down Expand Up @@ -1835,6 +1837,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
return true;
}

case Entry::Type::FunctionPrefix:
case Entry::Type::FunctionScope:
case Entry::Type::FunctionBasename:
case Entry::Type::FunctionTemplateArguments:
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Core/Mangled.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ GetItaniumDemangledStr(const char *M) {

TrackingOutputBuffer OB(demangled_cstr, demangled_size);
demangled_cstr = ipd.finishDemangle(&OB);
OB.NameInfo.SuffixRange.first = OB.NameInfo.QualifiersRange.second;
OB.NameInfo.SuffixRange.second = std::string(demangled_cstr).length();
info = std::move(OB.NameInfo);

assert(demangled_cstr &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,8 +401,8 @@ GetDemangledFunctionSuffix(const SymbolContext &sc) {
if (!info->hasBasename())
return std::nullopt;

return demangled_name.slice(info->QualifiersRange.second,
llvm::StringRef::npos);
return demangled_name.slice(info->SuffixRange.first,
info->SuffixRange.second);
}

static bool PrintDemangledArgumentList(Stream &s, const SymbolContext &sc) {
Expand Down
291 changes: 0 additions & 291 deletions lldb/unittests/Core/MangledTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#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"
Expand Down Expand Up @@ -320,293 +319,3 @@ TEST(MangledTest, NameIndexes_FindFunctionSymbols) {
EXPECT_EQ(0, Count("undemangable", eFunctionNameTypeBase));
EXPECT_EQ(0, Count("undemangable", eFunctionNameTypeMethod));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did something go wrong with renaming the test back?


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<ns::Qux<int>>::C<int (*)(Foo<Bar<int>, Bar<int>>)>::",
/*.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<decltype(c)::d<decltype(c)::d>>::",
/*.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::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>::",
/*.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::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>::",
/*.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<ns::Foo<ns::Foo<int>::Bar<float>::Qux>>::",
/*.qualifiers=*/""
},
{ "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvES2_Ef",
{ /*.BasenameRange=*/{64, 67}, /*.ScopeRange=*/{10, 64}, /*.ArgumentsRange=*/{ 72, 79 },
/*.QualifiersRange=*/{97, 97} },
/*.basename=*/"fpp",
/*.scope=*/"ns::HasFuncs<ns::Foo<ns::Foo<int>::Bar<float>::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<ns::Foo<ns::Foo<int>::Bar<float>::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
},
{ "___ZNK5dyld313MachOAnalyzer18forEachInitializerER11DiagnosticsRKNS0_15VMAddrConverterEU13block_pointerFvjEPKv_block_invoke.204",
{ /*.BasenameRange=*/{55, 73}, /*.ScopeRange=*/{33, 55}, /*.ArgumentsRange=*/{ 73, 181 },
/*.QualifiersRange=*/{181, 187} },
/*.basename=*/"forEachInitializer",
/*.scope=*/"dyld3::MachOAnalyzer::",
/*.qualifiers=*/" const",
},
{ "_ZZN5dyld45startEPNS_10KernelArgsEPvS2_ENK3$_1clEv",
{ /*.BasenameRange=*/{53, 63}, /*.ScopeRange=*/{0, 53}, /*.ArgumentsRange=*/{ 63, 65 },
/*.QualifiersRange=*/{65, 71} },
/*.basename=*/"operator()",
/*.scope=*/"dyld4::start(dyld4::KernelArgs*, void*, void*)::$_1::",
/*.qualifiers=*/" const",
},
{ "_ZZNK5dyld46Loader38runInitializersBottomUpPlusUpwardLinksERNS_12RuntimeStateEENK3$_0clEv",
{ /*.BasenameRange=*/{88, 98}, /*.ScopeRange=*/{0, 88}, /*.ArgumentsRange=*/{ 98, 100 },
/*.QualifiersRange=*/{100, 106} },
/*.basename=*/"operator()",
/*.scope=*/"dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const::$_0::",
/*.qualifiers=*/" const",
},
{ "_ZZNK5dyld46Loader38runInitializersBottomUpPlusUpwardLinksERNS_12RuntimeStateEENK3$_0clEv.cold",
{ /*.BasenameRange=*/{88, 98}, /*.ScopeRange=*/{0, 88}, /*.ArgumentsRange=*/{ 98, 100 },
/*.QualifiersRange=*/{100, 106} },
/*.basename=*/"operator()",
/*.scope=*/"dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const::$_0::",
/*.qualifiers=*/" const",
}
// 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, qualifiers, 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.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<size_t, size_t> &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);
std::free(OB.getBuffer());
}

INSTANTIATE_TEST_SUITE_P(DemanglingPartsTests, DemanglingPartsTestFixture,
::testing::ValuesIn(g_demangling_parts_test_cases));
Loading