aboutsummaryrefslogtreecommitdiff
path: root/lldb/source
diff options
context:
space:
mode:
authorAdrian Prantl <aprantl@apple.com>2024-08-20 16:01:22 -0700
committerGitHub <noreply@github.com>2024-08-20 16:01:22 -0700
commitf01f80ce6ca7640bb0e267b84b1ed0e89b57e2d9 (patch)
treec38a79f19b1eef07a0b315cc067035d380a83c80 /lldb/source
parent1e9d0028d35ae69263aa848b4cb02245f442eb5c (diff)
downloadllvm-f01f80ce6ca7640bb0e267b84b1ed0e89b57e2d9.zip
llvm-f01f80ce6ca7640bb0e267b84b1ed0e89b57e2d9.tar.gz
llvm-f01f80ce6ca7640bb0e267b84b1ed0e89b57e2d9.tar.bz2
[lldb] Extend frame recognizers to hide frames from backtraces (#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 ```
Diffstat (limited to 'lldb/source')
-rw-r--r--lldb/source/API/SBFrame.cpp15
-rw-r--r--lldb/source/API/SBThread.cpp3
-rw-r--r--lldb/source/Commands/CommandCompletions.cpp4
-rw-r--r--lldb/source/Commands/CommandObjectFrame.cpp24
-rw-r--r--lldb/source/Commands/CommandObjectMemory.cpp3
-rw-r--r--lldb/source/Commands/CommandObjectThread.cpp19
-rw-r--r--lldb/source/Commands/Options.td2
-rw-r--r--lldb/source/Core/Debugger.cpp3
-rw-r--r--lldb/source/Interpreter/CommandInterpreter.cpp9
-rw-r--r--lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp44
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h3
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp29
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h3
-rw-r--r--lldb/source/Target/Process.cpp7
-rw-r--r--lldb/source/Target/StackFrame.cpp26
-rw-r--r--lldb/source/Target/StackFrameList.cpp8
-rw-r--r--lldb/source/Target/StackFrameRecognizer.cpp29
-rw-r--r--lldb/source/Target/Thread.cpp12
-rw-r--r--lldb/source/Target/ThreadPlanStepOut.cpp2
19 files changed, 204 insertions, 41 deletions
diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp
index 47fc886..2689ecb 100644
--- a/lldb/source/API/SBFrame.cpp
+++ b/lldb/source/API/SBFrame.cpp
@@ -1195,13 +1195,24 @@ bool SBFrame::IsArtificial() const {
std::unique_lock<std::recursive_mutex> lock;
ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
- StackFrame *frame = exe_ctx.GetFramePtr();
- if (frame)
+ if (StackFrame *frame = exe_ctx.GetFramePtr())
return frame->IsArtificial();
return false;
}
+bool SBFrame::IsHidden() const {
+ LLDB_INSTRUMENT_VA(this);
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ if (StackFrame *frame = exe_ctx.GetFramePtr())
+ return frame->IsHidden();
+
+ return false;
+}
+
const char *SBFrame::GetFunctionName() {
LLDB_INSTRUMENT_VA(this);
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index 786f62b..140a292 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,
+ /*show_hidden=*/true);
} else
strm.PutCString("No status");
diff --git a/lldb/source/Commands/CommandCompletions.cpp b/lldb/source/Commands/CommandCompletions.cpp
index 54f4b36..216aaf9 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, /*show_hidden*/ true);
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, /*show_hidden*/ true);
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 29e460f..46c75e3 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -278,6 +278,30 @@ protected:
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;
+ const unsigned max_depth = 12;
+ for (unsigned num_try = 0; num_try < max_depth; ++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 (candidate_sp->IsHidden())
+ 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)
diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp
index 137b1ad..baf5d91 100644
--- a/lldb/source/Commands/CommandObjectMemory.cpp
+++ b/lldb/source/Commands/CommandObjectMemory.cpp
@@ -1570,7 +1570,8 @@ protected:
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 605f872..6a89c16 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -89,6 +89,9 @@ public:
"invalid boolean value for option '%c': %s", short_option,
option_arg.data());
} break;
+ case 'u':
+ m_filtered_backtrace = false;
+ break;
default:
llvm_unreachable("Unimplemented option");
}
@@ -99,6 +102,7 @@ public:
m_count = UINT32_MAX;
m_start = 0;
m_extended_backtrace = false;
+ m_filtered_backtrace = true;
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -109,6 +113,7 @@ public:
uint32_t m_count;
uint32_t m_start;
bool m_extended_backtrace;
+ bool m_filtered_backtrace;
};
CommandObjectThreadBacktrace(CommandInterpreter &interpreter)
@@ -121,7 +126,10 @@ public:
"call stacks.\n"
"Use 'settings set frame-format' to customize the printing of "
"frames in the backtrace and 'settings set thread-format' to "
- "customize the thread header.",
+ "customize the thread header.\n"
+ "Customizable frame recognizers may filter out less interesting "
+ "frames, which results in gaps in the numbering. "
+ "Use '-u' to see all frames.",
nullptr,
eCommandRequiresProcess | eCommandRequiresThread |
eCommandTryTargetAPILock | eCommandProcessMustBeLaunched |
@@ -199,7 +207,8 @@ protected:
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 @@ protected:
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 @@ public:
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 f050cd2..9c4dbed 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1048,6 +1048,8 @@ 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_unfiltered : Option<"unfiltered", "u">, Group<1>,
+ 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 309e01e..67f01707a 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,
+ /*show_hidden*/ true);
}
}
}
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index e451125..8729880 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -835,11 +835,12 @@ void CommandInterpreter::LoadCommandDictionary() {
std::unique_ptr<CommandObjectRegexCommand> bt_regex_cmd_up(
new CommandObjectRegexCommand(
*this, "_regexp-bt",
- "Show backtrace of the current thread's call stack. Any numeric "
- "argument displays at most that many frames. The argument 'all' "
- "displays all threads. Use 'settings set frame-format' to customize "
+ "Show backtrace of the current thread's call stack. Any numeric "
+ "argument displays at most that many frames. The argument 'all' "
+ "displays all threads. Use 'settings set frame-format' to customize "
"the printing of individual frames and 'settings set thread-format' "
- "to customize the thread header.",
+ "to customize the thread header. Frame recognizers may filter the"
+ "list. Use 'thread backtrace -u (--unfiltered)' to see them all.",
"bt [<digit> | all]", 0, false));
if (bt_regex_cmd_up) {
// accept but don't document "bt -c <number>" -- before bt was a regex
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
index c7202a4..c60200a 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,49 @@ static ConstString g_coro_frame = ConstString("__coro_frame");
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;
+ 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 {};
+ const 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/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index 3026b61..5351c1a 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -227,6 +227,9 @@ public:
LLDBSwigPython_GetRecognizedArguments(PyObject *implementor,
const lldb::StackFrameSP &frame_sp);
+ static bool LLDBSwigPython_ShouldHide(PyObject *implementor,
+ const lldb::StackFrameSP &frame_sp);
+
static bool LLDBSWIGPythonRunScriptKeywordProcess(
const char *python_function_name, const char *session_dictionary_name,
const lldb::ProcessSP &process, std::string &output);
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 335c482..2a94f11 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -1524,6 +1524,35 @@ lldb::ValueObjectListSP ScriptInterpreterPythonImpl::GetRecognizedArguments(
return ValueObjectListSP();
}
+bool ScriptInterpreterPythonImpl::ShouldHide(
+ const StructuredData::ObjectSP &os_plugin_object_sp,
+ lldb::StackFrameSP frame_sp) {
+ Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
+
+ if (!os_plugin_object_sp)
+ return false;
+
+ StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
+ if (!generic)
+ return false;
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)generic->GetValue());
+
+ if (!implementor.IsAllocated())
+ return false;
+
+ bool result =
+ SWIGBridge::LLDBSwigPython_ShouldHide(implementor.get(), frame_sp);
+
+ // if it fails, print the error but otherwise go on
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ return result;
+}
+
ScriptedProcessInterfaceUP
ScriptInterpreterPythonImpl::CreateScriptedProcessInterface() {
return std::make_unique<ScriptedProcessPythonInterface>(*this);
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index c2024ef..85d7995 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -107,6 +107,9 @@ public:
GetRecognizedArguments(const StructuredData::ObjectSP &implementor,
lldb::StackFrameSP frame_sp) override;
+ bool ShouldHide(const StructuredData::ObjectSP &implementor,
+ lldb::StackFrameSP frame_sp) override;
+
lldb::ScriptedProcessInterfaceUP CreateScriptedProcessInterface() override;
lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override;
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 3c9247fdb..b2a0f13 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,
+ /*show_hidden*/ true);
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,
+ /*show_hidden*/ num_frames <= 1);
++num_thread_infos_dumped;
} else {
Log *log = GetLog(LLDBLog::Process);
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 3a2b4d0..0ebaf55 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -1198,6 +1198,12 @@ bool StackFrame::IsArtificial() const {
return m_stack_frame_kind == StackFrame::Kind::Artificial;
}
+bool StackFrame::IsHidden() {
+ if (auto recognized_frame_sp = GetRecognizedFrame())
+ return recognized_frame_sp->ShouldHide();
+ return false;
+}
+
SourceLanguage StackFrame::GetLanguage() {
CompileUnit *cu = GetSymbolContext(eSymbolContextCompUnit).comp_unit;
if (cu)
@@ -1971,12 +1977,16 @@ bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
}
RecognizedStackFrameSP StackFrame::GetRecognizedFrame() {
- if (!m_recognized_frame_sp) {
- m_recognized_frame_sp = GetThread()
- ->GetProcess()
- ->GetTarget()
- .GetFrameRecognizerManager()
- .RecognizeFrame(CalculateStackFrame());
- }
- return m_recognized_frame_sp;
+ auto process = GetThread()->GetProcess();
+ if (!process)
+ return {};
+ // If recognizer list has been modified, discard cache.
+ auto &manager = process->GetTarget().GetFrameRecognizerManager();
+ auto new_generation = manager.GetGeneration();
+ if (m_frame_recognizer_generation != new_generation)
+ m_recognized_frame_sp.reset();
+ m_frame_recognizer_generation = new_generation;
+ if (!m_recognized_frame_sp.has_value())
+ m_recognized_frame_sp = manager.RecognizeFrame(CalculateStackFrame());
+ return m_recognized_frame_sp.value();
}
diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp
index 0cf9ce1..7808bd3 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 show_hidden,
const char *selected_frame_marker) {
size_t num_frames_displayed = 0;
@@ -951,7 +951,6 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
unselected_marker = buffer.c_str();
}
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 +962,11 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
else
marker = unselected_marker;
}
+
+ // Hide uninteresting frames unless it's the selected frame.
+ if (!show_hidden && frame_sp != selected_frame_sp && frame_sp->IsHidden())
+ continue;
+
// Check for interruption here. If we're fetching arguments, this loop
// can go slowly:
Debugger &dbg = m_thread.GetProcess()->GetTarget().GetDebugger();
diff --git a/lldb/source/Target/StackFrameRecognizer.cpp b/lldb/source/Target/StackFrameRecognizer.cpp
index 0ccb1ae..44411af 100644
--- a/lldb/source/Target/StackFrameRecognizer.cpp
+++ b/lldb/source/Target/StackFrameRecognizer.cpp
@@ -17,10 +17,14 @@ using namespace lldb;
using namespace lldb_private;
class ScriptedRecognizedStackFrame : public RecognizedStackFrame {
+ bool m_hidden;
+
public:
- ScriptedRecognizedStackFrame(ValueObjectListSP args) {
- m_arguments = args;
+ ScriptedRecognizedStackFrame(ValueObjectListSP args, bool hidden)
+ : m_hidden(hidden) {
+ m_arguments = std::move(args);
}
+ bool ShouldHide() override { return m_hidden; }
};
ScriptedStackFrameRecognizer::ScriptedStackFrameRecognizer(
@@ -38,13 +42,22 @@ ScriptedStackFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame) {
ValueObjectListSP args =
m_interpreter->GetRecognizedArguments(m_python_object_sp, frame);
auto args_synthesized = ValueObjectListSP(new ValueObjectList());
- for (const auto &o : args->GetObjects()) {
- args_synthesized->Append(ValueObjectRecognizerSynthesizedValue::Create(
- *o, eValueTypeVariableArgument));
+ if (args) {
+ for (const auto &o : args->GetObjects())
+ args_synthesized->Append(ValueObjectRecognizerSynthesizedValue::Create(
+ *o, eValueTypeVariableArgument));
}
+ bool hidden = m_interpreter->ShouldHide(m_python_object_sp, frame);
+
return RecognizedStackFrameSP(
- new ScriptedRecognizedStackFrame(args_synthesized));
+ new ScriptedRecognizedStackFrame(args_synthesized, hidden));
+}
+
+void StackFrameRecognizerManager::BumpGeneration() {
+ uint32_t n = m_generation;
+ n = (n + 1) & ((1 << 16) - 1);
+ m_generation = n;
}
void StackFrameRecognizerManager::AddRecognizer(
@@ -53,6 +66,7 @@ void StackFrameRecognizerManager::AddRecognizer(
m_recognizers.push_front({(uint32_t)m_recognizers.size(), recognizer, false,
module, RegularExpressionSP(), symbols,
RegularExpressionSP(), first_instruction_only});
+ BumpGeneration();
}
void StackFrameRecognizerManager::AddRecognizer(
@@ -61,6 +75,7 @@ void StackFrameRecognizerManager::AddRecognizer(
m_recognizers.push_front({(uint32_t)m_recognizers.size(), recognizer, true,
ConstString(), module, std::vector<ConstString>(),
symbol, first_instruction_only});
+ BumpGeneration();
}
void StackFrameRecognizerManager::ForEach(
@@ -97,10 +112,12 @@ bool StackFrameRecognizerManager::RemoveRecognizerWithID(
if (found == m_recognizers.end())
return false;
m_recognizers.erase(found);
+ BumpGeneration();
return true;
}
void StackFrameRecognizerManager::RemoveAllRecognizers() {
+ BumpGeneration();
m_recognizers.clear();
}
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index 74d1a26..fcf0f4e 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -1748,7 +1748,7 @@ 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 show_hidden, bool only_stacks) {
if (!only_stacks) {
ExecutionContext exe_ctx(shared_from_this());
@@ -1795,7 +1795,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, show_hidden, selected_frame_marker);
if (num_frames == 1)
strm.IndentLess();
strm.IndentLess();
@@ -1893,9 +1893,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_frame_info,
- uint32_t num_frames_with_source) {
- return GetStackFrameList()->GetStatus(
- strm, first_frame, num_frames, show_frame_info, num_frames_with_source);
+ uint32_t num_frames_with_source,
+ bool show_hidden) {
+ return GetStackFrameList()->GetStatus(strm, first_frame, num_frames,
+ show_frame_info, num_frames_with_source,
+ /*show_unique*/ false, show_hidden);
}
Unwind &Thread::GetUnwinder() {
diff --git a/lldb/source/Target/ThreadPlanStepOut.cpp b/lldb/source/Target/ThreadPlanStepOut.cpp
index 0a1e2ae..8ca1dbc 100644
--- a/lldb/source/Target/ThreadPlanStepOut.cpp
+++ b/lldb/source/Target/ThreadPlanStepOut.cpp
@@ -58,7 +58,7 @@ ThreadPlanStepOut::ThreadPlanStepOut(
return; // we can't do anything here. ValidatePlan() will return false.
// While stepping out, behave as-if artificial frames are not present.
- while (return_frame_sp->IsArtificial()) {
+ while (return_frame_sp->IsArtificial() || return_frame_sp->IsHidden()) {
m_stepped_past_frames.push_back(return_frame_sp);
++return_frame_index;