Skip to content

Commit a1bf7dd

Browse files
committed
[lldb] Add support for ScriptedFrame with real threads
This patch extends ScriptedFrame to work with real (non-scripted) threads, enabling frame providers to synthesize frames for native processes. Previously, ScriptedFrame only worked within ScriptedProcess/ScriptedThread contexts. This patch decouples ScriptedFrame from ScriptedThread, allowing users to augment or replace stack frames in real debugging sessions for use cases like custom calling conventions, reconstructing corrupted frames from core files, or adding diagnostic frames. Key changes: - ScriptedFrame::Create() now accepts ThreadSP instead of requiring ScriptedThread, extracting architecture from the target triple rather than ScriptedProcess.arch - Added SBTarget::RegisterScriptedFrameProvider() and ClearScriptedFrameProvider() APIs, with Target storing a SyntheticFrameProviderDescriptor template for new threads - Added "target frame-provider register/clear" commands for CLI access - Thread class gains LoadScriptedFrameProvider(), ClearScriptedFrameProvider(), and GetFrameProvider() methods for per-thread frame provider management - New SyntheticStackFrameList overrides FetchFramesUpTo() to lazily provide frames from either the frame provider or the real stack This enables practical use of the SyntheticFrameProvider infrastructure in real debugging workflows. rdar://161834688 Signed-off-by: Med Ismail Bennani <[email protected]>
1 parent 4cd17ee commit a1bf7dd

30 files changed

+1512
-62
lines changed

