Skip to content

[lldb] Extract logic out of GetFunctionDisplayName into a helper function #10766

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 7 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
228 changes: 121 additions & 107 deletions lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1704,30 +1704,16 @@ 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 = GetFunctionName(sc, exe_ctx);
if (display_name.empty())
return false;
s << display_name;
return true;
}
case Language::FunctionNameRepresentation::eNameWithArgs: {
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 = GetFunctionName(sc, exe_ctx);
if (display_name.empty())
return false;
ExecutionContextScope *exe_scope =
exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL;
const InlineFunctionInfo *inline_info = NULL;
VariableListSP variable_list_sp;
bool get_function_vars = true;
Expand All @@ -1747,109 +1733,137 @@ bool SwiftLanguage::GetFunctionDisplayName(
sc.function->GetBlock(true).GetBlockVariableList(true);
}

if (inline_info) {
s << display_name;
s.PutCString(" [inlined] ");
display_name = inline_info->GetName().GetString();
}

VariableList args;
if (variable_list_sp)
variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument,
args);
if (args.GetSize() == 0) {
s << display_name;
return true;

s << display_name;
if (inline_info) {
s.PutCString(" [inlined] ");

Choose a reason for hiding this comment

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

In llvm#135343 I added a SymbolContext::GetPossiblyInlinedFunctionName. We decided the [inlined] format was a bit too noisy.

Could we clean up the Swift plugin to use that functionality?

}
const char *cstr = display_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.
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;
s << GetFunctionDisplayArgs(sc, args, exe_ctx);
return true;
}
if (open_paren) {
close_paren = strchr(open_paren, ')');
}
return false;
}

