Skip to content
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

Merged
merged 1 commit into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
18 changes: 17 additions & 1 deletion lldb/bindings/python/python-wrapper.swig
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ PythonObject lldb_private::python::SWIGBridge::LLDBSWIGPython_CreateFrameRecogni
}

PyObject *lldb_private::python::SWIGBridge::LLDBSwigPython_GetRecognizedArguments(
PyObject * implementor, const lldb::StackFrameSP &frame_sp) {
PyObject *implementor, const lldb::StackFrameSP &frame_sp) {
static char callee_name[] = "get_recognized_arguments";

PythonObject arg = SWIGBridge::ToSWIGWrapper(frame_sp);
Expand All @@ -824,6 +824,22 @@ PyObject *lldb_private::python::SWIGBridge::LLDBSwigPython_GetRecognizedArgument
return result;
}

bool lldb_private::python::SWIGBridge::LLDBSwigPython_ShouldHide(
PyObject *implementor, const lldb::StackFrameSP &frame_sp) {
static char callee_name[] = "should_hide";

PythonObject arg = SWIGBridge::ToSWIGWrapper(frame_sp);

PythonString str(callee_name);

PyObject *result =
PyObject_CallMethodObjArgs(implementor, str.get(), arg.get(), NULL);
bool ret_val = result ? PyObject_IsTrue(result) : false;
Py_XDECREF(result);

return result;
}

