-
Notifications
You must be signed in to change notification settings - Fork 12k
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
[lldb] Extend frame recognizers to hide frames from backtraces #104523
Conversation
@llvm/pr-subscribers-lldb Author: Adrian Prantl (adrian-prantl) ChangesCompilers and language runtimes often use helper functions that are fundamentally uninteresting when debugging anything but the compiler/runtime itself. This patch introduces a user-extensible mechanism that allows for these frames to be hidden from backtraces and automatically skipped over when navigating the stack with This does not affect the numbering of frames, so My primary motivation for this feature is to hide thunks in the Swift programming language, but I'm including an example recognizer for rdar://126629381 Example output. (Yes, my proof-of-concept recognizer could hide even more frames if we had a method that returned the function name without the return type or I used something that isn't based off regex, but it's really only meant as an example). before:
after
Patch is 24.25 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/104523.diff 17 Files Affected:
diff --git a/lldb/include/lldb/Target/StackFrameList.h b/lldb/include/lldb/Target/StackFrameList.h
index 88e211ff692bd9..5cdca97e910613 100644
--- a/lldb/include/lldb/Target/StackFrameList.h
+++ b/lldb/include/lldb/Target/StackFrameList.h
@@ -91,7 +91,7 @@ class StackFrameList {
size_t GetStatus(Stream &strm, uint32_t first_frame, uint32_t num_frames,
bool show_frame_info, uint32_t num_frames_with_source,
- bool show_unique = false,
+ bool show_unique = false, bool should_filter = true,
const char *frame_marker = nullptr);
protected:
diff --git a/lldb/include/lldb/Target/StackFrameRecognizer.h b/lldb/include/lldb/Target/StackFrameRecognizer.h
index e9ac2750192ef6..dae1c6f4415718 100644
--- a/lldb/include/lldb/Target/StackFrameRecognizer.h
+++ b/lldb/include/lldb/Target/StackFrameRecognizer.h
@@ -28,20 +28,23 @@ namespace lldb_private {
/// This class provides extra information about a stack frame that was
/// provided by a specific stack frame recognizer. Right now, this class only
/// holds recognized arguments (via GetRecognizedArguments).
-
class RecognizedStackFrame
: public std::enable_shared_from_this<RecognizedStackFrame> {
public:
+ virtual ~RecognizedStackFrame() = default;
+
virtual lldb::ValueObjectListSP GetRecognizedArguments() {
return m_arguments;
}
virtual lldb::ValueObjectSP GetExceptionObject() {
return lldb::ValueObjectSP();
}
- virtual lldb::StackFrameSP GetMostRelevantFrame() { return nullptr; };
- virtual ~RecognizedStackFrame() = default;
+ virtual lldb::StackFrameSP GetMostRelevantFrame() { return nullptr; }
std::string GetStopDescription() { return m_stop_desc; }
+ /// Controls whether this frame should be filtered out when
+ /// displaying backtraces, for example.
+ virtual bool ShouldHide() { return false; }
protected:
lldb::ValueObjectListSP m_arguments;
@@ -53,7 +56,6 @@ class RecognizedStackFrame
/// A base class for frame recognizers. Subclasses (actual frame recognizers)
/// should implement RecognizeFrame to provide a RecognizedStackFrame for a
/// given stack frame.
-
class StackFrameRecognizer
: public std::enable_shared_from_this<StackFrameRecognizer> {
public:
@@ -73,7 +75,6 @@ class StackFrameRecognizer
/// Python implementation for frame recognizers. An instance of this class
/// tracks a particular Python classobject, which will be asked to recognize
/// stack frames.
-
class ScriptedStackFrameRecognizer : public StackFrameRecognizer {
lldb_private::ScriptInterpreter *m_interpreter;
lldb_private::StructuredData::ObjectSP m_python_object_sp;
@@ -144,7 +145,6 @@ class StackFrameRecognizerManager {
/// ValueObject subclass that presents the passed ValueObject as a recognized
/// value with the specified ValueType. Frame recognizers should return
/// instances of this class as the returned objects in GetRecognizedArguments().
-
class ValueObjectRecognizerSynthesizedValue : public ValueObject {
public:
static lldb::ValueObjectSP Create(ValueObject &parent, lldb::ValueType type) {
diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index aacc59c292ec79..9de58513b160b4 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -1128,11 +1128,12 @@ class Thread : public std::enable_shared_from_this<Thread>,
size_t GetStatus(Stream &strm, uint32_t start_frame, uint32_t num_frames,
uint32_t num_frames_with_source, bool stop_format,
- bool only_stacks = false);
+ bool should_filter, bool only_stacks = false);
size_t GetStackFrameStatus(Stream &strm, uint32_t first_frame,
uint32_t num_frames, bool show_frame_info,
- uint32_t num_frames_with_source);
+ uint32_t num_frames_with_source,
+ bool should_filter);
// We need a way to verify that even though we have a thread in a shared
// pointer that the object itself is still valid. Currently this won't be the
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index bda981041064ff..7a3d6ff9336159 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -1208,7 +1208,8 @@ bool SBThread::GetStatus(SBStream &status) const {
ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
if (exe_ctx.HasThreadScope()) {
- exe_ctx.GetThreadPtr()->GetStatus(strm, 0, 1, 1, true);
+ exe_ctx.GetThreadPtr()->GetStatus(strm, 0, 1, 1, true,
+ /*should_filter*/ false);
} else
strm.PutCString("No status");
diff --git a/lldb/source/Commands/CommandCompletions.cpp b/lldb/source/Commands/CommandCompletions.cpp
index 54f4b368166492..ea647bbfe4c026 100644
--- a/lldb/source/Commands/CommandCompletions.cpp
+++ b/lldb/source/Commands/CommandCompletions.cpp
@@ -791,7 +791,7 @@ void CommandCompletions::ThreadIndexes(CommandInterpreter &interpreter,
lldb::ThreadSP thread_sp;
for (uint32_t idx = 0; (thread_sp = threads.GetThreadAtIndex(idx)); ++idx) {
StreamString strm;
- thread_sp->GetStatus(strm, 0, 1, 1, true);
+ thread_sp->GetStatus(strm, 0, 1, 1, true, /*should_filter*/ false);
request.TryCompleteCurrentArg(std::to_string(thread_sp->GetIndexID()),
strm.GetString());
}
@@ -835,7 +835,7 @@ void CommandCompletions::ThreadIDs(CommandInterpreter &interpreter,
lldb::ThreadSP thread_sp;
for (uint32_t idx = 0; (thread_sp = threads.GetThreadAtIndex(idx)); ++idx) {
StreamString strm;
- thread_sp->GetStatus(strm, 0, 1, 1, true);
+ thread_sp->GetStatus(strm, 0, 1, 1, true, /*should_filter*/ false);
request.TryCompleteCurrentArg(std::to_string(thread_sp->GetID()),
strm.GetString());
}
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index 29e460fe3885ff..b93ffe27bb2f29 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -278,6 +278,30 @@ class CommandObjectFrameSelect : public CommandObjectParsed {
if (frame_idx == UINT32_MAX)
frame_idx = 0;
+ // If moving up/down by one, skip over hidden frames.
+ if (*m_options.relative_frame_offset == 1 ||
+ *m_options.relative_frame_offset == -1) {
+ uint32_t candidate_idx = frame_idx;
+ for (unsigned num_try = 0; num_try < 12; ++num_try) {
+ if (candidate_idx == 0 && *m_options.relative_frame_offset == -1) {
+ candidate_idx = UINT32_MAX;
+ break;
+ }
+ candidate_idx += *m_options.relative_frame_offset;
+ if (auto candidate_sp = thread->GetStackFrameAtIndex(candidate_idx)) {
+ if (auto recognized_frame_sp = candidate_sp->GetRecognizedFrame())
+ if (recognized_frame_sp->ShouldHide())
+ continue;
+ // Now candidate_idx is the first non-hidden frame.
+ break;
+ }
+ candidate_idx = UINT32_MAX;
+ break;
+ };
+ if (candidate_idx != UINT32_MAX)
+ m_options.relative_frame_offset = candidate_idx - frame_idx;
+ }
+
if (*m_options.relative_frame_offset < 0) {
if (static_cast<int32_t>(frame_idx) >=
-*m_options.relative_frame_offset)
@@ -318,7 +342,7 @@ class CommandObjectFrameSelect : public CommandObjectParsed {
}
}
}
- } else {
+ } else {
if (command.GetArgumentCount() > 1) {
result.AppendErrorWithFormat(
"too many arguments; expected frame-index, saw '%s'.\n",
@@ -341,7 +365,7 @@ class CommandObjectFrameSelect : public CommandObjectParsed {
frame_idx = 0;
}
}
- }
+ }
bool success = thread->SetSelectedFrameByIndexNoisily(
frame_idx, result.GetOutputStream());
diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp
index 137b1ad981073c..baf5d9196e553e 100644
--- a/lldb/source/Commands/CommandObjectMemory.cpp
+++ b/lldb/source/Commands/CommandObjectMemory.cpp
@@ -1570,7 +1570,8 @@ class CommandObjectMemoryHistory : public CommandObjectParsed {
const bool stop_format = false;
for (auto thread : thread_list) {
- thread->GetStatus(*output_stream, 0, UINT32_MAX, 0, stop_format);
+ thread->GetStatus(*output_stream, 0, UINT32_MAX, 0, stop_format,
+ /*should_filter*/ false);
}
result.SetStatus(eReturnStatusSuccessFinishResult);
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index 605f872a9f45e1..d58c8533f90f7b 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -80,14 +80,20 @@ class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads {
"invalid integer value for option '%c': %s", short_option,
option_arg.data());
break;
- case 'e': {
+ case 'e':
+ case 'f': {
bool success;
- m_extended_backtrace =
- OptionArgParser::ToBoolean(option_arg, false, &success);
- if (!success)
+ bool value = OptionArgParser::ToBoolean(option_arg, false, &success);
+ if (!success) {
error.SetErrorStringWithFormat(
"invalid boolean value for option '%c': %s", short_option,
option_arg.data());
+ break;
+ }
+ if (short_option == 'e')
+ m_extended_backtrace = value;
+ else
+ m_filtered_backtrace = value;
} break;
default:
llvm_unreachable("Unimplemented option");
@@ -99,6 +105,7 @@ class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads {
m_count = UINT32_MAX;
m_start = 0;
m_extended_backtrace = false;
+ m_filtered_backtrace = true;
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -109,6 +116,7 @@ class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads {
uint32_t m_count;
uint32_t m_start;
bool m_extended_backtrace;
+ bool m_filtered_backtrace;
};
CommandObjectThreadBacktrace(CommandInterpreter &interpreter)
@@ -199,7 +207,8 @@ class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads {
strm.PutChar('\n');
if (ext_thread_sp->GetStatus(strm, m_options.m_start,
m_options.m_count,
- num_frames_with_source, stop_format)) {
+ num_frames_with_source, stop_format,
+ m_options.m_filtered_backtrace)) {
DoExtendedBacktrace(ext_thread_sp.get(), result);
}
}
@@ -228,7 +237,8 @@ class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads {
const uint32_t num_frames_with_source = 0;
const bool stop_format = true;
if (!thread->GetStatus(strm, m_options.m_start, m_options.m_count,
- num_frames_with_source, stop_format, only_stacks)) {
+ num_frames_with_source, stop_format,
+ m_options.m_filtered_backtrace, only_stacks)) {
result.AppendErrorWithFormat(
"error displaying backtrace for thread: \"0x%4.4x\"\n",
thread->GetIndexID());
@@ -1392,7 +1402,8 @@ class CommandObjectThreadException : public CommandObjectIterateOverThreads {
const uint32_t num_frames_with_source = 0;
const bool stop_format = false;
exception_thread_sp->GetStatus(strm, 0, UINT32_MAX,
- num_frames_with_source, stop_format);
+ num_frames_with_source, stop_format,
+ /*filtered*/ false);
}
return true;
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index f050cd2ebb5ae0..a59c3c18918115 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1048,6 +1048,9 @@ let Command = "thread backtrace" in {
Arg<"FrameIndex">, Desc<"Frame in which to start the backtrace">;
def thread_backtrace_extended : Option<"extended", "e">, Group<1>,
Arg<"Boolean">, Desc<"Show the extended backtrace, if available">;
+ def thread_backtrace_full : Option<"filtered", "f">, Group<1>,
+ Arg<"Boolean">,
+ Desc<"Filter out frames according to installed frame recognizers">;
}
let Command = "thread step scope" in {
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 309e01e456580c..d9f1fb882fb105 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -1869,7 +1869,8 @@ void Debugger::HandleThreadEvent(const EventSP &event_sp) {
ThreadSP thread_sp(
Thread::ThreadEventData::GetThreadFromEvent(event_sp.get()));
if (thread_sp) {
- thread_sp->GetStatus(*GetAsyncOutputStream(), 0, 1, 1, stop_format);
+ thread_sp->GetStatus(*GetAsyncOutputStream(), 0, 1, 1, stop_format,
+ /*should_filter*/ false);
}
}
}
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
index c7202a47d0157e..209b971b2f2ade 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
@@ -26,6 +26,7 @@
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StackFrameRecognizer.h"
#include "lldb/Target/ThreadPlanRunToAddress.h"
#include "lldb/Target/ThreadPlanStepInRange.h"
#include "lldb/Utility/Timer.h"
@@ -40,8 +41,52 @@ static ConstString g_coro_frame = ConstString("__coro_frame");
char CPPLanguageRuntime::ID = 0;
+/// A frame recognizer that is isntalled to hide libc++ implementation
+/// details from the backtrace.
+class LibCXXFrameRecognizer : public StackFrameRecognizer {
+ RegularExpression m_hidden_function_regex;
+ RecognizedStackFrameSP m_hidden_frame;
+
+ struct LibCXXHiddenFrame : public RecognizedStackFrame {
+ bool ShouldHide() override { return true; }
+ };
+
+public:
+ LibCXXFrameRecognizer()
+ : m_hidden_function_regex(
+ R"(^std::__1::(__function.*::operator\(\)|__invoke))"
+ R"((\[.*\])?)" // ABI tag.
+ R"(( const)?$)"), // const.
+ m_hidden_frame(new LibCXXHiddenFrame()) {}
+
+ std::string GetName() override {
+ return "libc++ frame recognizer";
+ }
+
+ lldb::RecognizedStackFrameSP
+ RecognizeFrame(lldb::StackFrameSP frame_sp) override {
+ if (!frame_sp)
+ return {};
+ auto &sc =
+ frame_sp->GetSymbolContext(lldb::eSymbolContextFunction);
+ if (!sc.function)
+ return {};
+
+ if (m_hidden_function_regex.Execute(sc.function->GetNameNoArguments()))
+ return m_hidden_frame;
+
+ return {};
+ }
+};
+
CPPLanguageRuntime::CPPLanguageRuntime(Process *process)
- : LanguageRuntime(process) {}
+ : LanguageRuntime(process) {
+ if (process)
+ process->GetTarget().GetFrameRecognizerManager().AddRecognizer(
+ StackFrameRecognizerSP(new LibCXXFrameRecognizer()), {},
+ std::make_shared<RegularExpression>("^std::__1::"),
+ /*first_instruction_only*/ false);
+}
bool CPPLanguageRuntime::IsAllowedRuntimeValue(ConstString name) {
return name == g_this || name == g_promise || name == g_coro_frame;
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index e3c4f2ee398cc4..d7efa8267436fd 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -5545,7 +5545,8 @@ Process::RunThreadPlan(ExecutionContext &exe_ctx,
// Print a backtrace into the log so we can figure out where we are:
StreamString s;
s.PutCString("Thread state after unsuccessful completion: \n");
- thread->GetStackFrameStatus(s, 0, UINT32_MAX, true, UINT32_MAX);
+ thread->GetStackFrameStatus(s, 0, UINT32_MAX, true, UINT32_MAX,
+ /*should_filter*/ false);
log->PutString(s.GetString());
}
// Restore the thread state if we are going to discard the plan execution.
@@ -5819,8 +5820,8 @@ size_t Process::GetThreadStatus(Stream &strm,
continue;
}
thread_sp->GetStatus(strm, start_frame, num_frames,
- num_frames_with_source,
- stop_format);
+ num_frames_with_source, stop_format,
+ /*should_filter*/ num_frames > 1);
++num_thread_infos_dumped;
} else {
Log *log = GetLog(LLDBLog::Process);
diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp
index 0cf9ce1bf043f5..2f284d32ce77ac 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -924,7 +924,7 @@ StackFrameList::GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr) {
size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
uint32_t num_frames, bool show_frame_info,
uint32_t num_frames_with_source,
- bool show_unique,
+ bool show_unique, bool should_filter,
const char *selected_frame_marker) {
size_t num_frames_displayed = 0;
@@ -950,8 +950,8 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
buffer.insert(buffer.begin(), len, ' ');
unselected_marker = buffer.c_str();
}
+ bool filtered = false;
const char *marker = nullptr;
-
for (frame_idx = first_frame; frame_idx < last_frame; ++frame_idx) {
frame_sp = GetFrameAtIndex(frame_idx);
if (!frame_sp)
@@ -963,6 +963,15 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
else
marker = unselected_marker;
}
+
+ // Hide uninteresting frames unless it's the selected frame.
+ if (should_filter && frame_sp != selected_frame_sp)
+ if (auto recognized_frame_sp = frame_sp->GetRecognizedFrame())
+ if (recognized_frame_sp->ShouldHide()) {
+ filtered = true;
+ continue;
+ }
+
// Check for interruption here. If we're fetching arguments, this loop
// can go slowly:
Debugger &dbg = m_thread.GetProcess()->GetTarget().GetDebugger();
@@ -979,6 +988,8 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
++num_frames_displayed;
}
+ if (filtered)
+ strm << "Note: Some frames were hidden by frame recognizers\n";
strm.IndentLess();
return num_frames_displayed;
}
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index 74d1a268c6dffb..bedb89c72dd433 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -1748,7 +1748,8 @@ std::string Thread::RunModeAsString(lldb::RunMode mode) {
size_t Thread::GetStatus(Stream &strm, uint32_t start_frame,
uint32_t num_frames, uint32_t num_frames_with_source,
- bool stop_format, bool only_stacks) {
+ bool stop_format, bool should_filter,
+ bool only_stacks) {
if (!only_stacks) {
ExecutionContext exe_ctx(shared_from_this());
@@ -1795,7 +1796,7 @@ size_t Thread::GetStatus(Stream &strm, uint32_t start_frame,
num_frames_shown = GetStackFrameList()->GetStatus(
strm, start_frame, num_frames, show_frame_info, num_frames_with_source,
- show_frame_unique, selected_frame_marker);
+ show_frame_unique, should_filter, selected_frame_marker);
if (num_frames == 1)
strm.IndentLess();
strm.IndentLess();
@@ -1893,9 +1894,11 @@ bool Thread::GetDescription(Stream &strm, lldb::DescriptionLevel level,
size_t Thread::GetStackFrameStatus(Stream &strm, uint32_t first_frame,
uint32_t num_frames, bool show...
[truncated]
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
✅ With the latest revision this PR passed the Python code formatter. |
6289b79
to
66292a2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation is fine, but I have some questions about this. First, SBFrame should have IsImplementationFrame or whatever terminology we use for the user view of this, like we have IsInlined and IsArtificial today so a UI can choose to omit these stack frames too.
lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
Outdated
Show resolved
Hide resolved
public: | ||
LibCXXFrameRecognizer() | ||
: m_hidden_function_regex( | ||
R"(^std::__1::(__function.*::operator\(\)|__invoke))" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be too ugly, but sometimes when I have a regex table, I like to include an example of a string each one is meant to match against. This first one I can get, but the next two I'm not sure what names we're matching. This mostly reflects me not paying attention to libc++ internal method names as I skip past them in backtraces, not necessarily important.
@@ -979,6 +988,8 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame, | |||
++num_frames_displayed; | |||
} | |||
|
|||
if (filtered) | |||
strm << "Note: Some frames were hidden by frame recognizers\n"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we exposing the term "frame recognizers" to users intentionally? Could we express this as "Language implementation stack frames hidden"? I'm not convinced this is better, just asking, a lot of our errors and warning messages use lldb-internal terminology and I don't think it's helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a frame recognizer
multiword command so I think it's fine to expose it to the user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is intentional. Frame recognizers are visible to users and can (in theory) be extended via scripting:
(lldb) help frame recognizer
Commands for editing and viewing frame recognizers.
Syntax: frame recognizer [<sub-command-options>]
The following subcommands are supported:
add -- Add a new frame recognizer.
clear -- Delete all frame recognizers.
delete -- Delete an existing frame recognizer by id.
info -- Show which frame recognizer is applied a stack frame (if any).
list -- Show a list of active frame recognizers.
For more help on any particular subcommand, type 'help <command> <subcommand>'.
Ah sorry was still writing and posted by accident. For stepping we have a user exposed setting I don't know about printing the "Some frames were omitted" message by default when this takes place. Then again, I guess with the recognizers we're talking about here with C++ it will not be common, and so the one backtrace during your session where there's std::function stuff on the stack and it's omitted, it wouldn't be noisy. I don't know how frequent you think these recognizers might match for Swift code. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a pretty cool idea but can we make sure it works using python frame recognizers. I believe the user should also have the ability to hide certain frames if they want, like it doesn't have to be system or language runtime frames.
lldb/source/Commands/Options.td
Outdated
@@ -1048,6 +1048,9 @@ let Command = "thread backtrace" in { | |||
Arg<"FrameIndex">, Desc<"Frame in which to start the backtrace">; | |||
def thread_backtrace_extended : Option<"extended", "e">, Group<1>, | |||
Arg<"Boolean">, Desc<"Show the extended backtrace, if available">; | |||
def thread_backtrace_full : Option<"filtered", "f">, Group<1>, | |||
Arg<"Boolean">, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arg<"Boolean">, |
If you make this a Boolean
you have to pass an argument to the option (thread backtrace -f true
). If you don't specify it, you don't have to parse the argument into a boolean SetOptionValue
, you just have to check the short option and set your flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that this is True by default. I guess I could negate the option? What's a good name -u
, --unfiltered
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: -a
, --all-frames
(like the option for ls
) and in the help for that option we could mention that it shows all the frames including the ones hidden by a recognizer.
This seems pretty cool! |
The idea is that users can write their own frame recognizers in Python (though I have not exposed this in this patch). I'll see if I can ad this!
Thinking of it, it is probably good enough to explain this in |
The current frame never gets filtered. If you select a filtered frame with |
Action items so far:
|
Should hidden frames also be skipped by |
When we were talking about this originally I thought that StackFrames would get an "ImplementationFrame" property, and we would use that to determine whether to hide the frame in bt (when not passing --unfiltered). Then the frame recognizers when they are run on the frame in the ordinary course of things (we do this to determine recognized args anyway) they would set the property on the StackFrame. Then StackFrameList could just check that property. |
BTW, we should definitely get StepOut to step out past hidden frames. That should be pretty easy to do. |
I understand that not recomputing them might be desirable, but this is not how recognizers currently work. The only place frame recognizers currently get called is by When a frame recognizer is registered it takes a module regex a function regex and a first_instruction flag. All three of these must match before the recognizer is executed. That's num_recognizers*num_frames regexes. That's not expensive in the grans scheme of things, but you're right that it's not necessary. I'll see if I can hide the recognizer invocation inside StackFrame::shouldHide() and let StackFrame cache the result instead of having the Recognizer modify the StackFrame. Maybe I can come up with some cache invalidation strategy that makes sense. |
6653321
to
8f6af48
Compare
I uploaded a new version of the patch that addresses all outstanding feedback, except for
|
@medismailben I copied&pasted the python interface code from the function above; could you review that code extra carefully? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo comment.
8f6af48
to
ece2b89
Compare
Implemented StepOut. |
ece2b89
to
81af101
Compare
: LanguageRuntime(process) { | ||
if (process) | ||
process->GetTarget().GetFrameRecognizerManager().AddRecognizer( | ||
StackFrameRecognizerSP(new LibCXXFrameRecognizer()), {}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: make_shared
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same problem, it's a subclass.
llvm#104523)" This reverts commit f01f80c. This commit introduces an msan violation. See the discussion on llvm#104523.
I reverted the reverts and pushed two commits to fix the missing initialization. |
…104523) Compilers and language runtimes often use helper functions that are fundamentally uninteresting when debugging anything but the compiler/runtime itself. This patch introduces a user-extensible mechanism that allows for these frames to be hidden from backtraces and automatically skipped over when navigating the stack with `up` and `down`. This does not affect the numbering of frames, so `f <N>` will still provide access to the hidden frames. The `bt` output will also print a hint that frames have been hidden. My primary motivation for this feature is to hide thunks in the Swift programming language, but I'm including an example recognizer for `std::function::operator()` that I wished for myself many times while debugging LLDB. rdar://126629381 Example output. (Yes, my proof-of-concept recognizer could hide even more frames if we had a method that returned the function name without the return type or I used something that isn't based off regex, but it's really only meant as an example). before: ``` (lldb) thread backtrace --filtered=false * thread swiftlang#1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10 frame swiftlang#1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25 frame swiftlang#2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12 frame swiftlang#3: 0x0000000100003968 a.out`std::__1::__function::__alloc_func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()[abi:se200000](this=0x000000016fdff280, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:171:12 frame swiftlang#4: 0x00000001000026bc a.out`std::__1::__function::__func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()(this=0x000000016fdff278, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:313:10 frame swiftlang#5: 0x0000000100003c38 a.out`std::__1::__function::__value_func<int (int, int)>::operator()[abi:se200000](this=0x000000016fdff278, __args=0x000000016fdff224, __args=0x000000016fdff220) const at function.h:430:12 frame swiftlang#6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10 frame swiftlang#7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10 frame swiftlang#8: 0x0000000183cdf154 dyld`start + 2476 (lldb) ``` after ``` (lldb) bt * thread swiftlang#1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10 frame swiftlang#1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25 frame swiftlang#2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12 frame swiftlang#6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10 frame swiftlang#7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10 frame swiftlang#8: 0x0000000183cdf154 dyld`start + 2476 Note: Some frames were hidden by frame recognizers ``` (cherry picked from commit f01f80c)
The issue was introduced in llvm#104523. The code introduces the `ret_val` variable but does not use it. Instead it returns a pointer, which gets implicitly converted to bool. (cherry picked from commit 6528157)
The libc++ issue I described earlier still hasn't been addressed so I'm going to revert the changes again... |
This is a fix forward for the issue introduced in #104523.
I attempted a fix forward in 68a1593 but the test is still failing, although with a slightly different error now:
|
Would be interesting if this is fixed by #105695. How can I run the test cases against libc++'s unstable ABI locally? |
Looks like libc++ uses a different layout for llvm-project/libcxx/include/__functional/function.h Lines 827 to 831 in 52ae891
|
Set |
I updated #105695 and locally verified that it now fixes the test case for libc++ ABI v2 |
This is entirely tangential to this patch, which looks fine to me, but one of the jobs of a Frame recognizer is to add arguments to a StackFrame that doesn't have them. That's way more intrusive than just setting a "ShouldHide" type flag...
|
…104523) Compilers and language runtimes often use helper functions that are fundamentally uninteresting when debugging anything but the compiler/runtime itself. This patch introduces a user-extensible mechanism that allows for these frames to be hidden from backtraces and automatically skipped over when navigating the stack with `up` and `down`. This does not affect the numbering of frames, so `f <N>` will still provide access to the hidden frames. The `bt` output will also print a hint that frames have been hidden. My primary motivation for this feature is to hide thunks in the Swift programming language, but I'm including an example recognizer for `std::function::operator()` that I wished for myself many times while debugging LLDB. rdar://126629381 Example output. (Yes, my proof-of-concept recognizer could hide even more frames if we had a method that returned the function name without the return type or I used something that isn't based off regex, but it's really only meant as an example). before: ``` (lldb) thread backtrace --filtered=false * thread llvm#1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10 frame llvm#1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25 frame llvm#2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12 frame llvm#3: 0x0000000100003968 a.out`std::__1::__function::__alloc_func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()[abi:se200000](this=0x000000016fdff280, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:171:12 frame llvm#4: 0x00000001000026bc a.out`std::__1::__function::__func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()(this=0x000000016fdff278, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:313:10 frame llvm#5: 0x0000000100003c38 a.out`std::__1::__function::__value_func<int (int, int)>::operator()[abi:se200000](this=0x000000016fdff278, __args=0x000000016fdff224, __args=0x000000016fdff220) const at function.h:430:12 frame llvm#6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10 frame llvm#7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10 frame llvm#8: 0x0000000183cdf154 dyld`start + 2476 (lldb) ``` after ``` (lldb) bt * thread llvm#1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10 frame llvm#1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25 frame llvm#2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12 frame llvm#6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10 frame llvm#7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10 frame llvm#8: 0x0000000183cdf154 dyld`start + 2476 Note: Some frames were hidden by frame recognizers ```
The issue was introduced in llvm#104523. The code introduces the `ret_val` variable but does not use it. Instead it returns a pointer, which gets implicitly converted to bool.
This reverts commit 6528157. I'm reverting llvm#104523 (llvm@f01f80c) and this fixup belongs to the same series of changes.
This reverts commit 6f45602, which depends on llvm#104523, which I'm reverting.
llvm#104523)" This reverts commit f01f80c. This commit introduces an msan violation. See the discussion on llvm#104523.
…acktraces (llvm#104523)"" This reverts commit 547917a.
This is a fix forward for the issue introduced in llvm#104523.
…104523) Compilers and language runtimes often use helper functions that are fundamentally uninteresting when debugging anything but the compiler/runtime itself. This patch introduces a user-extensible mechanism that allows for these frames to be hidden from backtraces and automatically skipped over when navigating the stack with `up` and `down`. This does not affect the numbering of frames, so `f <N>` will still provide access to the hidden frames. The `bt` output will also print a hint that frames have been hidden. My primary motivation for this feature is to hide thunks in the Swift programming language, but I'm including an example recognizer for `std::function::operator()` that I wished for myself many times while debugging LLDB. rdar://126629381 Example output. (Yes, my proof-of-concept recognizer could hide even more frames if we had a method that returned the function name without the return type or I used something that isn't based off regex, but it's really only meant as an example). before: ``` (lldb) thread backtrace --filtered=false * thread swiftlang#1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10 frame swiftlang#1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25 frame swiftlang#2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12 frame swiftlang#3: 0x0000000100003968 a.out`std::__1::__function::__alloc_func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()[abi:se200000](this=0x000000016fdff280, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:171:12 frame swiftlang#4: 0x00000001000026bc a.out`std::__1::__function::__func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()(this=0x000000016fdff278, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:313:10 frame swiftlang#5: 0x0000000100003c38 a.out`std::__1::__function::__value_func<int (int, int)>::operator()[abi:se200000](this=0x000000016fdff278, __args=0x000000016fdff224, __args=0x000000016fdff220) const at function.h:430:12 frame swiftlang#6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10 frame swiftlang#7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10 frame swiftlang#8: 0x0000000183cdf154 dyld`start + 2476 (lldb) ``` after ``` (lldb) bt * thread swiftlang#1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10 frame swiftlang#1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25 frame swiftlang#2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12 frame swiftlang#6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10 frame swiftlang#7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10 frame swiftlang#8: 0x0000000183cdf154 dyld`start + 2476 Note: Some frames were hidden by frame recognizers ``` (cherry picked from commit f01f80c)
The issue was introduced in llvm#104523. The code introduces the `ret_val` variable but does not use it. Instead it returns a pointer, which gets implicitly converted to bool. (cherry picked from commit 6528157)
…104523) Compilers and language runtimes often use helper functions that are fundamentally uninteresting when debugging anything but the compiler/runtime itself. This patch introduces a user-extensible mechanism that allows for these frames to be hidden from backtraces and automatically skipped over when navigating the stack with `up` and `down`. This does not affect the numbering of frames, so `f <N>` will still provide access to the hidden frames. The `bt` output will also print a hint that frames have been hidden. My primary motivation for this feature is to hide thunks in the Swift programming language, but I'm including an example recognizer for `std::function::operator()` that I wished for myself many times while debugging LLDB. rdar://126629381 Example output. (Yes, my proof-of-concept recognizer could hide even more frames if we had a method that returned the function name without the return type or I used something that isn't based off regex, but it's really only meant as an example). before: ``` (lldb) thread backtrace --filtered=false * thread swiftlang#1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10 frame swiftlang#1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25 frame swiftlang#2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12 frame swiftlang#3: 0x0000000100003968 a.out`std::__1::__function::__alloc_func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()[abi:se200000](this=0x000000016fdff280, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:171:12 frame swiftlang#4: 0x00000001000026bc a.out`std::__1::__function::__func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()(this=0x000000016fdff278, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:313:10 frame swiftlang#5: 0x0000000100003c38 a.out`std::__1::__function::__value_func<int (int, int)>::operator()[abi:se200000](this=0x000000016fdff278, __args=0x000000016fdff224, __args=0x000000016fdff220) const at function.h:430:12 frame swiftlang#6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10 frame swiftlang#7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10 frame swiftlang#8: 0x0000000183cdf154 dyld`start + 2476 (lldb) ``` after ``` (lldb) bt * thread swiftlang#1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10 frame swiftlang#1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25 frame swiftlang#2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12 frame swiftlang#6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10 frame swiftlang#7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10 frame swiftlang#8: 0x0000000183cdf154 dyld`start + 2476 Note: Some frames were hidden by frame recognizers ``` (cherry picked from commit f01f80c)
The issue was introduced in llvm#104523. The code introduces the `ret_val` variable but does not use it. Instead it returns a pointer, which gets implicitly converted to bool. (cherry picked from commit 6528157)
…104523) Compilers and language runtimes often use helper functions that are fundamentally uninteresting when debugging anything but the compiler/runtime itself. This patch introduces a user-extensible mechanism that allows for these frames to be hidden from backtraces and automatically skipped over when navigating the stack with `up` and `down`. This does not affect the numbering of frames, so `f <N>` will still provide access to the hidden frames. The `bt` output will also print a hint that frames have been hidden. My primary motivation for this feature is to hide thunks in the Swift programming language, but I'm including an example recognizer for `std::function::operator()` that I wished for myself many times while debugging LLDB. rdar://126629381 Example output. (Yes, my proof-of-concept recognizer could hide even more frames if we had a method that returned the function name without the return type or I used something that isn't based off regex, but it's really only meant as an example). before: ``` (lldb) thread backtrace --filtered=false * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10 frame #1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25 frame #2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12 frame #3: 0x0000000100003968 a.out`std::__1::__function::__alloc_func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()[abi:se200000](this=0x000000016fdff280, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:171:12 frame #4: 0x00000001000026bc a.out`std::__1::__function::__func<int (*)(int, int), std::__1::allocator<int (*)(int, int)>, int (int, int)>::operator()(this=0x000000016fdff278, __arg=0x000000016fdff224, __arg=0x000000016fdff220) at function.h:313:10 frame #5: 0x0000000100003c38 a.out`std::__1::__function::__value_func<int (int, int)>::operator()[abi:se200000](this=0x000000016fdff278, __args=0x000000016fdff224, __args=0x000000016fdff220) const at function.h:430:12 frame #6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10 frame #7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10 frame #8: 0x0000000183cdf154 dyld`start + 2476 (lldb) ``` after ``` (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x0000000100001f04 a.out`foo(x=1, y=1) at main.cpp:4:10 frame #1: 0x0000000100003a00 a.out`decltype(std::declval<int (*&)(int, int)>()(std::declval<int>(), std::declval<int>())) std::__1::__invoke[abi:se200000]<int (*&)(int, int), int, int>(__f=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:149:25 frame #2: 0x000000010000399c a.out`int std::__1::__invoke_void_return_wrapper<int, false>::__call[abi:se200000]<int (*&)(int, int), int, int>(__args=0x000000016fdff280, __args=0x000000016fdff224, __args=0x000000016fdff220) at invoke.h:216:12 frame #6: 0x0000000100002038 a.out`std::__1::function<int (int, int)>::operator()(this= Function = foo(int, int) , __arg=1, __arg=1) const at function.h:989:10 frame #7: 0x0000000100001f64 a.out`main(argc=1, argv=0x000000016fdff4f8) at main.cpp:9:10 frame #8: 0x0000000183cdf154 dyld`start + 2476 Note: Some frames were hidden by frame recognizers ``` (cherry picked from commit f01f80c)
The issue was introduced in llvm#104523. The code introduces the `ret_val` variable but does not use it. Instead it returns a pointer, which gets implicitly converted to bool. (cherry picked from commit 6528157)
This reverts commit 6f45602, which depends on llvm#104523, which I'm reverting. (cherry picked from commit aa70f83)
This is a fix forward for the issue introduced in llvm#104523. (cherry picked from commit 68a1593)
Compilers and language runtimes often use helper functions that are fundamentally uninteresting when debugging anything but the compiler/runtime itself. This patch introduces a user-extensible mechanism that allows for these frames to be hidden from backtraces and automatically skipped over when navigating the stack with
up
anddown
.This does not affect the numbering of frames, so
f <N>
will still provide access to the hidden frames. Thebt
output will also print a hint that frames have been hidden.My primary motivation for this feature is to hide thunks in the Swift programming language, but I'm including an example recognizer for
std::function::operator()
that I wished for myself many times while debugging LLDB.rdar://126629381
Example output. (Yes, my proof-of-concept recognizer could hide even more frames if we had a method that returned the function name without the return type or I used something that isn't based off regex, but it's really only meant as an example).
before:
after