[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:
@@ -88,6 +88,13 @@ struct Entry {
|
||||
FunctionNameWithArgs,
|
||||
FunctionNameNoArgs,
|
||||
FunctionMangledName,
|
||||
FunctionScope,
|
||||
FunctionBasename,
|
||||
FunctionTemplateArguments,
|
||||
FunctionFormattedArguments,
|
||||
FunctionReturnLeft,
|
||||
FunctionReturnRight,
|
||||
FunctionQualifiers,
|
||||
FunctionAddrOffset,
|
||||
FunctionAddrOffsetConcrete,
|
||||
FunctionLineOffset,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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++
|
||||
|
||||
@@ -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()};
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -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'
|
||||
@@ -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
|
||||
@@ -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={{.*}})'
|
||||
@@ -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
|
||||
@@ -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 ''
|
||||
@@ -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
|
||||
55
lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test
Normal file
55
lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test
Normal 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)'
|
||||
@@ -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
|
||||
41
lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test
Normal file
41
lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test
Normal 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 ''
|
||||
@@ -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
|
||||
@@ -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 ''
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user