if (open_paren)
s.Write(cstr, open_paren - cstr + 1);
else {
s << display_name;
s.PutChar('(');
std::string SwiftLanguage::GetFunctionName(const SymbolContext &sc,
const ExecutionContext *exe_ctx) {
if (!sc.function)
return {};
if (sc.function->GetLanguage() != eLanguageTypeSwift)
return {};
std::string name = SwiftLanguageRuntime::DemangleSymbolAsString(
sc.function->GetMangled().GetMangledName().GetStringRef(),
SwiftLanguageRuntime::eSimplified, &sc, exe_ctx);
if (name.empty())
return {};
const char *cstr = name.data();
const char *open_paren = strchr(cstr, '(');
const char *generic = strchr(cstr, '<');
if (open_paren && generic && generic < open_paren)
return std::string(cstr, generic);
if (open_paren)
return std::string(cstr, open_paren);

Choose a reason for hiding this comment

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

Hmmm why is this new logic needed?

Choose a reason for hiding this comment

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

We didn't previously do this in the NoArgs case right?

Copy link
Author

Choose a reason for hiding this comment

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

I am a bit confused by the original implementation of the NoArgs case: if the frame is foo(a: 1) I would expect the NoArgscase to returnfooand nothing else. But in the original implementation, it returnsfoo(a: Int)`.

This additional logic is due to me splitting the the logic into 2 helper functions. Say we have foo<T>(a: T). GetFunctionName will return foo and GetFunctionDisplayArgs will return <Int>(a=1).

Hence: no, we didn't previously do this in the NoArgs case, but I think this was a mistake. Since there are no tests for the way backtraces are printed in the Swift plugin, I have no idea how much of a breaking change this is.

Choose a reason for hiding this comment

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

Oh I see. In that case, if eNoArgs was always broken, lets just remove that case entirely? That'll mean less code to change in the other PR (and we won't be breaking any users)

return name;
}

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);
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--;
}
const size_t num_args = args.GetSize();
for (size_t arg_idx = 0; arg_idx < num_args; ++arg_idx) {
std::string buffer;

VariableSP var_sp(args.GetVariableAtIndex(arg_idx));
ValueObjectSP var_value_sp(
ValueObjectVariable::Create(exe_scope, var_sp));
if (!var_sp || !var_value_sp || var_sp->IsArtificial())
continue;
StreamString ss;
const char *var_representation = nullptr;
const char *var_name = var_value_sp->GetName().GetCString();
if (var_value_sp->GetCompilerType().IsValid()) {
if (var_value_sp && exe_scope->CalculateTarget())
var_value_sp = var_value_sp->GetQualifiedRepresentationIfAvailable(
exe_scope->CalculateTarget()
->TargetProperties::GetPreferDynamicValue(),
exe_scope->CalculateTarget()
->TargetProperties::GetEnableSyntheticValue());
if (var_value_sp->GetCompilerType().IsAggregateType() &&
DataVisualization::ShouldPrintAsOneLiner(*var_value_sp.get())) {
static StringSummaryFormat format(TypeSummaryImpl::Flags()
.SetHideItemNames(false)
.SetShowMembersOneLiner(true),
"");
format.FormatObject(var_value_sp.get(), buffer, TypeSummaryOptions());
var_representation = buffer.c_str();
} else
var_value_sp->DumpPrintableRepresentation(
ss,
ValueObject::ValueObjectRepresentationStyle::
eValueObjectRepresentationStyleSummary,
eFormatDefault,
ValueObject::PrintableRepresentationSpecialCases::eAllow, false);
}
if (ss.GetData() && ss.GetSize())
var_representation = ss.GetData();
if (arg_idx > 0)
s.PutCString(", ");
if (var_value_sp->GetError().Success()) {
if (var_representation)
s.Printf("%s=%s", var_name, var_representation);
else
s.Printf("%s=%s at %s", var_name,
var_value_sp->GetTypeName().GetCString(),
var_value_sp->GetLocationAsCString());
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();
for (size_t arg_idx = 0; arg_idx < num_args; ++arg_idx) {
std::string buffer;

VariableSP var_sp(args.GetVariableAtIndex(arg_idx));
ValueObjectSP var_value_sp(ValueObjectVariable::Create(exe_scope, var_sp));
if (!var_sp || !var_value_sp || var_sp->IsArtificial())
continue;
StreamString ss;
const char *var_representation = nullptr;
const char *var_name = var_value_sp->GetName().GetCString();
if (var_value_sp->GetCompilerType().IsValid()) {
if (var_value_sp && exe_scope->CalculateTarget())
var_value_sp = var_value_sp->GetQualifiedRepresentationIfAvailable(
exe_scope->CalculateTarget()
->TargetProperties::GetPreferDynamicValue(),
exe_scope->CalculateTarget()
->TargetProperties::GetEnableSyntheticValue());
if (var_value_sp->GetCompilerType().IsAggregateType() &&
DataVisualization::ShouldPrintAsOneLiner(*var_value_sp.get())) {
static StringSummaryFormat format(TypeSummaryImpl::Flags()
.SetHideItemNames(false)
.SetShowMembersOneLiner(true),
"");
format.FormatObject(var_value_sp.get(), buffer, TypeSummaryOptions());
var_representation = buffer.c_str();
} else
s.Printf("%s=<unavailable>", var_name);
var_value_sp->DumpPrintableRepresentation(
ss,
ValueObject::ValueObjectRepresentationStyle::
eValueObjectRepresentationStyleSummary,
eFormatDefault,
ValueObject::PrintableRepresentationSpecialCases::eAllow, false);
}

if (close_paren)
s.PutCString(close_paren);
else
s.PutChar(')');
}
return true;
if (ss.GetData() && ss.GetSize())
var_representation = ss.GetData();
if (arg_idx > 0)
s.PutCString(", ");
if (var_value_sp->GetError().Success()) {
if (var_representation)
s.Printf("%s=%s", var_name, var_representation);
else
s.Printf("%s=%s at %s", var_name,
var_value_sp->GetTypeName().GetCString(),
var_value_sp->GetLocationAsCString());
} else
s.Printf("%s=<unavailable>", var_name);
}
return false;

if (close_paren)
s.PutCString(close_paren);
else
s.PutChar(')');

return s.GetString().str();
}

void SwiftLanguage::GetExceptionResolverDescription(bool catch_on,
Expand Down
31 changes: 31 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,37 @@ class SwiftLanguage : public Language {
FunctionNameRepresentation representation,
Stream &s) override;

/// Returns the name of function up to the first generic or opening
/// parenthesis.
///
/// The following function will have the name "foo":
/// \code{.swift}
/// func foo<T>(bar: T) {}
/// \endcode
///
/// \param sc The associated SymbolContext.
/// \param exe_ctx The associated ExecutionContext.
/// \returns The name of a function as an std::string.
std::string GetFunctionName(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
/// "<Int>(bar=1)"
/// \code{.swift}
/// func foo<T>(bar: T) {}
/// foo(1)
/// \endcode
///
/// \param sc The associated SymbolContext.
/// \param args The VariableList that are passed to the function.
/// \param exe_ctx The associated ExecutionContext.
/// \returns The generics and arguments of a function call as an std::string.
std::string
GetFunctionDisplayArgs(const SymbolContext &sc, VariableList &args,
const lldb_private::ExecutionContext *exe_ctx);

void GetExceptionResolverDescription(bool catch_on, bool throw_on,
Stream &s) override;

Expand Down
52 changes: 52 additions & 0 deletions lldb/test/Shell/Swift/function-name-backtrace.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# XFAIL: target-windows

# Test Swift function name printing in backtraces.

# RUN: split-file %s %t
# RUN: %target-swiftc -g %t/main.swift -o %t.out
# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \
# RUN: | FileCheck %s

#--- main.swift
func foo() async -> Int {
return await try! bar(a: 1, b: 1)
}

var res = {
(index: Int) -> Int in
return index + 10
}(1)

fileprivate func bar(a: Int, b: Int) async throws -> Int {
var baz = Baz(baz: 1)
return res + a + b + Foo.foo_(a: baz)
}

struct Foo {
let foo: Int
static func foo_<T>(a: T) -> Int {
var a_ = a as! Baz
return a_.qux(a: 1)
}
}

struct Baz {
var baz: Int
mutating func qux<T>(a: T) -> Int {
baz += 1
return baz
}
}

await foo()

#--- commands.input
b qux

run
bt

# CHECK: `Baz.qux<Int>(a=1)
# CHECK: `static Foo.foo_<main.Baz>(a=(baz = 1))
# CHECK: `bar(a=1, b=1)
# CHECK: `foo()