Skip to content

[Syntax Highlighting] Add name and parameters syntax highlighting in Swift backtraces #10710

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
14 changes: 11 additions & 3 deletions lldb/include/lldb/Core/Mangled.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ class Mangled {
ePreferDemangledWithoutArguments
};

enum NameFormatPreference {
eCompactName,
eFullName,
};

enum ManglingScheme {
eManglingSchemeNone = 0,
eManglingSchemeMSVC,
Expand Down Expand Up @@ -127,7 +132,9 @@ class Mangled {
///
/// \return
/// A const reference to the demangled name string object.
ConstString GetDemangledName(const SymbolContext *sc = nullptr) const;
ConstString
GetDemangledName(const SymbolContext *sc = nullptr,
NameFormatPreference preference = eFullName) const;

/// Display demangled name get accessor.
///
Expand Down Expand Up @@ -291,8 +298,9 @@ class Mangled {
/// demangled name (if any). If \c force is \c true (or the mangled name
/// on this object was not previously demangled), demangle and cache the
/// name.
ConstString GetDemangledNameImpl(bool force,
const SymbolContext *sc = nullptr) const;
ConstString
GetDemangledNameImpl(bool force, const SymbolContext *sc = nullptr,
NameFormatPreference preference = eFullName) const;

/// The mangled version of the name.
ConstString m_mangled;
Expand Down
78 changes: 55 additions & 23 deletions lldb/source/Core/Mangled.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,53 @@ void Mangled::SetValue(ConstString name) {
}
}

// BEGIN SWIFT
#ifdef LLDB_ENABLE_SWIFT
std::pair<ConstString, DemangledNameInfo>
GetSwiftDemangledStr(ConstString m_mangled, const SymbolContext *sc,
ConstString &m_demangled,
Mangled::NameFormatPreference preference) {
const char *mangled_name = m_mangled.AsCString("");
Log *log = GetLog(LLDBLog::Demangle);
LLDB_LOGF(log, "demangle swift: %s", mangled_name);

Choose a reason for hiding this comment

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

because otherwise this will crash on a nullptr

Copy link
Author

Choose a reason for hiding this comment

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

Fixed, thanks 👍

SwiftLanguageRuntime::DemangleMode demangle_mode;
switch (preference) {
case Mangled::eFullName:
demangle_mode = SwiftLanguageRuntime::DemangleMode::eTypeName;
break;
case Mangled::eCompactName:
demangle_mode = SwiftLanguageRuntime::DemangleMode::eSimplified;
break;
}
auto [demangled, info] = SwiftLanguageRuntime::TrackedDemangleSymbolAsString(
mangled_name, demangle_mode, sc);
info.PrefixRange.second =
std::min(info.BasenameRange.first, info.ArgumentsRange.first);
info.SuffixRange.first =
std::max(info.BasenameRange.second, info.ArgumentsRange.second);
info.SuffixRange.second = demangled.length();

// Don't cache the demangled name if the function isn't available yet.
// Only cache eFullName demangled functions to keep the cache consistent.
if (!sc || !sc->function ||
preference == Mangled::NameFormatPreference::eCompactName) {
LLDB_LOGF(log, "demangle swift: %s -> \"%s\" (not cached)", mangled_name,
demangled.c_str());
return std::make_pair(ConstString(demangled), info);
}
if (demangled.empty()) {
LLDB_LOGF(log, "demangle swift: %s -> error: failed to demangle",
mangled_name);
} else {
LLDB_LOGF(log, "demangle swift: %s -> \"%s\"", mangled_name,
demangled.c_str());
m_demangled.SetStringWithMangledCounterpart(demangled, m_mangled);
}
return std::make_pair(m_demangled, info);
}
#endif // LLDB_ENABLE_SWIFT
// END SWIFT

// Local helpers for different demangling implementations.
static char *GetMSVCDemangledStr(llvm::StringRef M) {
char *demangled_cstr = llvm::microsoftDemangle(
Expand Down Expand Up @@ -293,10 +340,10 @@ bool Mangled::GetRichManglingInfo(RichManglingContext &context,
}

ConstString Mangled::GetDemangledName( // BEGIN SWIFT
const SymbolContext *sc
const SymbolContext *sc, NameFormatPreference preference
// END SWIFT
) const {
return GetDemangledNameImpl(/*force=*/false, sc);
return GetDemangledNameImpl(/*force=*/false, sc, preference);
}

std::optional<DemangledNameInfo> const &Mangled::GetDemangledInfo() const {
Expand All @@ -311,7 +358,8 @@ std::optional<DemangledNameInfo> const &Mangled::GetDemangledInfo() const {
// 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::GetDemangledNameImpl(bool force, // BEGIN SWIFT
const SymbolContext *sc
const SymbolContext *sc,
NameFormatPreference preference
// END SWIFT
) const {
if (!m_mangled)
Expand Down Expand Up @@ -350,26 +398,10 @@ ConstString Mangled::GetDemangledNameImpl(bool force, // BEGIN SWIFT
// explicitly unsupported on llvm.org.
#ifdef LLDB_ENABLE_SWIFT
{
const char *mangled_name = m_mangled.GetCString();
Log *log = GetLog(LLDBLog::Demangle);
LLDB_LOGF(log, "demangle swift: %s", mangled_name);
std::string demangled(SwiftLanguageRuntime::DemangleSymbolAsString(
mangled_name, SwiftLanguageRuntime::eTypeName, sc));
// Don't cache the demangled name the function isn't available yet.
if (!sc || !sc->function) {
LLDB_LOGF(log, "demangle swift: %s -> \"%s\" (not cached)", mangled_name,
demangled.c_str());
return ConstString(demangled);
}
if (demangled.empty()) {
LLDB_LOGF(log, "demangle swift: %s -> error: failed to demangle",
mangled_name);
} else {
LLDB_LOGF(log, "demangle swift: %s -> \"%s\"", mangled_name,
demangled.c_str());
m_demangled.SetStringWithMangledCounterpart(demangled, m_mangled);
}
return m_demangled;
auto demangled =
GetSwiftDemangledStr(m_mangled, sc, m_demangled, preference);
m_demangled_info.emplace(std::move(demangled.second));
return demangled.first;
}
#endif // LLDB_ENABLE_SWIFT
break;
Expand Down
119 changes: 102 additions & 17 deletions lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1906,23 +1906,96 @@ SwiftLanguage::GetDemangledFunctionNameWithoutArguments(Mangled mangled) const {
return mangled_name;
}

static std::optional<llvm::StringRef>
static llvm::Expected<std::pair<llvm::StringRef, DemangledNameInfo>>
GetAndValidateInfo(const SymbolContext &sc) {
Mangled mangled = sc.GetPossiblyInlinedFunctionName();
if (!mangled)
return llvm::createStringError("Function does not have a mangled name.");

auto demangled_name =
mangled.GetDemangledName(nullptr, Mangled::eCompactName).GetStringRef();

Choose a reason for hiding this comment

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

I think I asked this before but why do we need to use eCompactName here? I know that some tests failed if you switched it to full, but why is that? Is the name tracking more complicated and that's why it only supports compact mode?

Or was it because the backtrace previously always used the compact mode, but in other places around LLDB we use full mode? So to avoid changing the backtrace too much you used compact here too? I think it seems reasonable to use compact demangled names when displaying a backtrace. We probably want something like that for C++ too. If my understanding is correct, what are the places where we need non-compact demangled names?

Copy link
Author

Choose a reason for hiding this comment

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

I re-ran the tests just to make sure I got the information right:

The backtrace was using eCompactName before. The code to get the demangled name for backtraces called SwiftLanguage::GetFunctionDisplayName.

Some other parts of LLDB used eFullName before. The demangling was happening in Mangled::GetSwiftDemangledStr.

However, with the highlighting changes, the backtrace now uses Mangled::GetSwiftDemangledStr as well. So now both the backtrace and the rest of the features use Mangled::GetSwiftDemangledStr.

If my understanding is correct, what are the places where we need non-compact demangled names?

This changes the behavior of the python GetFunctionName API for example, which itself breaks some tests.

I think at some point some other APIs broke, but rerunning the tests with the latest changes and forcing eTypeDisplayName, this is the only one which causes breakages.

if (demangled_name.empty())
return llvm::createStringError(
"Function '%s' does not have a demangled name.",
mangled.GetMangledName().AsCString(""));

const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
if (!info)
return llvm::createStringError(
"Function '%s' does not have demangled info.", demangled_name.data());

// Function without a basename is nonsense.
if (!info->hasBasename())
return llvm::createStringError(
"DemangledInfo for '%s does not have basename range.",
demangled_name.data());

return std::make_pair(demangled_name, *info);
}

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

auto [demangled_name, info] = *info_or_err;

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

static std::optional<llvm::StringRef>
static llvm::Expected<llvm::StringRef>
GetDemangledFunctionPrefix(const SymbolContext &sc) {
return std::nullopt;
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 suffix range.",
demangled_name.data());

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

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

auto [demangled_name, info] = *info_or_err;

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

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

static bool PrintDemangledArgumentList(Stream &s, const SymbolContext &sc) {
return false;
assert(sc.symbol);

auto info_or_err = GetAndValidateInfo(sc);
if (!info_or_err) {
LLDB_LOG_ERROR(GetLog(LLDBLog::Language), info_or_err.takeError(),
"Failed to handle ${{function.formatted-arguments}} "
"frame-format variable: {0}");
return false;
}
auto [demangled_name, info] = *info_or_err;

if (!info.hasArguments())
return false;

s << demangled_name.slice(info.ArgumentsRange.first,
info.ArgumentsRange.second);

return true;
}

static VariableListSP GetFunctionVariableList(const SymbolContext &sc) {
Expand All @@ -1941,11 +2014,15 @@ bool SwiftLanguage::HandleFrameFormatVariable(const SymbolContext &sc,
Stream &s) {
switch (type) {
case FormatEntity::Entry::Type::FunctionBasename: {
std::optional<llvm::StringRef> name = GetDemangledBasename(sc);
if (!name)
auto name_or_err = GetDemangledBasename(sc);
if (!name_or_err) {
LLDB_LOG_ERROR(
GetLog(LLDBLog::Language), name_or_err.takeError(),
"Failed to handle ${{function.basename}} frame-format variable: {0}");
return false;
}

s << *name;
s << *name_or_err;

return true;
}
Expand All @@ -1972,20 +2049,28 @@ bool SwiftLanguage::HandleFrameFormatVariable(const SymbolContext &sc,
return true;
}
case FormatEntity::Entry::Type::FunctionPrefix: {
std::optional<llvm::StringRef> prefix = GetDemangledFunctionPrefix(sc);
if (!prefix)
auto prefix_or_err = GetDemangledFunctionPrefix(sc);
if (!prefix_or_err) {
LLDB_LOG_ERROR(
GetLog(LLDBLog::Language), prefix_or_err.takeError(),
"Failed to handle ${{function.prefix}} frame-format variable: {0}");
return false;
}

s << *prefix;
s << *prefix_or_err;

return true;
}
case FormatEntity::Entry::Type::FunctionSuffix: {
std::optional<llvm::StringRef> suffix = GetDemangledFunctionSuffix(sc);
if (!suffix)
auto suffix_or_err = GetDemangledFunctionSuffix(sc);
if (!suffix_or_err) {
LLDB_LOG_ERROR(
GetLog(LLDBLog::Language), suffix_or_err.takeError(),
"Failed to handle ${{function.suffix}} frame-format variable: {0}");
return false;
}

s << *suffix;
s << *suffix_or_err;

return true;
}
Expand Down Expand Up @@ -2019,7 +2104,7 @@ class PluginProperties : public Properties {
}

FormatEntity::Entry GetFunctionNameFormat() const {
return GetPropertyAtIndexAs<const FormatEntity::Entry>(
return GetPropertyAtIndexAs<FormatEntity::Entry>(
ePropertyFunctionNameFormat, {});
}
};
Expand Down
Loading