Skip to content

[lldb] Add frame recognizers for libc++ std::invoke #105695

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
merged 7 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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: 13 additions & 1 deletion lldb/include/lldb/Target/StackFrameRecognizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,30 @@ class ScriptedStackFrameRecognizer : public StackFrameRecognizer {
/// Class that provides a registry of known stack frame recognizers.
class StackFrameRecognizerManager {
public:
/// Add a new recognizer that triggers on a given symbol name.
///
/// \param symbol_mangling controls whether the symbol name should be
/// compared to the mangled or demangled name.
void AddRecognizer(lldb::StackFrameRecognizerSP recognizer,
ConstString module, llvm::ArrayRef<ConstString> symbols,
Mangled::NamePreference symbol_mangling,
bool first_instruction_only = true);

/// Add a new recognizer that triggers on a symbol regex.
///
/// \param symbol_mangling controls whether the regex should apply
/// to the mangled or demangled name.
void AddRecognizer(lldb::StackFrameRecognizerSP recognizer,
lldb::RegularExpressionSP module,
lldb::RegularExpressionSP symbol,
Mangled::NamePreference symbol_mangling,
bool first_instruction_only = true);

void ForEach(std::function<
void(uint32_t recognizer_id, std::string recognizer_name,
std::string module, llvm::ArrayRef<ConstString> symbols,
bool regexp)> const &callback);
Mangled::NamePreference name_reference, bool regexp)> const
&callback);

bool RemoveRecognizerWithID(uint32_t recognizer_id);

Expand All @@ -142,6 +153,7 @@ class StackFrameRecognizerManager {
lldb::RegularExpressionSP module_regexp;
std::vector<ConstString> symbols;
lldb::RegularExpressionSP symbol_regexp;
Mangled::NamePreference symbol_mangling;
bool first_instruction_only;
};

Expand Down
68 changes: 44 additions & 24 deletions lldb/source/Commands/CommandObjectFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,7 @@ class CommandObjectFrameDiagnose : public CommandObjectParsed {
// We've already handled the case where the value object sp is null, so
// this is just to make sure future changes don't skip that:
assert(valobj_sp.get() && "Must have a valid ValueObject to print");
ValueObjectPrinter printer(*valobj_sp, &result.GetOutputStream(),
options);
ValueObjectPrinter printer(*valobj_sp, &result.GetOutputStream(), options);
if (llvm::Error error = printer.PrintValueObject())
result.AppendError(toString(std::move(error)));
}
Expand Down Expand Up @@ -899,13 +898,16 @@ void CommandObjectFrameRecognizerAdd::DoExecute(Args &command,
auto func =
RegularExpressionSP(new RegularExpression(m_options.m_symbols.front()));
GetTarget().GetFrameRecognizerManager().AddRecognizer(
recognizer_sp, module, func, m_options.m_first_instruction_only);
recognizer_sp, module, func, Mangled::NamePreference::ePreferDemangled,
m_options.m_first_instruction_only);
} else {
auto module = ConstString(m_options.m_module);
std::vector<ConstString> symbols(m_options.m_symbols.begin(),
m_options.m_symbols.end());
GetTarget().GetFrameRecognizerManager().AddRecognizer(
recognizer_sp, module, symbols, m_options.m_first_instruction_only);
recognizer_sp, module, symbols,
Mangled::NamePreference::ePreferDemangled,
m_options.m_first_instruction_only);
}
#endif

Expand All @@ -927,6 +929,34 @@ class CommandObjectFrameRecognizerClear : public CommandObjectParsed {
}
};

static void
PrintRecognizerDetails(Stream &strm, const std::string &name,
const std::string &module,
llvm::ArrayRef<lldb_private::ConstString> symbols,
Mangled::NamePreference symbol_mangling, bool regexp) {
strm << name << ", ";

if (!module.empty())
strm << "module " << module << ", ";

switch (symbol_mangling) {
case Mangled::NamePreference ::ePreferMangled:
strm << "mangled symbol ";
break;
case Mangled::NamePreference ::ePreferDemangled:
strm << "demangled symbol ";
break;
case Mangled::NamePreference ::ePreferDemangledWithoutArguments:
strm << "demangled (no args) symbol ";
break;
}

if (regexp)
strm << "regex ";

llvm::interleaveComma(symbols, strm);
}