void *lldb_private::python::SWIGBridge::LLDBSWIGPython_GetDynamicSetting(
void *module, const char *setting, const lldb::TargetSP &target_sp) {
if (!module || !setting)
Expand Down
4 changes: 4 additions & 0 deletions lldb/include/lldb/API/SBFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ class LLDB_API SBFrame {

bool IsArtificial() const;

/// Return whether a frame recognizer decided this frame should not
/// be displayes in backtraces etc.
bool IsHidden() const;

/// The version that doesn't supply a 'use_dynamic' value will use the
/// target's default.
lldb::SBValue EvaluateExpression(const char *expr);
Expand Down
5 changes: 5 additions & 0 deletions lldb/include/lldb/Interpreter/ScriptInterpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ class ScriptInterpreter : public PluginInterface {
return lldb::ValueObjectListSP();
}

virtual bool ShouldHide(const StructuredData::ObjectSP &implementor,
lldb::StackFrameSP frame_sp) {
return false;
}

virtual StructuredData::GenericSP
CreateScriptedBreakpointResolver(const char *class_name,
const StructuredDataImpl &args_data,
Expand Down
36 changes: 22 additions & 14 deletions lldb/include/lldb/Target/StackFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,11 @@ class StackFrame : public ExecutionContextScope,
/// may have limited support for inspecting variables.
bool IsArtificial() const;

/// Query whether this frame should be hidden from backtraces. Frame
/// recognizers can customize this behavior and hide distracting
/// system implementation details this way.
bool IsHidden();

/// Query this frame to find what frame it is in this Thread's
/// StackFrameList.
///
Expand Down Expand Up @@ -518,33 +523,36 @@ class StackFrame : public ExecutionContextScope,
bool HasCachedData() const;

private:
// For StackFrame only
/// For StackFrame only.
adrian-prantl marked this conversation as resolved.
Show resolved Hide resolved
/// \{
lldb::ThreadWP m_thread_wp;
uint32_t m_frame_index;
uint32_t m_concrete_frame_index;
lldb::RegisterContextSP m_reg_context_sp;
StackID m_id;
Address m_frame_code_addr; // The frame code address (might not be the same as
// the actual PC for inlined frames) as a
// section/offset address
/// \}

/// The frame code address (might not be the same as the actual PC
/// for inlined frames) as a section/offset address.
Address m_frame_code_addr;
SymbolContext m_sc;
Flags m_flags;
Scalar m_frame_base;
Status m_frame_base_error;
bool m_cfa_is_valid; // Does this frame have a CFA? Different from CFA ==
// LLDB_INVALID_ADDRESS
uint16_t m_frame_recognizer_generation;
/// Does this frame have a CFA? Different from CFA == LLDB_INVALID_ADDRESS.
bool m_cfa_is_valid;
Kind m_stack_frame_kind;

// Whether this frame behaves like the zeroth frame, in the sense
// that its pc value might not immediately follow a call (and thus might
// be the first address of its function). True for actual frame zero as
// well as any other frame with the same trait.
/// Whether this frame behaves like the zeroth frame, in the sense
/// that its pc value might not immediately follow a call (and thus might
/// be the first address of its function). True for actual frame zero as
/// well as any other frame with the same trait.
bool m_behaves_like_zeroth_frame;
lldb::VariableListSP m_variable_list_sp;
ValueObjectList m_variable_list_value_objects; // Value objects for each
// variable in
// m_variable_list_sp
lldb::RecognizedStackFrameSP m_recognized_frame_sp;
/// Value objects for each variable in m_variable_list_sp.
ValueObjectList m_variable_list_value_objects;
std::optional<lldb::RecognizedStackFrameSP> m_recognized_frame_sp;
StreamString m_disassembly;
std::recursive_mutex m_mutex;

Expand Down
2 changes: 1 addition & 1 deletion lldb/include/lldb/Target/StackFrameList.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 show_hidden = false,
const char *frame_marker = nullptr);

protected:
Expand Down
21 changes: 15 additions & 6 deletions lldb/include/lldb/Target/StackFrameRecognizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "lldb/lldb-private-forward.h"
#include "lldb/lldb-public.h"

#include <cstdint>
#include <deque>
#include <optional>
#include <vector>
Expand All @@ -28,20 +29,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;
Expand All @@ -53,7 +57,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:
Expand All @@ -73,10 +76,10 @@ 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;

std::string m_python_class;

public:
Expand Down Expand Up @@ -123,8 +126,14 @@ class StackFrameRecognizerManager {
lldb::StackFrameRecognizerSP GetRecognizerForFrame(lldb::StackFrameSP frame);

lldb::RecognizedStackFrameSP RecognizeFrame(lldb::StackFrameSP frame);
/// Returns a number that changes whenever the list of recognizers
/// has been modified.
uint16_t GetGeneration() const { return m_generation; }

private:
/// Increase the generation counter.
void BumpGeneration();
adrian-prantl marked this conversation as resolved.
Show resolved Hide resolved

struct RegisteredEntry {
uint32_t recognizer_id;
lldb::StackFrameRecognizerSP recognizer;
Expand All @@ -137,14 +146,14 @@ class StackFrameRecognizerManager {
};

std::deque<RegisteredEntry> m_recognizers;
uint16_t m_generation;
};

/// \class ValueObjectRecognizerSynthesizedValue
///
/// 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) {
Expand Down
4 changes: 2 additions & 2 deletions lldb/include/lldb/Target/Thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -1128,11 +1128,11 @@ 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 show_hidden, 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 show_hidden);

// 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
Expand Down
15 changes: 13 additions & 2 deletions lldb/source/API/SBFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 2 additions & 1 deletion lldb/source/API/SBThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down
4 changes: 2 additions & 2 deletions lldb/source/Commands/CommandCompletions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down Expand Up @@ -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());
}
Expand Down
24 changes: 24 additions & 0 deletions lldb/source/Commands/CommandObjectFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
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)
Expand Down
3 changes: 2 additions & 1 deletion lldb/source/Commands/CommandObjectMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
19 changes: 15 additions & 4 deletions lldb/source/Commands/CommandObjectThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads {
"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");
}
Expand All @@ -99,6 +102,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 {
Expand All @@ -109,6 +113,7 @@ class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads {
uint32_t m_count;
uint32_t m_start;
bool m_extended_backtrace;
bool m_filtered_backtrace;
};

CommandObjectThreadBacktrace(CommandInterpreter &interpreter)
Expand All @@ -121,7 +126,10 @@ class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads {
"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 |
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Commands/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
adrian-prantl marked this conversation as resolved.
Show resolved Hide resolved
}

let Command = "thread step scope" in {
Expand Down
Loading
Loading