[lldb][Format] Introduce new frame-format variables for function parts (#131836)

Adds new frame-format variables and implements them in the CPlusPlusLanguage plugin.

We use the `DemangledNameInfo` type to retrieve the necessary part of the demangled name.

https://github.com/llvm/llvm-project/pull/131836
This commit is contained in:
Michael Buch
2025-04-11 11:02:19 +01:00
parent a2672250be
commit 8b91b44a3b
20 changed files with 719 additions and 30 deletions

View File

@@ -88,6 +88,13 @@ struct Entry {
FunctionNameWithArgs,
FunctionNameNoArgs,
FunctionMangledName,
FunctionScope,
FunctionBasename,
FunctionTemplateArguments,
FunctionFormattedArguments,
FunctionReturnLeft,
FunctionReturnRight,
FunctionQualifiers,
FunctionAddrOffset,
FunctionAddrOffsetConcrete,
FunctionLineOffset,

View File

@@ -311,8 +311,7 @@ public:
/// mangling preference. If this object represents an inlined function,
/// returns the name of the inlined function. Returns nullptr if no function
/// name could be determined.
const char *GetPossiblyInlinedFunctionName(
Mangled::NamePreference mangling_preference) const;
Mangled GetPossiblyInlinedFunctionName() const;
// Member variables
lldb::TargetSP target_sp; ///< The Target for a given query

View File

@@ -15,6 +15,7 @@
#include <set>
#include <vector>
#include "lldb/Core/FormatEntity.h"
#include "lldb/Core/Highlighter.h"
#include "lldb/Core/PluginInterface.h"
#include "lldb/DataFormatters/DumpValueObjectOptions.h"
@@ -371,6 +372,13 @@ public:
FunctionNameRepresentation representation,
Stream &s);
virtual bool HandleFrameFormatVariable(const SymbolContext &sc,
const ExecutionContext *exe_ctx,
FormatEntity::Entry::Type type,
Stream &s) {
return false;
}
virtual ConstString
GetDemangledFunctionNameWithoutArguments(Mangled mangled) const {
if (ConstString demangled = mangled.GetDemangledName())

View File

@@ -122,7 +122,15 @@ constexpr Definition g_function_child_entries[] = {
Definition("pc-offset", EntryType::FunctionPCOffset),
Definition("initial-function", EntryType::FunctionInitial),
Definition("changed", EntryType::FunctionChanged),
Definition("is-optimized", EntryType::FunctionIsOptimized)};
Definition("is-optimized", EntryType::FunctionIsOptimized),
Definition("scope", EntryType::FunctionScope),
Definition("basename", EntryType::FunctionBasename),
Definition("template-arguments", EntryType::FunctionTemplateArguments),
Definition("formatted-arguments", EntryType::FunctionFormattedArguments),
Definition("return-left", EntryType::FunctionReturnLeft),
Definition("return-right", EntryType::FunctionReturnRight),
Definition("qualifiers", EntryType::FunctionQualifiers),
};
constexpr Definition g_line_child_entries[] = {
Entry::DefinitionWithChildren("file", EntryType::LineEntryFile,
@@ -353,6 +361,13 @@ const char *FormatEntity::Entry::TypeToCString(Type t) {
ENUM_TO_CSTR(FunctionNameWithArgs);
ENUM_TO_CSTR(FunctionNameNoArgs);
ENUM_TO_CSTR(FunctionMangledName);
ENUM_TO_CSTR(FunctionScope);
ENUM_TO_CSTR(FunctionBasename);
ENUM_TO_CSTR(FunctionTemplateArguments);
ENUM_TO_CSTR(FunctionFormattedArguments);
ENUM_TO_CSTR(FunctionReturnLeft);
ENUM_TO_CSTR(FunctionReturnRight);
ENUM_TO_CSTR(FunctionQualifiers);
ENUM_TO_CSTR(FunctionAddrOffset);
ENUM_TO_CSTR(FunctionAddrOffsetConcrete);
ENUM_TO_CSTR(FunctionLineOffset);
@@ -1167,8 +1182,9 @@ static bool PrintFunctionNameWithArgs(Stream &s,
ExecutionContextScope *exe_scope =
exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
const char *cstr =
sc.GetPossiblyInlinedFunctionName(Mangled::ePreferDemangled);
const char *cstr = sc.GetPossiblyInlinedFunctionName()
.GetName(Mangled::ePreferDemangled)
.AsCString();
if (!cstr)
return false;
@@ -1186,7 +1202,8 @@ static bool PrintFunctionNameWithArgs(Stream &s,
return true;
}
static bool HandleFunctionNameWithArgs(Stream &s,const ExecutionContext *exe_ctx,
static bool HandleFunctionNameWithArgs(Stream &s,
const ExecutionContext *exe_ctx,
const SymbolContext &sc) {
Language *language_plugin = nullptr;
bool language_plugin_handled = false;
@@ -1711,8 +1728,9 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
return true;
}
const char *name = sc->GetPossiblyInlinedFunctionName(
Mangled::NamePreference::ePreferDemangled);
const char *name = sc->GetPossiblyInlinedFunctionName()
.GetName(Mangled::NamePreference::ePreferDemangled)
.AsCString();
if (!name)
return false;
@@ -1743,8 +1761,10 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
return true;
}
const char *name = sc->GetPossiblyInlinedFunctionName(
Mangled::NamePreference::ePreferDemangledWithoutArguments);
const char *name =
sc->GetPossiblyInlinedFunctionName()
.GetName(Mangled::NamePreference::ePreferDemangledWithoutArguments)
.AsCString();
if (!name)
return false;
@@ -1753,19 +1773,38 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
return true;
}
case Entry::Type::FunctionScope:
case Entry::Type::FunctionBasename:
case Entry::Type::FunctionTemplateArguments:
case Entry::Type::FunctionFormattedArguments:
case Entry::Type::FunctionReturnRight:
case Entry::Type::FunctionReturnLeft:
case Entry::Type::FunctionQualifiers: {
if (!sc->function)
return false;
Language *language_plugin =
Language::FindPlugin(sc->function->GetLanguage());
if (!language_plugin)
return false;
return language_plugin->HandleFrameFormatVariable(*sc, exe_ctx, entry.type,
s);
}
case Entry::Type::FunctionNameWithArgs: {
if (!sc)
return false;
return HandleFunctionNameWithArgs(s, exe_ctx, *sc);
}
case Entry::Type::FunctionMangledName: {
if (!sc)
return false;
const char *name = sc->GetPossiblyInlinedFunctionName(
Mangled::NamePreference::ePreferMangled);
const char *name = sc->GetPossiblyInlinedFunctionName()
.GetName(Mangled::NamePreference::ePreferMangled)
.AsCString();
if (!name)
return false;

View File

@@ -235,6 +235,151 @@ static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream,
return true;
}
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>
GetDemangledTemplateArguments(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;
if (info->ArgumentsRange.first < info->BasenameRange.second)
return std::nullopt;
return demangled_name.slice(info->BasenameRange.second,
info->ArgumentsRange.first);
}
static std::optional<llvm::StringRef>
GetDemangledReturnTypeLHS(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;
if (info->ScopeRange.first >= demangled_name.size())
return std::nullopt;
return demangled_name.substr(0, info->ScopeRange.first);
}
static std::optional<llvm::StringRef>
GetDemangledFunctionQualifiers(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;
if (info->QualifiersRange.second < info->QualifiersRange.first)
return std::nullopt;
return demangled_name.slice(info->QualifiersRange.first,
info->QualifiersRange.second);
}
static std::optional<llvm::StringRef>
GetDemangledReturnTypeRHS(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;
if (info->QualifiersRange.first < info->ArgumentsRange.second)
return std::nullopt;
return demangled_name.slice(info->ArgumentsRange.second,
info->QualifiersRange.first);
}
static std::optional<llvm::StringRef>
GetDemangledScope(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;
if (info->ScopeRange.second < info->ScopeRange.first)
return std::nullopt;
return demangled_name.slice(info->ScopeRange.first, info->ScopeRange.second);
}
bool CPlusPlusLanguage::CxxMethodName::TrySimplifiedParse() {
// This method tries to parse simple method definitions which are presumably
// most comman in user programs. Definitions that can be parsed by this
@@ -1694,8 +1839,9 @@ static bool PrintFunctionNameWithArgs(Stream &s,
ExecutionContextScope *exe_scope =
exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
const char *cstr = sc.GetPossiblyInlinedFunctionName(
Mangled::NamePreference::ePreferDemangled);
const char *cstr = sc.GetPossiblyInlinedFunctionName()
.GetName(Mangled::NamePreference::ePreferDemangled)
.AsCString();
if (!cstr)
return false;
@@ -1739,3 +1885,86 @@ bool CPlusPlusLanguage::GetFunctionDisplayName(
return false;
}
}
bool CPlusPlusLanguage::HandleFrameFormatVariable(
const SymbolContext &sc, const ExecutionContext *exe_ctx,
FormatEntity::Entry::Type type, Stream &s) {
assert(sc.function);
switch (type) {
case FormatEntity::Entry::Type::FunctionScope: {
std::optional<llvm::StringRef> scope = GetDemangledScope(sc);
if (!scope)
return false;
s << *scope;
return true;
}
case FormatEntity::Entry::Type::FunctionBasename: {
std::optional<llvm::StringRef> name = GetDemangledBasename(sc);
if (!name)
return false;
s << *name;
return true;
}
case FormatEntity::Entry::Type::FunctionTemplateArguments: {
std::optional<llvm::StringRef> template_args =
GetDemangledTemplateArguments(sc);
if (!template_args)
return false;
s << *template_args;
return true;
}
case FormatEntity::Entry::Type::FunctionFormattedArguments: {
VariableList args;
if (auto variable_list_sp = GetFunctionVariableList(sc))
variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument,
args);
ExecutionContextScope *exe_scope =
exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
s << '(';
FormatEntity::PrettyPrintFunctionArguments(s, args, exe_scope);
s << ')';
return true;
}
case FormatEntity::Entry::Type::FunctionReturnRight: {
std::optional<llvm::StringRef> return_rhs = GetDemangledReturnTypeRHS(sc);
if (!return_rhs)
return false;
s << *return_rhs;
return true;
}
case FormatEntity::Entry::Type::FunctionReturnLeft: {
std::optional<llvm::StringRef> return_lhs = GetDemangledReturnTypeLHS(sc);
if (!return_lhs)
return false;
s << *return_lhs;
return true;
}
case FormatEntity::Entry::Type::FunctionQualifiers: {
std::optional<llvm::StringRef> quals = GetDemangledFunctionQualifiers(sc);
if (!quals)
return false;
s << *quals;
return true;
}
default:
return false;
}
}

View File

@@ -105,6 +105,13 @@ public:
FunctionNameRepresentation representation,
Stream &s) override;
bool HandleFrameFormatVariable(const SymbolContext &sc,
const ExecutionContext *exe_ctx,
FormatEntity::Entry::Type type,
Stream &s) override;
static bool IsCPPMangledName(llvm::StringRef name);
// Extract C++ context and identifier from a string using heuristic matching
// (as opposed to
// CPlusPlusLanguage::CxxMethodName which has to have a fully qualified C++

View File

@@ -10,6 +10,7 @@
#include "lldb/Core/Address.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/DemangledNameInfo.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Host/Host.h"
@@ -872,34 +873,36 @@ const Symbol *SymbolContext::FindBestGlobalDataSymbol(ConstString name,
return nullptr; // no error; we just didn't find anything
}
char const *SymbolContext::GetPossiblyInlinedFunctionName(
Mangled::NamePreference mangling_preference) const {
const char *name = nullptr;
if (function)
name = function->GetMangled().GetName(mangling_preference).AsCString();
else if (symbol)
name = symbol->GetMangled().GetName(mangling_preference).AsCString();
Mangled SymbolContext::GetPossiblyInlinedFunctionName() const {
auto get_mangled = [this]() {
if (function)
return function->GetMangled();
if (symbol)
return symbol->GetMangled();
return Mangled{};
};
if (!block)
return name;
return get_mangled();
const Block *inline_block = block->GetContainingInlinedBlock();
if (!inline_block)
return name;
return get_mangled();
const InlineFunctionInfo *inline_info =
inline_block->GetInlinedFunctionInfo();
if (!inline_info)
return name;
return get_mangled();
// If we do have an inlined frame name, return that.
if (char const *inline_name =
inline_info->GetMangled().GetName(mangling_preference).AsCString())
if (const Mangled &inline_name = inline_info->GetMangled())
return inline_name;
// Sometimes an inline frame may not have mangling information,
// but does have a valid name.
return inline_info->GetName().AsCString();
return Mangled{inline_info->GetName().AsCString()};
}
//

View File

@@ -0,0 +1,46 @@
# Test the ${function.basename} frame-format variable.
# RUN: split-file %s %t
# RUN: %build %t/main.cpp -o %t.out
# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \
# RUN: | FileCheck %s
#
#--- main.cpp
namespace ns {
template<typename T>
struct Bar {
template<typename K>
T bar(K k) const & { return 1.0f; }
};
template<typename T>
struct Foo {
template<typename K>
[[gnu::abi_tag("Test")]] void foo() const volatile && {
Bar<float> b;
b.bar(b);
}
};
template<typename T>
T func() {
ns::Foo<int>{}.foo<int>();
return T{};
}
} // namespace ns
int main() {
ns::func<ns::Foo<int>>();
return 0;
}
#--- commands.input
settings set -f frame-format "custom-frame '${function.basename}'\n"
break set -n bar
run
bt
# CHECK: custom-frame 'bar'
# CHECK: custom-frame 'foo[abi:Test]'
# CHECK: custom-frame 'func'

View File

@@ -0,0 +1,24 @@
# Check that we have an appropriate fallback for ${function.basename} in languages that
# don't implement this frame format variable (in this case Objective-C).
#
# RUN: split-file %s %t
# RUN: %build %t/main.m -o %t.objc.out
# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
# RUN: | FileCheck %s
#--- main.m
int func() {}
int bar() { func(); }
int main() { return bar(); }
#--- commands.input
settings set -f frame-format "custom-frame '${function.basename}'\n"
break set -n bar
run
bt
# CHECK: bt
# CHECK-NOT: custom-frame

View File

@@ -0,0 +1,42 @@
# Test the ${function.formatted-arguments} frame-format variable.
# RUN: split-file %s %t
# RUN: %build %t/main.cpp -o %t.out
# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \
# RUN: | FileCheck %s
#--- main.cpp
struct Foo {
void func() {}
};
void bar() {
Foo{}.func();
}
void foo(int, int x) {
bar();
}
void myFunc(char const * str,
void (*fptr)(int, int)) {
fptr(5, 10);
}
int main(int argc, char const *argv[]) {
myFunc("hello", &foo);
return 0;
}
#--- commands.input
settings set -f frame-format "custom-frame '${function.formatted-arguments}'\n"
break set -n func
run
bt
# CHECK: custom-frame '(this={{.*}})'
# CHECK: custom-frame '()'
# CHECK: custom-frame '((null)=5, x=10)'
# CHECK: custom-frame '(str="hello", fptr=({{.*}}.out`foo(int, int) at main.cpp:{{[0-9]+}}))'
# CHECK: custom-frame '(argc=1, argv={{.*}})'

View File

@@ -0,0 +1,24 @@
# Check that we have an appropriate fallback for ${function.formatted-arguments} in languages that
# don't implement this frame format variable (in this case Objective-C).
#
# RUN: split-file %s %t
# RUN: %build %t/main.m -o %t.objc.out
# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
# RUN: | FileCheck %s
#--- main.m
int func() {}
int bar() { func(); }
int main() { return bar(); }
#--- commands.input
settings set -f frame-format "custom-frame '${function.formatted-arguments}'\n"
break set -n func
run
bt
# CHECK: bt
# CHECK-NOT: custom-frame

View File

@@ -0,0 +1,24 @@
# Test the ${function.qualifiers} frame-format variable.
# RUN: split-file %s %t
# RUN: %build %t/main.cpp -o %t.out
# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \
# RUN: | FileCheck %s
#--- main.cpp
struct Foo {
void foo() const volatile && {}
void bar() { Foo{}.foo(); }
};
int main() { Foo{}.bar(); }
#--- commands.input
settings set -f frame-format "custom-frame '${function.qualifiers}'\n"
break set -n foo
run
bt
# CHECK: custom-frame ' const volatile &&'
# CHECK: custom-frame ''

View File

@@ -0,0 +1,25 @@
# Check that we have an appropriate fallback for ${function.qualifiers} in
# languages that don't implement this frame format variable (in this case Objective-C).
# RUN: split-file %s %t
#
# RUN: %build %t/main.m -o %t.objc.out
# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
# RUN: | FileCheck %s
#--- main.m
int foo() {}
int bar() { foo(); }
int main() { return bar(); }
#--- commands.input
settings set -f frame-format "custom-frame '${function.qualifiers}'\n"
break set -n foo
run
bt
# CHECK: bt
# CHECK-NOT: custom-frame

View File

@@ -0,0 +1,55 @@
# Test the ${function.return-left} and ${function.return-right}
# frame-format variables.
# RUN: split-file %s %t
# RUN: %build %t/main.cpp -o %t.out
# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \
# RUN: | FileCheck %s
#--- main.cpp
namespace ns::ns2 {
template<typename T>
struct Foo {};
template<typename T>
Foo<int> qux(int) {
return {};
}
template<typename T>
Foo<int> (*bar(Foo<float>))(int) {
qux<T>(5);
return qux<T>;
}
struct Bar {
template<typename T>
Foo<int> (* (*foo(int) const &&)(Foo<float>))(int) {
bar<T>(Foo<float>{});
return bar<T>;
}
};
}
int main(int argc, char const *argv[]) {
ns::ns2::Bar{}.foo<int>(5);
return 0;
}
#--- commands.input
settings set -f frame-format "custom-frame '${function.return-left}'\n"
break set -n qux
run
bt
# CHECK: custom-frame 'ns::ns2::Foo<int> '
# CHECK: custom-frame 'ns::ns2::Foo<int> (*'
# CHECK: custom-frame 'ns::ns2::Foo<int> (* (*'
settings set -f frame-format "other-frame '${function.return-right}'\n"
bt
# CHECK: other-frame ''
# CHECK: other-frame ')(int)'
# CHECK: other-frame ')(ns::ns2::Foo<float>))(int)'

View File

@@ -0,0 +1,31 @@
# Check that we have an appropriate fallback for ${function.return-left} and
# ${function.return-right} in languages that don't implement this frame
# format variable (in this case Objective-C).
#
# RUN: split-file %s %t
# RUN: %build %t/main.m -o %t.objc.out
# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
# RUN: | FileCheck %s
#--- main.m
int qux() {}
int bar() { qux(); }
int main() { return bar(); }
#--- commands.input
settings set -f frame-format "custom-frame '${function.return-left}'\n"
break set -n qux
run
bt
# CHECK: bt
# CHECK-NOT: custom-frame
settings set -f frame-format "other-frame '${function.return-right}'\n"
bt
# CHECK: bt
# CHECK-NOT: other-frame

View File

@@ -0,0 +1,41 @@
# Test the ${function.scope} frame-format variable.
# RUN: split-file %s %t
# RUN: %build %t/main.cpp -o %t.out
# RUN: %lldb -o "settings set interpreter.stop-command-source-on-error false" \
# RUN: -x -b -s %t/commands.input %t.out -o exit 2>&1 \
# RUN: | FileCheck %s
#--- main.cpp
namespace ns::ns2 {
inline namespace ins {
template <typename T>
struct Foo {
void func() {}
};
int foo() {
Foo<int>{}.func();
return 5;
}
} // namespace ins
} // namespace ns::ns2
using namespace ns::ns2;
int bar() {
return ns::ns2::foo();
}
int main() { return bar(); }
#--- commands.input
settings set -f frame-format "custom-frame '${function.scope}'\n"
break set -n func
run
bt
# CHECK: frame 'ns::ns2::ins::Foo<int>::'
# CHECK: frame 'ns::ns2::ins::'
# CHECK: frame ''

View File

@@ -0,0 +1,24 @@
# Check that we have an appropriate fallback for ${function.scope} in languages that
# don't implement this frame format variable (in this case Objective-C).
#
# RUN: split-file %s %t
# RUN: %build %t/main.m -o %t.objc.out
# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
# RUN: | FileCheck %s
#--- main.m
int func() {}
int bar() { func(); }
int main() { return bar(); }
#--- commands.input
settings set -f frame-format "custom-frame '${function.scope}'\n"
break set -n func
run
bt
# CHECK: bt
# CHECK-NOT: custom-frame

View File

@@ -0,0 +1,37 @@
# Test the ${function.template-arguments} frame-format variable.
# RUN: split-file %s %t
# RUN: %build %t/main.cpp -o %t.cxx.out
# RUN: %lldb -x -b -s %t/commands.input %t.cxx.out -o exit 2>&1 \
# RUN: | FileCheck %s
#--- main.cpp
template<typename K>
struct Foo {
template<typename T>
void func() {}
};
template<typename T, template <typename> class K,
typename M>
int foo() {
Foo<int>{}.func<T>();
return 5;
}
int bar() {
return foo<int, Foo, Foo<float>>();
}
int main() { return bar(); }
#--- commands.input
settings set -f frame-format "custom-frame '${function.template-arguments}'\n"
break set -n func
run
bt
# CHECK: custom-frame '<int>'
# CHECK: custom-frame '<int, Foo, Foo<float>>'
# CHECK: custom-frame ''

View File

@@ -0,0 +1,24 @@
# Check that we have an appropriate fallback for ${function.template-arguments} in
# languages that don't implement this frame format variable (in this case Objective-C).
#
# RUN: split-file %s %t
# RUN: %build %t/main.m -o %t.objc.out
# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
# RUN: | FileCheck %s
#--- main.m
int func() {}
int bar() { func(); }
int main() { return bar(); }
#--- commands.input
settings set -f frame-format "custom-frame '${function.template-arguments}'\n"
break set -n func
run
bt
# CHECK: bt
# CHECK-NOT: custom-frame

View File

@@ -22,13 +22,13 @@ c
c
# NAME_WITH_ARGS: frame int ns::foo<void (Foo::*)(int (*)(int)) const noexcept>(str="method")
c
# NAME_WITH_ARGS: frame ns::returns_func_ptr<int>((null)={{.*}})
# NAME_WITH_ARGS: frame detail::Quux<double> (* (*ns::returns_func_ptr<int>((null)={{.*}}))(int))(float)
c
# NAME_WITH_ARGS: frame void Foo::foo<int (*)()>(this={{.*}}, arg=({{.*}}`(anonymous namespace)::anon_bar() at {{.*}}))
c
# NAME_WITH_ARGS: frame void Foo::operator<<<1>(this={{.*}}, (null)=0)
c
# NAME_WITH_ARGS: frame Foo::returns_func_ptr<int>(this={{.*}}, (null)={{.*}})
# NAME_WITH_ARGS: frame detail::Quux<double> (* (*Foo::returns_func_ptr<int>(this={{.*}}, (null)={{.*}}))(int))(float) const
c
# NAME_WITH_ARGS: frame inlined_foo(str="bar")
q