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

Open
wants to merge 3 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
2 changes: 2 additions & 0 deletions lldb/.clang-format-ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td
source/Plugins/Language/Swift/LanguageSwiftProperties.td
5 changes: 5 additions & 0 deletions lldb/include/lldb/Core/DemangledNameInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ struct DemangledNameInfo {
bool hasBasename() const {
return BasenameRange.second > BasenameRange.first;
}

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

/// An OutputBuffer which keeps a record of where certain parts of a
Expand Down
8 changes: 8 additions & 0 deletions lldb/include/lldb/Core/PluginManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,14 @@ class PluginManager {
static bool CreateSettingForCPlusPlusLanguagePlugin(
Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
llvm::StringRef description, bool is_global_property);

static lldb::OptionValuePropertiesSP
GetSettingForSwiftLanguagePlugin(Debugger &debugger,

Choose a reason for hiding this comment

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

Again, it would be nice if this could live in Plugin/ (but maybe it's harder to pull off here?)

Copy link
Author

Choose a reason for hiding this comment

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

These static methods are only called in SwiftLanguage.cpp. They could be moved to that file, but that would break the convention of the PluginManager. I think it would be better that these methods live in their respective XLanguage.cpp file, to keep language specific components separated. What do you think @Michael137 ?

llvm::StringRef setting_name);

static bool CreateSettingForSwiftLanguagePlugin(
Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
llvm::StringRef description, bool is_global_property);
};

} // namespace lldb_private
Expand Down
58 changes: 36 additions & 22 deletions lldb/source/Core/Mangled.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,41 @@ void Mangled::SetValue(ConstString name) {
}
}

// BEGIN SWIFT
#ifdef LLDB_ENABLE_SWIFT
static ConstString GetSwiftDemangledStr(ConstString m_mangled,
const SymbolContext *sc,
ConstString &m_demangled) {
const char *mangled_name = m_mangled.GetCString();
Log *log = GetLog(LLDBLog::Demangle);
LLDB_LOGF(log, "demangle swift: %s", mangled_name);
auto [demangled, info] = SwiftLanguageRuntime::TrackedDemangleSymbolAsString(
mangled_name, SwiftLanguageRuntime::eSimplified, 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 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;
}
#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 @@ -349,28 +384,7 @@ ConstString Mangled::GetDemangledNameImpl(bool force, // BEGIN SWIFT
// Demangling a swift name requires the swift compiler. This is
// 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;
}
return GetSwiftDemangledStr(m_mangled, sc, m_demangled);
#endif // LLDB_ENABLE_SWIFT
break;
case eManglingSchemeNone:
Expand Down
15 changes: 15 additions & 0 deletions lldb/source/Core/PluginManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1766,6 +1766,7 @@ static constexpr llvm::StringLiteral kJITLoaderPluginName("jit-loader");
static constexpr llvm::StringLiteral
kStructuredDataPluginName("structured-data");
static constexpr llvm::StringLiteral kCPlusPlusLanguagePlugin("cplusplus");
static constexpr llvm::StringLiteral kSwiftLanguagePlugin("swift");

lldb::OptionValuePropertiesSP
PluginManager::GetSettingForDynamicLoaderPlugin(Debugger &debugger,
Expand Down Expand Up @@ -1937,3 +1938,17 @@ bool PluginManager::CreateSettingForCPlusPlusLanguagePlugin(
"Settings for CPlusPlus language plug-ins",
properties_sp, description, is_global_property);
}

lldb::OptionValuePropertiesSP
PluginManager::GetSettingForSwiftLanguagePlugin(Debugger &debugger,
llvm::StringRef setting_name) {
return GetSettingForPlugin(debugger, setting_name, kSwiftLanguagePlugin);
}

bool PluginManager::CreateSettingForSwiftLanguagePlugin(
Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
llvm::StringRef description, bool is_global_property) {
return CreateSettingForPlugin(debugger, kSwiftLanguagePlugin,
"Settings for Swift language plug-ins",
properties_sp, description, is_global_property);
}
12 changes: 12 additions & 0 deletions lldb/source/Plugins/Language/Swift/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
lldb_tablegen(LanguageSwiftProperties.inc -gen-lldb-property-defs
SOURCE LanguageSwiftProperties.td
TARGET LLDBPluginLanguageSwiftPropertiesGen)

lldb_tablegen(LanguageSwiftPropertiesEnum.inc -gen-lldb-property-enum-defs
SOURCE LanguageSwiftProperties.td
TARGET LLDBPluginLanguageSwiftPropertiesEnumGen)

set(LLVM_NO_RTTI 1)

add_lldb_library(lldbPluginSwiftLanguage PLUGIN
Expand Down Expand Up @@ -36,3 +44,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT SWIFT_COMPILER_MSVC_LIKE)
target_compile_options(lldbPluginSwiftLanguage PRIVATE
-Wno-dollar-in-identifier-extension)
endif()

add_dependencies(lldbPluginSwiftLanguage
LLDBPluginLanguageSwiftPropertiesGen
LLDBPluginLanguageSwiftPropertiesEnumGen)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
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}">,
Desc<"Swift specific frame format string to use when displaying stack frame information for threads.">;
}
216 changes: 215 additions & 1 deletion lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ void SwiftLanguage::Initialize() {
static ConstString g_SwiftStringStorageClass("_TtCs15__StringStorage");
static ConstString g_NSArrayClass1("_TtCs22__SwiftDeferredNSArray");
PluginManager::RegisterPlugin(GetPluginNameStatic(), "Swift Language",
CreateInstance);
CreateInstance, &DebuggerInitialize);