lldb/examples/python/templates/scripted_process.py

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ def __init__(self, process, args):
245245
key/value pairs used by the scripted thread.
246246
"""
247247
self.target = None
248+
self.arch = None
248249
self.originating_process = None
249250
self.process = None
250251
self.args = None
@@ -266,6 +267,9 @@ def __init__(self, process, args):
266267
and process.IsValid()
267268
):
268269
self.target = process.target
270+
triple = self.target.triple
271+
if triple:
272+
self.arch = triple.split("-")[0]
269273
self.originating_process = process
270274
self.process = self.target.GetProcess()
271275
self.get_register_info()
@@ -352,17 +356,14 @@ def get_stackframes(self):
352356
def get_register_info(self):
353357
if self.register_info is None:
354358
self.register_info = dict()
355-
if "x86_64" in self.originating_process.arch:
359+
if "x86_64" in self.arch:
356360
self.register_info["sets"] = ["General Purpose Registers"]
357361
self.register_info["registers"] = INTEL64_GPR
358-
elif (
359-
"arm64" in self.originating_process.arch
360-
or self.originating_process.arch == "aarch64"
361-
):
362+
elif "arm64" in self.arch or self.arch == "aarch64":
362363
self.register_info["sets"] = ["General Purpose Registers"]
363364
self.register_info["registers"] = ARM64_GPR
364365
else:
365-
raise ValueError("Unknown architecture", self.originating_process.arch)
366+
raise ValueError("Unknown architecture", self.arch)
366367
return self.register_info
367368

368369
@abstractmethod
@@ -405,11 +406,12 @@ def __init__(self, thread, args):
405406
"""Construct a scripted frame.
406407
407408
Args:
408-
thread (ScriptedThread): The thread owning this frame.
409+
thread (ScriptedThread/lldb.SBThread): The thread owning this frame.
409410
args (lldb.SBStructuredData): A Dictionary holding arbitrary
410411
key/value pairs used by the scripted frame.
411412
"""
412413
self.target = None
414+
self.arch = None
413415
self.originating_thread = None
414416
self.thread = None
415417
self.args = None
@@ -424,10 +426,14 @@ def __init__(self, thread, args):
424426
or isinstance(thread, lldb.SBThread)
425427
and thread.IsValid()
426428
):
427-
self.target = thread.target
428429
self.process = thread.process
430+
self.target = self.process.target
431+
triple = self.target.triple
432+
if triple:
433+
self.arch = triple.split("-")[0]
434+
tid = thread.tid if isinstance(thread, ScriptedThread) else thread.id
429435
self.originating_thread = thread
430-
self.thread = self.process.GetThreadByIndexID(thread.tid)
436+
self.thread = self.process.GetThreadByIndexID(tid)
431437
self.get_register_info()
432438

433439
@abstractmethod
@@ -508,7 +514,18 @@ def get_variables(self, filters):
508514

509515
def get_register_info(self):
510516
if self.register_info is None:
511-
self.register_info = self.originating_thread.get_register_info()
517+
if isinstance(self.originating_thread, ScriptedThread):
518+
self.register_info = self.originating_thread.get_register_info()
519+
elif isinstance(self.originating_thread, lldb.SBThread):
520+
self.register_info = dict()
521+
if "x86_64" in self.arch:
522+
self.register_info["sets"] = ["General Purpose Registers"]
523+
self.register_info["registers"] = INTEL64_GPR
524+
elif "arm64" in self.arch or self.arch == "aarch64":
525+
self.register_info["sets"] = ["General Purpose Registers"]
526+
self.register_info["registers"] = ARM64_GPR
527+
else:
528+
raise ValueError("Unknown architecture", self.arch)
512529
return self.register_info
513530

514531
@abstractmethod
@@ -642,12 +659,12 @@ def get_stop_reason(self):
642659

643660
# TODO: Passthrough stop reason from driving process
644661
if self.driving_thread.GetStopReason() != lldb.eStopReasonNone:
645-
if "arm64" in self.originating_process.arch:
662+
if "arm64" in self.arch:
646663
stop_reason["type"] = lldb.eStopReasonException
647664
stop_reason["data"]["desc"] = (
648665
self.driving_thread.GetStopDescription(100)
649666
)
650-
elif self.originating_process.arch == "x86_64":
667+
elif self.arch == "x86_64":
651668
stop_reason["type"] = lldb.eStopReasonSignal
652669
stop_reason["data"]["signal"] = signal.SIGTRAP
653670
else:

lldb/include/lldb/API/SBTarget.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,34 @@ class LLDB_API SBTarget {
986986

987987
lldb::SBMutex GetAPIMutex() const;
988988

989+
/// Register a scripted frame provider for this target.
990+
///
991+
/// \param[in] class_name
992+
/// The name of the Python class that implements the frame provider.
993+
///
994+
/// \param[in] args_dict
995+
/// A dictionary of arguments to pass to the frame provider class.
996+
///
997+
/// \return
998+
/// An error object indicating success or failure.
999+
lldb::SBError RegisterScriptedFrameProvider(const char *class_name,
1000+
lldb::SBStructuredData args_dict);
1001+
1002+
/// Remove a scripted frame provider from this target by name.
1003+
///
1004+
/// \param[in] provider_name
1005+
/// The name of the frame provider class to remove.
1006+
///
1007+
/// \return
1008+
/// An error object indicating success or failure.
1009+
lldb::SBError RemoveScriptedFrameProvider(const char *provider_name);
1010+
1011+
/// Clear the scripted frame provider for this target.
1012+
///
1013+
/// \return
1014+
/// An error object indicating success or failure.
1015+
lldb::SBError ClearScriptedFrameProvider();
1016+
9891017
protected:
9901018
friend class SBAddress;
9911019
friend class SBAddressRange;

lldb/include/lldb/Target/StackFrame.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,10 @@ class StackFrame : public ExecutionContextScope,
442442
uint32_t GetFrameIndex() const;
443443

444444
/// Set this frame's synthetic frame index.
445-
void SetFrameIndex(uint32_t index) { m_frame_index = index; }
445+
void SetFrameIndex(uint32_t index) {
446+
m_frame_index = index;
447+
m_concrete_frame_index = index;
448+
}
446449

447450
/// Query this frame to find what frame it is in this Thread's
448451
/// StackFrameList, not counting inlined frames.

lldb/include/lldb/Target/StackFrameList.h

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ namespace lldb_private {
2020

2121
class ScriptedThread;
2222

23-
class StackFrameList {
23+
class StackFrameList : public std::enable_shared_from_this<StackFrameList> {
2424
public:
2525
// Constructors and Destructors
2626
StackFrameList(Thread &thread, const lldb::StackFrameListSP &prev_frames_sp,
2727
bool show_inline_frames);
2828

29-
~StackFrameList();
29+
virtual ~StackFrameList();
3030

3131
/// Get the number of visible frames. Frames may be created if \p can_create
3232
/// is true. Synthetic (inline) frames expanded from the concrete frame #0
@@ -106,6 +106,7 @@ class StackFrameList {
106106

107107
protected:
108108
friend class Thread;
109+
friend class ScriptedFrameProvider;
109110
friend class ScriptedThread;
110111

111112
/// Use this API to build a stack frame list (used for scripted threads, for
@@ -211,18 +212,20 @@ class StackFrameList {
211212
/// Whether or not to show synthetic (inline) frames. Immutable.
212213
const bool m_show_inlined_frames;
213214

215+
/// These two Fetch frames APIs and SynthesizeTailCallFrames are called in
216+
/// GetFramesUpTo, they are the ones that actually add frames. They must be
217+
/// called with the writer end of the list mutex held.
218+
219+
/// Returns true if fetching frames was interrupted, false otherwise.
220+
virtual bool FetchFramesUpTo(uint32_t end_idx,
221+
InterruptionControl allow_interrupt);
222+
214223
private:
215224
uint32_t SetSelectedFrameNoLock(lldb_private::StackFrame *frame);
216225
lldb::StackFrameSP
217226
GetFrameAtIndexNoLock(uint32_t idx,
218227
std::shared_lock<std::shared_mutex> &guard);
219228

220-
/// These two Fetch frames APIs and SynthesizeTailCallFrames are called in
221-
/// GetFramesUpTo, they are the ones that actually add frames. They must be
222-
/// called with the writer end of the list mutex held.
223-
224-
/// Returns true if fetching frames was interrupted, false otherwise.
225-
bool FetchFramesUpTo(uint32_t end_idx, InterruptionControl allow_interrupt);
226229
/// Not currently interruptible so returns void.
227230
void FetchOnlyConcreteFramesUpTo(uint32_t end_idx);
228231
void SynthesizeTailCallFrames(StackFrame &next_frame);
@@ -231,6 +234,27 @@ class StackFrameList {
231234
const StackFrameList &operator=(const StackFrameList &) = delete;
232235
};
233236

237+
/// A StackFrameList that wraps another StackFrameList and uses a
238+
/// SyntheticFrameProvider to lazily provide frames from either the provider
239+
/// or the underlying real stack frame list.
240+
class SyntheticStackFrameList : public StackFrameList {
241+
public:
242+
SyntheticStackFrameList(Thread &thread, lldb::StackFrameListSP input_frames,
243+
const lldb::StackFrameListSP &prev_frames_sp,
244+
bool show_inline_frames);
245+
246+
protected:
247+
/// Override FetchFramesUpTo to lazily return frames from the provider
248+
/// or from the actual stack frame list.
249+
bool FetchFramesUpTo(uint32_t end_idx,
250+
InterruptionControl allow_interrupt) override;
251+
252+
private:
253+
/// The input stack frame list that the provider transforms.
254+
/// This could be a real StackFrameList or another SyntheticStackFrameList.
255+
lldb::StackFrameListSP m_input_frames;
256+
};
257+
234258
} // namespace lldb_private
235259

236260
#endif // LLDB_TARGET_STACKFRAMELIST_H

lldb/include/lldb/Target/SyntheticFrameProvider.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ struct SyntheticFrameProviderDescriptor {
6464
/// Check if this descriptor has valid metadata for script-based providers.
6565
bool IsValid() const { return scripted_metadata_sp != nullptr; }
6666

67+
/// Dump a description of this descriptor to the given stream.
6768
void Dump(Stream *s) const;
6869
};
6970

lldb/include/lldb/Target/Target.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "lldb/Target/PathMappingList.h"
3333
#include "lldb/Target/SectionLoadHistory.h"
3434
#include "lldb/Target/Statistics.h"
35+
#include "lldb/Target/SyntheticFrameProvider.h"
3536
#include "lldb/Target/ThreadSpec.h"
3637
#include "lldb/Utility/ArchSpec.h"
3738
#include "lldb/Utility/Broadcaster.h"
@@ -697,6 +698,39 @@ class Target : public std::enable_shared_from_this<Target>,
697698
Status Attach(ProcessAttachInfo &attach_info,
698699
Stream *stream); // Optional stream to receive first stop info
699700

701+
// Frame provider methods
702+
703+
/// Add or update a scripted frame provider descriptor for this target.
704+
/// The descriptor's name is extracted from its scripted metadata class name.
705+
/// All new threads in this target will check if they match any descriptors
706+
/// to create their frame providers.
707+
///
708+
/// \param[in] descriptor
709+
/// The descriptor to add or update.
710+
Status AddScriptedFrameProviderDescriptor(
711+
const SyntheticFrameProviderDescriptor &descriptor);
712+
713+
/// Remove a scripted frame provider descriptor by name.
714+
///
715+
/// \param[in] name
716+
/// The name/key of the descriptor to remove (the class name).
717+
///
718+
/// \return
719+
/// True if a descriptor was removed, false if no descriptor with that
720+
/// name existed.
721+
bool RemoveScriptedFrameProviderDescriptor(llvm::StringRef name);
722+
723+
/// Clear all scripted frame provider descriptors for this target.
724+
void ClearScriptedFrameProviderDescriptors();
725+
726+
/// Get all scripted frame provider descriptors for this target.
727+
const llvm::StringMap<SyntheticFrameProviderDescriptor> &
728+
GetScriptedFrameProviderDescriptors() const;
729+
730+
/// Get a specific scripted frame provider descriptor by name.
731+
std::optional<SyntheticFrameProviderDescriptor>
732+
GetScriptedFrameProviderDescriptor(llvm::StringRef name) const;
733+
700734
// This part handles the breakpoints.
701735

702736
BreakpointList &GetBreakpointList(bool internal = false);
@@ -1689,6 +1723,13 @@ class Target : public std::enable_shared_from_this<Target>,
16891723
PathMappingList m_image_search_paths;
16901724
TypeSystemMap m_scratch_type_system_map;
16911725

1726+
/// Map of scripted frame provider descriptors for this target.
1727+
/// Keys are the provider class names, values are the descriptors.
1728+
/// Used to initialize frame providers for new threads.
1729+
llvm::StringMap<SyntheticFrameProviderDescriptor>
1730+
m_frame_provider_descriptors;
1731+
mutable std::recursive_mutex m_frame_provider_descriptors_mutex;
1732+
16921733
typedef std::map<lldb::LanguageType, lldb::REPLSP> REPLMap;
16931734
REPLMap m_repl_map;
16941735

lldb/include/lldb/Target/Thread.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,15 @@ class Thread : public std::enable_shared_from_this<Thread>,
12971297

12981298
lldb::StackFrameListSP GetStackFrameList();
12991299

1300+
llvm::Error
1301+
LoadScriptedFrameProvider(const SyntheticFrameProviderDescriptor &descriptor);
1302+
1303+
void ClearScriptedFrameProvider();
1304+
1305+
lldb::SyntheticFrameProviderSP GetFrameProvider() const {
1306+
return m_frame_provider_sp;
1307+
}
1308+
13001309
protected:
13011310
friend class ThreadPlan;
13021311
friend class ThreadList;
@@ -1400,6 +1409,9 @@ class Thread : public std::enable_shared_from_this<Thread>,
14001409
/// The Thread backed by this thread, if any.
14011410
lldb::ThreadWP m_backed_thread;
14021411

1412+
/// The Scripted Frame Provider, if any.
1413+
lldb::SyntheticFrameProviderSP m_frame_provider_sp;
1414+
14031415
private:
14041416
bool m_extended_info_fetched; // Have we tried to retrieve the m_extended_info
14051417
// for this thread?

lldb/include/lldb/Utility/ScriptedMetadata.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class ScriptedMetadata {
2727
}
2828
}
2929

30+
ScriptedMetadata(const ScriptedMetadata &other)
31+
: m_class_name(other.m_class_name), m_args_sp(other.m_args_sp) {}
32+
3033
explicit operator bool() const { return !m_class_name.empty(); }
3134

3235
llvm::StringRef GetClassName() const { return m_class_name; }

0 commit comments

Comments
 (0)