Skip to content

[lldb] add TemplateRange and NameQualifiersRange to DemangledNameInfo #150999

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions lldb/docs/use/formatting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ A complete list of currently supported format string variables is listed below:
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``function.name-without-args`` | The name of the current function without arguments and values (used to include a function name in-line in the ``disassembly-format``) |
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``function.name-qualifiers`` | Any qualifiers added after the name of a function and before its arguments or template arguments. E.g., for Swift the name qualifier for ``closure #1 in A.foo<Int>()`` is `` in A.foo``. |
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``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.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 prefix will always be empty. |
Expand Down Expand Up @@ -332,6 +334,7 @@ The function names displayed in backtraces/``frame info``/``thread info`` are th
- ``${function.prefix}``
- ``${function.scope}``
- ``${function.basename}``
- ``${function.name-qualifiers}``
- ``${function.template-arguments}``
- ``${function.formatted-arguments}``
- ``${function.qualifiers}``
Expand Down
41 changes: 39 additions & 2 deletions lldb/include/lldb/Core/DemangledNameInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,29 @@ namespace lldb_private {
struct DemangledNameInfo {
/// A [start, end) pair for the function basename.
/// The basename is the name without scope qualifiers
/// and without template parameters. E.g.,
/// 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> BasenameRange;

/// A [start, end) pair for the function template arguments.
///
/// E.g.,
/// \code{.cpp}
/// void foo::bar<int>::someFunc<float>(int) const &&
/// ^ ^
/// start end
/// \endcode
std::pair<size_t, size_t> TemplateArgumentsRange;

/// A [start, end) pair for the function scope qualifiers.
/// E.g., for
///
/// E.g.,
/// \code{.cpp}
/// void foo::bar<int>::qux<float>(int) const &&
/// ^ ^
Expand All @@ -40,6 +53,7 @@ struct DemangledNameInfo {
std::pair<size_t, size_t> ScopeRange;

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

/// Indicates the [start, end) of the function's name qualifiers. This is a
/// catch-all range for anything in between the basename and the function's
/// arguments or template arguments, that is not tracked by the rest of the
/// pairs.
///
/// E.g.,
/// \code{.swift}
/// closure #1 in A.foo<Int>()
/// ^ ^
/// start end
/// \endcode
std::pair<size_t, size_t> NameQualifiersRange;

/// 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.
Expand All @@ -75,6 +102,11 @@ struct DemangledNameInfo {
return BasenameRange.second > BasenameRange.first;
}

/// Returns \c true if this object holds a valid template arguments range.
bool hasTemplateArguments() const {
return TemplateArgumentsRange.second >= TemplateArgumentsRange.first;
}

/// Returns \c true if this object holds a valid scope range.
bool hasScope() const { return ScopeRange.second >= ScopeRange.first; }

Expand All @@ -88,6 +120,11 @@ struct DemangledNameInfo {
return QualifiersRange.second >= QualifiersRange.first;
}

/// Returns \c true if this object holds a valid name qualifiers range.
bool hasNameQualifiers() const {
return NameQualifiersRange.second >= NameQualifiersRange.first;
}

/// Returns \c true if this object holds a valid prefix range.
bool hasPrefix() const { return PrefixRange.second >= PrefixRange.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 @@ -91,6 +91,7 @@ struct Entry {
FunctionPrefix,
FunctionScope,
FunctionBasename,
FunctionNameQualifiers,
FunctionTemplateArguments,
FunctionFormattedArguments,
FunctionReturnLeft,
Expand Down
8 changes: 8 additions & 0 deletions lldb/source/Core/DemangledNameInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ void TrackingOutputBuffer::finalizeStart() {
if (NameInfo.BasenameRange.second == 0)
NameInfo.BasenameRange.second = getCurrentPosition();

// There is something between the basename and the start of the function
// arguments. Assume those are template arguments (which *should* be true for
// C++ demangled names, but this assumption may change in the future, in
// which case this needs to be adjusted).
if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first)
Copy link
Member

Choose a reason for hiding this comment

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

Lets add a comment

Suggested change
if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first)
// There is something between the basename and the start of the function arguments.
// Assume those are template arguments (which *should* be true for C++ demangled names, but
// this assumption may change in the future, in which case this needs to be adjusted).
if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first)

NameInfo.TemplateArgumentsRange = {NameInfo.BasenameRange.second,
NameInfo.ArgumentsRange.first};
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we should modify TrackingOutputBuffer::printLeftImpl(const NameWithTemplateArgs &N) to track this instead? Would that be more accurate/future proof?

We could change it to:

updateTemplateArgumentsStart();
N.TemplateArgs->print(*this);
updateTemplateArgumentsEnd();

And inside updateTemplateArgumentsStart you would check shouldTrack(). I think that should work.

The benefit being that we won't get any surprises when the demangler decides to put things between the template arguments and function parameters.

Would this work with your planned Swift plugin changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree that this would be much more robust.
I think I've tried that in one of my iterations but this caused some of the tests to regress. I will post the results of the failing tests in a moment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

********************
Failed Tests (20):
  lldb-api :: commands/expression/import-std-module/weak_ptr-dbg-info-content/TestDbgInfoContentWeakPtrFromStdModule.py
  lldb-api :: commands/expression/two-files/TestObjCTypeQueryFromOtherCompileUnit.py
  lldb-api :: commands/frame/var/direct-ivar/objc/TestFrameVarDirectIvarObjC.py
  lldb-api :: commands/register/register/register_command/TestRegisters.py
  lldb-api :: functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCCF.py
  lldb-api :: functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCExpr.py
  lldb-api :: functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjCKVO.py
  lldb-api :: functionalities/data-formatter/data-formatter-objc/cmtime/TestDataFormatterCMTime.py
  lldb-api :: functionalities/data-formatter/data-formatter-proper-plurals/TestFormattersOneIsSingular.py
  lldb-api :: functionalities/process_crash_info/TestProcessCrashInfo.py
  lldb-api :: functionalities/single-thread-step/TestSingleThreadStepTimeout.py
  lldb-api :: lang/c/tls_globals/TestTlsGlobals.py
  lldb-api :: lang/cpp/dynamic-value/TestDynamicValue.py
  lldb-api :: lang/objc/objc-dyn-sbtype/TestObjCDynamicSBType.py
  lldb-api :: lang/objc/objc-foundation-dictionary-empty/TestNSDictionary0.py
  lldb-api :: macosx/function-starts/TestFunctionStarts.py
  lldb-api :: macosx/no-nlist-memory-module/TestNoNlistsDylib.py
  lldb-shell :: ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test
  lldb-shell :: SymbolFile/DWARF/objc-gmodules-class-extension.test
  lldb-unit :: Core/./LLDBCoreTests/DemanglingPartsTests/DemanglingPartsTestFixture/DemanglingParts/7

The only seemingly related failing test is DemanglingPartsTestFixture/DemanglingParts/7 with:

/Users/charleszablit/Developer/llvm-proper/llvm-project/lldb/unittests/Core/MangledTest.cpp:681: Failure
Expected equality of these values:
  OB->NameInfo.TemplateArgumentsRange
    Which is: (38, 342)
  info.TemplateArgumentsRange
    Which is: (0, 0)

This value should be (0, 0):

   { "_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvEC1EDn",
     {
       /*.BasenameRange=*/{344, 354}, /*.TemplateArgumentsRange=*/{0, 0}, /*.ScopeRange=*/{0, 344},
       /*.ArgumentsRange=*/{354, 370}, /*.QualifiersRange=*/{370, 370}, /*.NameQualifiersRange=*/{0, 0},
       /*.PrefixRange=*/{0, 0}, /*.SuffixRange=*/{0, 0}
     },
     /*.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=*/""
   }

I think we should override more methods in TrackingOutputBuffer for this to work.

Copy link
Member

Choose a reason for hiding this comment

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

Can you paste the diff you used to run these tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh sorry, yes:

diff --git a/lldb/source/Core/DemangledNameInfo.cpp b/lldb/source/Core/DemangledNameInfo.cpp
index 95db524fba2d..021016d7c072 100644
--- a/lldb/source/Core/DemangledNameInfo.cpp
+++ b/lldb/source/Core/DemangledNameInfo.cpp
@@ -80,6 +80,20 @@ void TrackingOutputBuffer::finalizeQualifiersEnd() {
   NameInfo.QualifiersRange.second = getCurrentPosition();
 }

+void TrackingOutputBuffer::updateTemplateArgumentsStart() {
+  if (!shouldTrack())
+    return;
+
+  NameInfo.TemplateArgumentsRange.first = getCurrentPosition();
+}
+
+void TrackingOutputBuffer::updateTemplateArgumentsEnd() {
+  if (!shouldTrack())
+    return;
+
+  NameInfo.TemplateArgumentsRange.second = getCurrentPosition();
+}
+
 void TrackingOutputBuffer::finalizeStart() {
   if (!shouldTrack())
     return;
@@ -92,9 +106,9 @@ void TrackingOutputBuffer::finalizeStart() {
   if (NameInfo.BasenameRange.second == 0)
     NameInfo.BasenameRange.second = getCurrentPosition();

-  if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first)
-    NameInfo.TemplateArgumentsRange = {NameInfo.BasenameRange.second,
-                                       NameInfo.ArgumentsRange.first};
+  // if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first)
+  //   NameInfo.TemplateArgumentsRange = {NameInfo.BasenameRange.second,
+  //                                      NameInfo.ArgumentsRange.first};

   assert(!shouldTrack());
   assert(canFinalize());
@@ -228,7 +242,9 @@ void TrackingOutputBuffer::printLeftImpl(const NestedName &N) {
 void TrackingOutputBuffer::printLeftImpl(const NameWithTemplateArgs &N) {
   N.Name->print(*this);
   updateBasenameEnd();
+  updateTemplateArgumentsStart();
   N.TemplateArgs->print(*this);
+  updateTemplateArgumentsEnd();
 }

 } // namespace lldb_private

Copy link
Member

@Michael137 Michael137 Aug 4, 2025

Choose a reason for hiding this comment

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

Ah i think it's because we're tracking the template arguments when printing the scope. Ideally we would only track them when we already have a basename. But that doesn't get finalized until we're in TrackingOutputBuffer::finalizeStart. So it sounds like tracking it in finalizeStart may be our best bet for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I understand. I've reverted my changes and added the comment like you asked above. Thanks!


assert(!shouldTrack());
assert(canFinalize());
}
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 @@ -128,6 +128,7 @@ constexpr Definition g_function_child_entries[] = {
Definition("prefix", EntryType::FunctionPrefix),
Definition("scope", EntryType::FunctionScope),
Definition("basename", EntryType::FunctionBasename),
Definition("name-qualifiers", EntryType::FunctionNameQualifiers),
Definition("template-arguments", EntryType::FunctionTemplateArguments),
Definition("formatted-arguments", EntryType::FunctionFormattedArguments),
Definition("return-left", EntryType::FunctionReturnLeft),
Expand Down Expand Up @@ -390,6 +391,7 @@ const char *FormatEntity::Entry::TypeToCString(Type t) {
ENUM_TO_CSTR(FunctionPrefix);
ENUM_TO_CSTR(FunctionScope);
ENUM_TO_CSTR(FunctionBasename);
ENUM_TO_CSTR(FunctionNameQualifiers);
ENUM_TO_CSTR(FunctionTemplateArguments);
ENUM_TO_CSTR(FunctionFormattedArguments);
ENUM_TO_CSTR(FunctionReturnLeft);
Expand Down Expand Up @@ -1842,6 +1844,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
case Entry::Type::FunctionPrefix:
case Entry::Type::FunctionScope:
case Entry::Type::FunctionBasename:
case Entry::Type::FunctionNameQualifiers:
case Entry::Type::FunctionTemplateArguments:
case Entry::Type::FunctionFormattedArguments:
case Entry::Type::FunctionReturnRight:
Expand Down
10 changes: 5 additions & 5 deletions lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,12 @@ GetDemangledTemplateArguments(const SymbolContext &sc) {

auto [demangled_name, info] = *info_or_err;

if (info.ArgumentsRange.first < info.BasenameRange.second)
return llvm::createStringError("Arguments range for '%s' is invalid.",
demangled_name.data());
if (!info.hasTemplateArguments())
return llvm::createStringError(
"Template arguments range for '%s' is invalid.", demangled_name.data());

return demangled_name.slice(info.BasenameRange.second,
info.ArgumentsRange.first);
return demangled_name.slice(info.TemplateArgumentsRange.first,
info.TemplateArgumentsRange.second);
}

static llvm::Expected<llvm::StringRef>
Expand Down
Loading
Loading