Skip to content

[lldb] fix cropped demangled names in Swift backtraces #11068

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

Open
wants to merge 2 commits into
base: swift/release/6.2
Choose a base branch
from
Open
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. |
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``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
25 changes: 25 additions & 0 deletions lldb/include/lldb/Core/DemangledNameInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ struct DemangledNameInfo {
/// \endcode
std::pair<size_t, size_t> BasenameRange;

/// A [start, end) pair for the function template arguments.
/// 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> TemplateRange;

/// A [start, end) pair for the function scope qualifiers.
/// E.g., for
/// \code{.cpp}
Expand Down Expand Up @@ -59,6 +69,11 @@ 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 arguments,
/// that is not tracked by the rest of the pairs.
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 +90,11 @@ struct DemangledNameInfo {
return BasenameRange.second > BasenameRange.first;
}

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

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

Expand All @@ -88,6 +108,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
3 changes: 3 additions & 0 deletions lldb/source/Core/FormatEntity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ constexpr Definition g_frame_child_entries[] = {
constexpr Definition g_function_child_entries[] = {
Definition("id", EntryType::FunctionID),
Definition("name", EntryType::FunctionName),
Definition("name-qualifiers", EntryType::FunctionNameQualifiers),
Definition("name-without-args", EntryType::FunctionNameNoArgs),
Definition("name-with-args", EntryType::FunctionNameWithArgs),
Definition("mangled-name", EntryType::FunctionMangledName),
Expand Down Expand Up @@ -384,6 +385,7 @@ const char *FormatEntity::Entry::TypeToCString(Type t) {
ENUM_TO_CSTR(FunctionDidChange);
ENUM_TO_CSTR(FunctionInitialFunction);
ENUM_TO_CSTR(FunctionName);
ENUM_TO_CSTR(FunctionNameQualifiers);
ENUM_TO_CSTR(FunctionNameWithArgs);
ENUM_TO_CSTR(FunctionNameNoArgs);
ENUM_TO_CSTR(FunctionMangledName);
Expand Down Expand Up @@ -1852,6 +1854,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
9 changes: 9 additions & 0 deletions lldb/source/Core/Mangled.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,15 @@ GetSwiftDemangledStr(ConstString m_mangled, const SymbolContext *sc,
info.SuffixRange.first =
std::max(info.BasenameRange.second, info.ArgumentsRange.second);
info.SuffixRange.second = demangled.length();
if (info.hasBasename() && info.hasArguments()) {
if (info.hasTemplate()) {
info.NameQualifiersRange.second =
std::min(info.ArgumentsRange.first, info.TemplateRange.first);
} else {
info.NameQualifiersRange.second = info.ArgumentsRange.first;
}
info.NameQualifiersRange.first = info.BasenameRange.second;
}

// Don't cache the demangled name if the function isn't available yet.
// Only cache eFullName demangled functions to keep the cache consistent.
Expand Down
14 changes: 14 additions & 0 deletions lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1953,6 +1953,20 @@ bool CPlusPlusLanguage::HandleFrameFormatVariable(
return true;
}

case FormatEntity::Entry::Type::FunctionNameQualifiers: {
auto name_or_err = GetDemangledBasename(sc);
if (!name_or_err) {
LLDB_LOG_ERROR(GetLog(LLDBLog::Language), name_or_err.takeError(),
"Failed to handle ${{function.name-qualifiers}} "
"frame-format variable: {0}");
return false;
}

s << *name_or_err;

return true;
}

case FormatEntity::Entry::Type::FunctionTemplateArguments: {
auto template_args_or_err = GetDemangledTemplateArguments(sc);
if (!template_args_or_err) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ include "../../../../include/lldb/Core/PropertiesBase.td"
let Definition = "language_swift" in {
def FunctionNameFormat: Property<"function-name-format", "FormatEntity">,
Global,
DefaultStringValue<"${function.prefix}${ansi.fg.yellow}${function.basename}${ansi.normal}${function.formatted-arguments}${function.suffix}">,
DefaultStringValue<"${function.prefix}${ansi.fg.yellow}${function.basename}${ansi.normal}${function.name-qualifiers}${function.template-arguments}${function.formatted-arguments}${function.suffix}">,
Desc<"Swift specific frame format string to use when displaying stack frame information for threads.">;
}
129 changes: 84 additions & 45 deletions lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1718,13 +1718,7 @@ bool SwiftLanguage::GetFunctionDisplayName(
// No need to customize this.
return false;
case Language::FunctionNameRepresentation::eNameWithNoArgs: {
if (!sc.function)
return false;
if (sc.function->GetLanguage() != eLanguageTypeSwift)
return false;
std::string display_name = SwiftLanguageRuntime::DemangleSymbolAsString(
sc.function->GetMangled().GetMangledName().GetStringRef(),
SwiftLanguageRuntime::eSimplified, &sc, exe_ctx);
std::string display_name = GetDemangledFunctionName(sc, exe_ctx);
if (display_name.empty())
return false;
s << display_name;
Expand Down Expand Up @@ -1759,69 +1753,82 @@ bool SwiftLanguage::GetFunctionDisplayName(
variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument,
args);

s << GetFunctionTemplateArguments(sc, exe_ctx);
s << GetFunctionDisplayArgs(sc, args, exe_ctx);
return true;
}
}
return false;
}

std::string SwiftLanguage::GetFunctionName(const SymbolContext &sc,
const ExecutionContext *exe_ctx) {
std::string
SwiftLanguage::GetDemangledFunctionName(const SymbolContext &sc,
const ExecutionContext *exe_ctx) {
if (!sc.function)
return {};
if (sc.function->GetLanguage() != eLanguageTypeSwift)
return {};
std::string name = SwiftLanguageRuntime::DemangleSymbolAsString(
return SwiftLanguageRuntime::DemangleSymbolAsString(
sc.GetPossiblyInlinedFunctionName().GetMangledName(),
SwiftLanguageRuntime::eSimplified, &sc, exe_ctx);
if (name.empty())
}

std::string SwiftLanguage::GetFunctionName(const SymbolContext &sc,
const ExecutionContext *exe_ctx) {
std::string demangled_name = GetDemangledFunctionName(sc, exe_ctx);
if (demangled_name.empty())
return {};
size_t open_paren = name.find('(');
size_t generic = name.find('<');
size_t open_paren = demangled_name.find('(');
size_t generic = demangled_name.find('<');
size_t name_end = std::min(open_paren, generic);
if (name_end == std::string::npos)
return name;
return name.substr(0, name_end);
return demangled_name;
return demangled_name.substr(0, name_end);
}

std::string
SwiftLanguage::GetFunctionTemplateArguments(const SymbolContext &sc,
const ExecutionContext *exe_ctx) {
std::string demangled_name = GetDemangledFunctionName(sc, exe_ctx);
if (demangled_name.empty())
return {};
size_t open_paren = demangled_name.find('(');
size_t generic_start = demangled_name.find('<');
if (generic_start == std::string::npos || generic_start > open_paren)
return {};

int generic_depth = 1;
size_t generic_end = generic_start + 1;

while (generic_end < demangled_name.size() && generic_depth > 0) {
if (demangled_name[generic_end] == '<') {
generic_depth++;
} else if (demangled_name[generic_end] == '>') {
generic_depth--;
}
generic_end++;
}

if (generic_depth != 0)
return {};

return demangled_name.substr(generic_start, generic_end - generic_start);
}

std::string SwiftLanguage::GetFunctionDisplayArgs(
const SymbolContext &sc, VariableList &args,
const lldb_private::ExecutionContext *exe_ctx) {
ExecutionContextScope *exe_scope =
exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL;
std::string name = SwiftLanguageRuntime::DemangleSymbolAsString(
sc.function->GetMangled().GetMangledName().GetStringRef(),
SwiftLanguageRuntime::eSimplified, &sc, exe_ctx);
std::string name = GetDemangledFunctionName(sc, exe_ctx);
lldb_private::StreamString s;
const char *cstr = name.data();
const char *open_paren = strchr(cstr, '(');
const char *close_paren = nullptr;
const char *generic = strchr(cstr, '<');
// If before the arguments list begins there is a template sign
// then scan to the end of the generic args before you try to find
// the arguments list.
const char *generic_start = generic;
if (generic && open_paren && generic < open_paren) {
int generic_depth = 1;
++generic;
for (; *generic && generic_depth > 0; generic++) {
if (*generic == '<')
generic_depth++;
if (*generic == '>')
generic_depth--;
}
if (*generic)
open_paren = strchr(generic, '(');
else
open_paren = nullptr;
}
if (open_paren) {
close_paren = strchr(open_paren, ')');
}

if (generic_start && generic_start < open_paren)
s.Write(generic_start, open_paren - generic_start);
s.PutChar('(');

const size_t num_args = args.GetSize();
Expand Down Expand Up @@ -1945,6 +1952,23 @@ GetDemangledBasename(const SymbolContext &sc) {
info.BasenameRange.second);
}

static llvm::Expected<llvm::StringRef>
GetDemangledNameQualifiers(const SymbolContext &sc) {
auto info_or_err = GetAndValidateInfo(sc);
if (!info_or_err)
return info_or_err.takeError();

auto [demangled_name, info] = *info_or_err;

if (!info.hasPrefix())
return llvm::createStringError(
"DemangledInfo for '%s does not have a name qualifiers range.",
demangled_name.data());

return demangled_name.slice(info.NameQualifiersRange.first,
info.NameQualifiersRange.second);
}

static llvm::Expected<llvm::StringRef>
GetDemangledFunctionPrefix(const SymbolContext &sc) {
auto info_or_err = GetAndValidateInfo(sc);
Expand All @@ -1955,7 +1979,7 @@ GetDemangledFunctionPrefix(const SymbolContext &sc) {

if (!info.hasPrefix())
return llvm::createStringError(
"DemangledInfo for '%s does not have suffix range.",
"DemangledInfo for '%s does not have a prefix range.",
demangled_name.data());

return demangled_name.slice(info.PrefixRange.first, info.PrefixRange.second);
Expand All @@ -1971,7 +1995,7 @@ GetDemangledFunctionSuffix(const SymbolContext &sc) {

if (!info.hasSuffix())
return llvm::createStringError(
"DemangledInfo for '%s does not have suffix range.",
"DemangledInfo for '%s does not have a suffix range.",
demangled_name.data());

return demangled_name.slice(info.SuffixRange.first, info.SuffixRange.second);
Expand Down Expand Up @@ -2026,6 +2050,24 @@ bool SwiftLanguage::HandleFrameFormatVariable(const SymbolContext &sc,

return true;
}
case FormatEntity::Entry::Type::FunctionNameQualifiers: {
auto qualifiers_or_err = GetDemangledNameQualifiers(sc);
if (!qualifiers_or_err) {
LLDB_LOG_ERROR(GetLog(LLDBLog::Language), qualifiers_or_err.takeError(),
"Failed to handle ${{function.name-qualifiers}} "
"frame-format variable: {0}");
return false;
}

s << *qualifiers_or_err;

return true;
}
case FormatEntity::Entry::Type::FunctionTemplateArguments: {
s << GetFunctionTemplateArguments(sc, exe_ctx);

return true;
}
case FormatEntity::Entry::Type::FunctionFormattedArguments: {
// This ensures we print the arguments even when no debug-info is available.
//
Expand All @@ -2034,9 +2076,7 @@ bool SwiftLanguage::HandleFrameFormatVariable(const SymbolContext &sc,
// once we have a "fallback operator" in the frame-format language.
if (!sc.function && sc.symbol)
return PrintDemangledArgumentList(s, sc);
std::string display_name = SwiftLanguageRuntime::DemangleSymbolAsString(
sc.function->GetMangled().GetMangledName().GetStringRef(),
SwiftLanguageRuntime::eSimplified, &sc, exe_ctx);
std::string display_name = GetDemangledFunctionName(sc, exe_ctx);
if (display_name.empty())
return false;

Expand Down Expand Up @@ -2076,7 +2116,6 @@ bool SwiftLanguage::HandleFrameFormatVariable(const SymbolContext &sc,
}

case FormatEntity::Entry::Type::FunctionScope:
case FormatEntity::Entry::Type::FunctionTemplateArguments:
case FormatEntity::Entry::Type::FunctionReturnRight:
case FormatEntity::Entry::Type::FunctionReturnLeft:
case FormatEntity::Entry::Type::FunctionQualifiers:
Expand Down
6 changes: 6 additions & 0 deletions lldb/source/Plugins/Language/Swift/SwiftLanguage.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ class SwiftLanguage : public Language {
FunctionNameRepresentation representation,
Stream &s) override;

std::string GetDemangledFunctionName(const SymbolContext &sc,
const ExecutionContext *exe_ctx);

/// Returns the name of function up to the first generic or opening
/// parenthesis.
///
Expand All @@ -83,6 +86,9 @@ class SwiftLanguage : public Language {
std::string GetFunctionName(const SymbolContext &sc,
const ExecutionContext *exe_ctx);

std::string GetFunctionTemplateArguments(const SymbolContext &sc,
const ExecutionContext *exe_ctx);

/// Returns the arguments of a function call with its generics if any.
///
/// Calling GetFunctionDisplayArgs on the following function call will return
Expand Down
Loading