class CommandObjectFrameRecognizerDelete : public CommandObjectParsed {
public:
CommandObjectFrameRecognizerDelete(CommandInterpreter &interpreter)
Expand All @@ -947,19 +977,13 @@ class CommandObjectFrameRecognizerDelete : public CommandObjectParsed {
GetTarget().GetFrameRecognizerManager().ForEach(
[&request](uint32_t rid, std::string rname, std::string module,
llvm::ArrayRef<lldb_private::ConstString> symbols,
bool regexp) {
Mangled::NamePreference symbol_mangling, bool regexp) {
StreamString strm;
if (rname.empty())
rname = "(internal)";

strm << rname;
if (!module.empty())
strm << ", module " << module;
if (!symbols.empty())
for (auto &symbol : symbols)
strm << ", symbol " << symbol;
if (regexp)
strm << " (regexp)";
PrintRecognizerDetails(strm, rname, module, symbols, symbol_mangling,
regexp);

request.TryCompleteCurrentArg(std::to_string(rid), strm.GetString());
});
Expand Down Expand Up @@ -1016,22 +1040,18 @@ class CommandObjectFrameRecognizerList : public CommandObjectParsed {
void DoExecute(Args &command, CommandReturnObject &result) override {
bool any_printed = false;
GetTarget().GetFrameRecognizerManager().ForEach(
[&result, &any_printed](
uint32_t recognizer_id, std::string name, std::string module,
llvm::ArrayRef<ConstString> symbols, bool regexp) {
[&result,
&any_printed](uint32_t recognizer_id, std::string name,
std::string module, llvm::ArrayRef<ConstString> symbols,
Mangled::NamePreference symbol_mangling, bool regexp) {
Stream &stream = result.GetOutputStream();

if (name.empty())
name = "(internal)";

stream << std::to_string(recognizer_id) << ": " << name;
if (!module.empty())
stream << ", module " << module;
if (!symbols.empty())
for (auto &symbol : symbols)
stream << ", symbol " << symbol;
if (regexp)
stream << " (regexp)";
stream << std::to_string(recognizer_id) << ": ";
PrintRecognizerDetails(stream, name, module, symbols, symbol_mangling,
regexp);

stream.EOL();
stream.Flush();
Expand Down
2 changes: 1 addition & 1 deletion lldb/source/Commands/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1049,7 +1049,7 @@ let Command = "thread backtrace" in {
def thread_backtrace_extended : Option<"extended", "e">, Group<1>,
Arg<"Boolean">, Desc<"Show the extended backtrace, if available">;
def thread_backtrace_unfiltered : Option<"unfiltered", "u">, Group<1>,
Desc<"Filter out frames according to installed frame recognizers">;
Desc<"Do not filter out frames according to installed frame recognizers">;
Copy link
Member

Choose a reason for hiding this comment

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

Can just push this as an NFC separately

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't have direct push-access and piggy-backing it in this PR is less effort for me

}

let Command = "thread step scope" in {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include <cstring>
#include <iostream>

#include <memory>

Expand Down Expand Up @@ -44,7 +45,7 @@ char CPPLanguageRuntime::ID = 0;
/// A frame recognizer that is installed to hide libc++ implementation
/// details from the backtrace.
class LibCXXFrameRecognizer : public StackFrameRecognizer {
RegularExpression m_hidden_function_regex;
std::array<RegularExpression, 4> m_hidden_regex;
RecognizedStackFrameSP m_hidden_frame;

struct LibCXXHiddenFrame : public RecognizedStackFrame {
Expand All @@ -53,10 +54,39 @@ class LibCXXFrameRecognizer : public StackFrameRecognizer {

public:
LibCXXFrameRecognizer()
: m_hidden_function_regex(
R"(^std::__.*::(__function.*::operator\(\)|__invoke))"
R"((\[.*\])?)" // ABI tag.
R"(( const)?$)"), // const.
: m_hidden_regex{
// internal implementation details of std::function
// std::__1::__function::__alloc_func<void (*)(), std::__1::allocator<void (*)()>, void ()>::operator()[abi:ne200000]
// std::__1::__function::__func<void (*)(), std::__1::allocator<void (*)()>, void ()>::operator()
// std::__1::__function::__value_func<void ()>::operator()[abi:ne200000]() const
RegularExpression{""
R"(^std::__[^:]*::)" // Namespace.
R"(__function::.*::operator\(\))"
R"((\[.*\])?)" // ABI tag.
R"(( const)?$)"}, // const.
// internal implementation details of std::function in ABI v2
// std::__2::__function::__policy_invoker<void (int, int)>::__call_impl[abi:ne200000]<std::__2::__function::__default_alloc_func<int (*)(int, int), int (int, int)>>
RegularExpression{""
R"(^std::__[^:]*::)" // Namespace.
R"(__function::.*::__call_impl)"
R"((\[.*\])?)" // ABI tag.
R"(<.*>)"}, // template argument.
// internal implementation details of std::invoke
// std::__1::__invoke[abi:ne200000]<void (*&)()>
RegularExpression{
R"(^std::__[^:]*::)" // Namespace.
R"(__invoke)"
Copy link
Member Author

Choose a reason for hiding this comment

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

I wonder if we should simply hide everything starting with ^std::__[0-9]*::__.*. Or are there any __ functions in libc++ which are not implementation details and should be shown in stacktraces by default?

Copy link
Member

Choose a reason for hiding this comment

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

Agreed. Don't think we want to maintain this list.

(also, side-note, I think matching on const and abi_tag is probably redundant. Not a concern if we just filter out all the std::__*::__ stuff).

Copy link
Member Author

Choose a reason for hiding this comment

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

I would like to defer switching to ^std::__[0-9]*::__.* in a follow-up commit, if that's fine by you.

I am pretty sure that this commit here works without unintended side effects, but I am not sure if ^std::__[0-9]*::__.* would regress debugability in unforeseen ways

Copy link
Member

@petrhosek petrhosek Aug 26, 2024

Choose a reason for hiding this comment

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

libc++ ABI namespace doesn't have to be just a number, it can be any string that starts with __. For example, in some of our Google projects we use __ktl and __pw; libFuzzer uses __Fuzzer in its internal libc++ build.

Copy link
Member Author

Choose a reason for hiding this comment

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

right. I posted this comment before I saw your fix on main. By now, the commit contains `^std::__[^:]::

R"((\[.*\])?)" // ABI tag.
R"(<.*>)"}, // template argument.
// internal implementation details of std::invoke
// std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ne200000]<void (*&)()>
RegularExpression{
R"(^std::__[^:]*::)" // Namespace.
R"(__invoke_void_return_wrapper<.*>::__call)"
R"((\[.*\])?)" // ABI tag.
R"(<.*>)"} // template argument.

},
m_hidden_frame(new LibCXXHiddenFrame()) {}

std::string GetName() override { return "libc++ frame recognizer"; }
Expand All @@ -69,8 +99,9 @@ class LibCXXFrameRecognizer : public StackFrameRecognizer {
if (!sc.function)
return {};

if (m_hidden_function_regex.Execute(sc.function->GetNameNoArguments()))
return m_hidden_frame;
for (RegularExpression &r : m_hidden_regex)
if (r.Execute(sc.function->GetNameNoArguments()))
return m_hidden_frame;

return {};
}
Expand All @@ -81,8 +112,9 @@ CPPLanguageRuntime::CPPLanguageRuntime(Process *process)
if (process)
process->GetTarget().GetFrameRecognizerManager().AddRecognizer(
StackFrameRecognizerSP(new LibCXXFrameRecognizer()), {},
std::make_shared<RegularExpression>("^std::__.*::"),
/*first_instruction_only*/ false);
std::make_shared<RegularExpression>("^std::__[^:]*::"),
/*mangling_preference=*/Mangled::ePreferDemangledWithoutArguments,
/*first_instruction_only=*/false);
}

bool CPPLanguageRuntime::IsAllowedRuntimeValue(ConstString name) {
Expand All @@ -108,8 +140,7 @@ bool contains_lambda_identifier(llvm::StringRef &str_ref) {

CPPLanguageRuntime::LibCppStdFunctionCallableInfo
line_entry_helper(Target &target, const SymbolContext &sc, Symbol *symbol,
llvm::StringRef first_template_param_sref,
bool has_invoke) {
llvm::StringRef first_template_param_sref, bool has_invoke) {

CPPLanguageRuntime::LibCppStdFunctionCallableInfo optional_info;

Expand Down Expand Up @@ -190,7 +221,7 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(
ValueObjectSP sub_member_f_(member_f_->GetChildMemberWithName("__f_"));

if (sub_member_f_)
member_f_ = sub_member_f_;
member_f_ = sub_member_f_;
}

if (!member_f_)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3465,6 +3465,6 @@ static void RegisterObjCExceptionRecognizer(Process *process) {

process->GetTarget().GetFrameRecognizerManager().AddRecognizer(
StackFrameRecognizerSP(new ObjCExceptionThrowFrameRecognizer()),
module.GetFilename(), symbols,
module.GetFilename(), symbols, Mangled::NamePreference::ePreferDemangled,
/*first_instruction_only*/ true);
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ void RegisterAbortWithPayloadFrameRecognizer(Process *process) {
return;

process->GetTarget().GetFrameRecognizerManager().AddRecognizer(
std::make_shared<AbortWithPayloadFrameRecognizer>(), module_name,
sym_name, /*first_instruction_only*/ false);
std::make_shared<AbortWithPayloadFrameRecognizer>(), module_name,
sym_name, Mangled::NamePreference::ePreferDemangled,
/*first_instruction_only*/ false);
}

RecognizedStackFrameSP
Expand All @@ -55,7 +56,7 @@ AbortWithPayloadFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) {
static constexpr llvm::StringLiteral info_key("abort_with_payload");

Log *log = GetLog(LLDBLog::SystemRuntime);

if (!frame_sp) {
LLDB_LOG(log, "abort_with_payload recognizer: invalid frame.");
return {};
Expand Down Expand Up @@ -196,8 +197,8 @@ AbortWithPayloadFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) {
abort_dict_sp->AddStringItem(reason_key, reason_string);
abort_dict_sp->AddIntegerItem(flags_key, flags_val);

// This will overwrite the abort_with_payload information in the dictionary
// already. But we can only crash on abort_with_payload once, so that
// This will overwrite the abort_with_payload information in the dictionary
// already. But we can only crash on abort_with_payload once, so that
// shouldn't matter.
process->GetExtendedCrashInfoDict()->AddItem(info_key, abort_dict_sp);

Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Target/AssertFrameRecognizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ void RegisterAssertFrameRecognizer(Process *process) {
target.GetFrameRecognizerManager().AddRecognizer(
std::make_shared<AssertFrameRecognizer>(),
location.module_spec.GetFilename(), location.symbols,
Mangled::ePreferDemangled,
/*first_instruction_only*/ false);
return;
}
Expand All @@ -112,6 +113,7 @@ void RegisterAssertFrameRecognizer(Process *process) {
std::make_shared<AssertFrameRecognizer>(),
std::make_shared<RegularExpression>(std::move(module_re)),
std::make_shared<RegularExpression>(std::move(symbol_re)),
Mangled::ePreferDemangled,
/*first_instruction_only*/ false);
}

Expand Down
25 changes: 16 additions & 9 deletions lldb/source/Target/StackFrameRecognizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,25 +62,29 @@ void StackFrameRecognizerManager::BumpGeneration() {

void StackFrameRecognizerManager::AddRecognizer(
StackFrameRecognizerSP recognizer, ConstString module,
llvm::ArrayRef<ConstString> symbols, bool first_instruction_only) {
llvm::ArrayRef<ConstString> symbols,
Mangled::NamePreference symbol_mangling, bool first_instruction_only) {
m_recognizers.push_front({(uint32_t)m_recognizers.size(), recognizer, false,
module, RegularExpressionSP(), symbols,
RegularExpressionSP(), first_instruction_only});
RegularExpressionSP(), symbol_mangling,
first_instruction_only});
BumpGeneration();
}

void StackFrameRecognizerManager::AddRecognizer(
StackFrameRecognizerSP recognizer, RegularExpressionSP module,
RegularExpressionSP symbol, bool first_instruction_only) {
RegularExpressionSP symbol, Mangled::NamePreference symbol_mangling,
bool first_instruction_only) {
m_recognizers.push_front({(uint32_t)m_recognizers.size(), recognizer, true,
ConstString(), module, std::vector<ConstString>(),
symbol, first_instruction_only});
symbol, symbol_mangling, first_instruction_only});
BumpGeneration();
}

void StackFrameRecognizerManager::ForEach(
const std::function<void(uint32_t, std::string, std::string,
llvm::ArrayRef<ConstString>, bool)> &callback) {
const std::function<
void(uint32_t, std::string, std::string, llvm::ArrayRef<ConstString>,
Mangled::NamePreference name_reference, bool)> &callback) {
for (auto entry : m_recognizers) {
if (entry.is_regexp) {
std::string module_name;
Expand All @@ -92,11 +96,13 @@ void StackFrameRecognizerManager::ForEach(
symbol_name = entry.symbol_regexp->GetText().str();

callback(entry.recognizer_id, entry.recognizer->GetName(), module_name,
llvm::ArrayRef(ConstString(symbol_name)), true);
llvm::ArrayRef(ConstString(symbol_name)), entry.symbol_mangling,
true);

} else {
callback(entry.recognizer_id, entry.recognizer->GetName(),
entry.module.GetCString(), entry.symbols, false);
entry.module.GetCString(), entry.symbols, entry.symbol_mangling,
false);
}
}
}
Expand Down Expand Up @@ -125,7 +131,6 @@ StackFrameRecognizerSP
StackFrameRecognizerManager::GetRecognizerForFrame(StackFrameSP frame) {
const SymbolContext &symctx = frame->GetSymbolContext(
eSymbolContextModule | eSymbolContextFunction | eSymbolContextSymbol);
ConstString function_name = symctx.GetFunctionName();
ModuleSP module_sp = symctx.module_sp;
if (!module_sp)
return StackFrameRecognizerSP();
Expand All @@ -145,6 +150,8 @@ StackFrameRecognizerManager::GetRecognizerForFrame(StackFrameSP frame) {
if (!entry.module_regexp->Execute(module_name.GetStringRef()))
continue;

ConstString function_name = symctx.GetFunctionName(entry.symbol_mangling);

if (!entry.symbols.empty())
if (!llvm::is_contained(entry.symbols, function_name))
continue;
Expand Down
Loading
Loading