lldb_private::formatters::NSString_Additionals::GetAdditionalSummaries()
.emplace(
Expand Down Expand Up @@ -1895,6 +1895,220 @@ SwiftLanguage::GetDemangledFunctionNameWithoutArguments(Mangled mangled) const {
return mangled_name;
}

static std::optional<llvm::StringRef>
GetDemangledBasename(const SymbolContext &sc) {
Mangled mangled = sc.GetPossiblyInlinedFunctionName();
if (!mangled)
return std::nullopt;

auto demangled_name = mangled.GetDemangledName().GetStringRef();
if (demangled_name.empty())
return std::nullopt;

const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
if (!info)
return std::nullopt;

// Function without a basename is nonsense.
if (!info->hasBasename())
return std::nullopt;

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

static std::optional<llvm::StringRef>
GetDemangledFunctionPrefix(const SymbolContext &sc) {
Mangled mangled = sc.GetPossiblyInlinedFunctionName();
if (!mangled)
return std::nullopt;

auto demangled_name = mangled.GetDemangledName().GetStringRef();
if (demangled_name.empty())
return std::nullopt;

const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
if (!info)
return std::nullopt;

// Function without a basename is nonsense.
if (!info->hasBasename())
return std::nullopt;

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

static std::optional<llvm::StringRef>
GetDemangledFunctionSuffix(const SymbolContext &sc) {
Mangled mangled = sc.GetPossiblyInlinedFunctionName();
if (!mangled)
return std::nullopt;

auto demangled_name = mangled.GetDemangledName().GetStringRef();
if (demangled_name.empty())
return std::nullopt;

const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
if (!info)
return std::nullopt;

// Function without a basename is nonsense.
if (!info->hasBasename())
return std::nullopt;

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

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

Mangled mangled = sc.GetPossiblyInlinedFunctionName();
if (!mangled)
return false;

auto demangled_name = mangled.GetDemangledName().GetStringRef();
if (demangled_name.empty())
return false;

const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
if (!info)
return false;

// Function without a basename is nonsense.
if (!info->hasBasename())
return false;

if (info->ArgumentsRange.second < info->ArgumentsRange.first)
return false;

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

return true;
}

static VariableListSP GetFunctionVariableList(const SymbolContext &sc) {
assert(sc.function);

if (sc.block)
if (Block *inline_block = sc.block->GetContainingInlinedBlock())
return inline_block->GetBlockVariableList(true);

return sc.function->GetBlock(true).GetBlockVariableList(true);
}

bool SwiftLanguage::HandleFrameFormatVariable(const SymbolContext &sc,
const ExecutionContext *exe_ctx,
FormatEntity::Entry::Type type,
Stream &s) {
switch (type) {
case FormatEntity::Entry::Type::FunctionBasename: {
std::optional<llvm::StringRef> name = GetDemangledBasename(sc);
if (!name)
return false;

s << *name;

return true;
}
case FormatEntity::Entry::Type::FunctionFormattedArguments: {
// This ensures we print the arguments even when no debug-info is available.
//
// FIXME: we should have a Entry::Type::FunctionArguments and
// use it in the plugin.cplusplus.display.function-name-format
// 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);
if (display_name.empty())
return false;

VariableList args;
if (auto variable_list_sp = GetFunctionVariableList(sc))
variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument,
args);

ExecutionContextScope *exe_scope =
exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
return GetFunctionDisplayArgs(args, s, display_name, exe_scope);
}
case FormatEntity::Entry::Type::FunctionPrefix: {
std::optional<llvm::StringRef> prefix = GetDemangledFunctionPrefix(sc);
if (!prefix)
return false;

s << *prefix;

return true;
}
case FormatEntity::Entry::Type::FunctionSuffix: {
std::optional<llvm::StringRef> suffix = GetDemangledFunctionSuffix(sc);
if (!suffix)
return false;

s << *suffix;

return true;
}

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:
default:
return true;
}
}

#define LLDB_PROPERTIES_language_swift
#include "LanguageSwiftProperties.inc"

enum {
#define LLDB_PROPERTIES_language_swift
#include "LanguageSwiftPropertiesEnum.inc"
};

namespace {
class PluginProperties : public Properties {
public:
static llvm::StringRef GetSettingName() { return "display"; }

PluginProperties() {
m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
m_collection_sp->Initialize(g_language_swift_properties);
}

FormatEntity::Entry GetFunctionNameFormat() const {
return GetPropertyAtIndexAs<const FormatEntity::Entry>(
ePropertyFunctionNameFormat, {});
}
};
} // namespace

static PluginProperties &GetGlobalPluginProperties() {
static PluginProperties g_settings;
return g_settings;
}

FormatEntity::Entry SwiftLanguage::GetFunctionNameFormat() const {
return GetGlobalPluginProperties().GetFunctionNameFormat();
}

void SwiftLanguage::DebuggerInitialize(Debugger &debugger) {
if (!PluginManager::GetSettingForSwiftLanguagePlugin(
debugger, PluginProperties::GetSettingName())) {
PluginManager::CreateSettingForSwiftLanguagePlugin(
debugger, GetGlobalPluginProperties().GetValueProperties(),
"Properties for the Swift language plug-in.",
/*is_global_property=*/true);
}
}

namespace {
using namespace swift::Demangle;
struct AsyncInfo {
Expand Down
Loading