Skip to content

Commit 4cd17ee

Browse files
authored
[lldb/Interpreter] Implement ScriptedFrameProvider{,Python}Interface (#166662)
This patch implements the base and python interface for the ScriptedFrameProvider class. This is necessary to call python APIs from the ScriptedFrameProvider that will come in a follow-up. Signed-off-by: Med Ismail Bennani <[email protected]> Signed-off-by: Med Ismail Bennani <[email protected]>
1 parent 165563c commit 4cd17ee

20 files changed

+346
-1
lines changed

lldb/bindings/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ function(finish_swig_python swig_target lldb_python_bindings_dir lldb_python_tar
107107
"plugins"
108108
FILES
109109
"${LLDB_SOURCE_DIR}/examples/python/templates/parsed_cmd.py"
110+
"${LLDB_SOURCE_DIR}/examples/python/templates/scripted_frame_provider.py"
110111
"${LLDB_SOURCE_DIR}/examples/python/templates/scripted_process.py"
111112
"${LLDB_SOURCE_DIR}/examples/python/templates/scripted_platform.py"
112113
"${LLDB_SOURCE_DIR}/examples/python/templates/operating_system.py"

lldb/bindings/python/python-swigsafecast.swig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ PythonObject SWIGBridge::ToSWIGWrapper(lldb::ThreadPlanSP thread_plan_sp) {
3737
SWIGTYPE_p_lldb__SBThreadPlan);
3838
}
3939

40+
PythonObject SWIGBridge::ToSWIGWrapper(lldb::StackFrameListSP frames_sp) {
41+
return ToSWIGHelper(new lldb::SBFrameList(std::move(frames_sp)),
42+
SWIGTYPE_p_lldb__SBFrameList);
43+
}
44+
4045
PythonObject SWIGBridge::ToSWIGWrapper(lldb::BreakpointSP breakpoint_sp) {
4146
return ToSWIGHelper(new lldb::SBBreakpoint(std::move(breakpoint_sp)),
4247
SWIGTYPE_p_lldb__SBBreakpoint);

lldb/bindings/python/python-wrapper.swig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,18 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBExecutionContext(PyOb
556556
return sb_ptr;
557557
}
558558

559+
void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrameList(PyObject *data) {
560+
lldb::SBFrameList *sb_ptr = NULL;
561+
562+
int valid_cast = SWIG_ConvertPtr(data, (void **)&sb_ptr,
563+
SWIGTYPE_p_lldb__SBFrameList, 0);
564+
565+
if (valid_cast == -1)
566+
return NULL;
567+
568+
return sb_ptr;
569+
}
570+
559571
bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommand(
560572
const char *python_function_name, const char *session_dictionary_name,
561573
lldb::DebuggerSP debugger, const char *args,
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
from abc import ABCMeta, abstractmethod
2+
3+
import lldb
4+
5+
6+
class ScriptedFrameProvider(metaclass=ABCMeta):
7+
"""
8+
The base class for a scripted frame provider.
9+
10+
A scripted frame provider allows you to provide custom stack frames for a
11+
thread, which can be used to augment or replace the standard unwinding
12+
mechanism. This is useful for:
13+
14+
- Providing frames for custom calling conventions or languages
15+
- Reconstructing missing frames from crash dumps or core files
16+
- Adding diagnostic or synthetic frames for debugging
17+
- Visualizing state machines or async execution contexts
18+
19+
Most of the base class methods are `@abstractmethod` that need to be
20+
overwritten by the inheriting class.
21+
22+
Example usage:
23+
24+
.. code-block:: python
25+
26+
# Attach a frame provider to a thread
27+
thread = process.GetSelectedThread()
28+
error = thread.SetScriptedFrameProvider(
29+
"my_module.MyFrameProvider",
30+
lldb.SBStructuredData()
31+
)
32+
"""
33+
34+
@abstractmethod
35+
def __init__(self, input_frames, args):
36+
"""Construct a scripted frame provider.
37+
38+
Args:
39+
input_frames (lldb.SBFrameList): The frame list to use as input.
40+
This allows you to access frames by index. The frames are
41+
materialized lazily as you access them.
42+
args (lldb.SBStructuredData): A Dictionary holding arbitrary
43+
key/value pairs used by the scripted frame provider.
44+
"""
45+
self.input_frames = None
46+
self.args = None
47+
self.thread = None
48+
self.target = None
49+
self.process = None
50+
51+
if isinstance(input_frames, lldb.SBFrameList) and input_frames.IsValid():
52+
self.input_frames = input_frames
53+
self.thread = input_frames.GetThread()
54+
if self.thread and self.thread.IsValid():
55+
self.process = self.thread.GetProcess()
56+
if self.process and self.process.IsValid():
57+
self.target = self.process.GetTarget()
58+
59+
if isinstance(args, lldb.SBStructuredData) and args.IsValid():
60+
self.args = args
61+
62+
@abstractmethod
63+
def get_frame_at_index(self, index):
64+
"""Get a single stack frame at the given index.
65+
66+
This method is called lazily when a specific frame is needed in the
67+
thread's backtrace (e.g., via the 'bt' command). Each frame is
68+
requested individually as needed.
69+
70+
Args:
71+
index (int): The frame index to retrieve (0 for youngest/top frame).
72+
73+
Returns:
74+
Dict or None: A frame dictionary describing the stack frame, or None
75+
if no frame exists at this index. The dictionary should contain:
76+
77+
Required fields:
78+
- idx (int): The synthetic frame index (0 for youngest/top frame)
79+
- pc (int): The program counter address for the synthetic frame
80+
81+
Alternatively, you can return:
82+
- A ScriptedFrame object for full control over frame behavior
83+
- An integer representing an input frame index to reuse
84+
- None to indicate no more frames exist
85+
86+
Example:
87+
88+
.. code-block:: python
89+
90+
def get_frame_at_index(self, index):
91+
# Return None when there are no more frames
92+
if index >= self.total_frames:
93+
return None
94+
95+
# Re-use an input frame by returning its index
96+
if self.should_use_input_frame(index):
97+
return index # Returns input frame at this index
98+
99+
# Or create a custom frame dictionary
100+
if index == 0:
101+
return {
102+
"idx": 0,
103+
"pc": 0x100001234,
104+
}
105+
106+
return None
107+
108+
Note:
109+
The frames are indexed from 0 (youngest/top) to N (oldest/bottom).
110+
This method will be called repeatedly with increasing indices until
111+
None is returned.
112+
"""
113+
pass

lldb/include/lldb/API/SBFrameList.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@
1111

1212
#include "lldb/API/SBDefines.h"
1313

14+
namespace lldb_private {
15+
class ScriptInterpreter;
16+
namespace python {
17+
class SWIGBridge;
18+
}
19+
namespace lua {
20+
class SWIGBridge;
21+
}
22+
} // namespace lldb_private
23+
1424
namespace lldb {
1525

1626
/// Represents a list of SBFrame objects.
@@ -66,6 +76,10 @@ class LLDB_API SBFrameList {
6676
protected:
6777
friend class SBThread;
6878

79+
friend class lldb_private::python::SWIGBridge;
80+
friend class lldb_private::lua::SWIGBridge;
81+
friend class lldb_private::ScriptInterpreter;
82+
6983
private:
7084
SBFrameList(const lldb::StackFrameListSP &frame_list_sp);
7185

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEPROVIDERINTERFACE_H
10+
#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEPROVIDERINTERFACE_H
11+
12+
#include "lldb/lldb-private.h"
13+
14+
#include "ScriptedInterface.h"
15+
16+
namespace lldb_private {
17+
class ScriptedFrameProviderInterface : public ScriptedInterface {
18+
public:
19+
virtual llvm::Expected<StructuredData::GenericSP>
20+
CreatePluginObject(llvm::StringRef class_name,
21+
lldb::StackFrameListSP input_frames,
22+
StructuredData::DictionarySP args_sp) = 0;
23+
24+
virtual StructuredData::ObjectSP GetFrameAtIndex(uint32_t index) {
25+
return {};
26+
}
27+
};
28+
} // namespace lldb_private
29+
30+
#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEPROVIDERINTERFACE_H

lldb/include/lldb/Interpreter/ScriptInterpreter.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "lldb/API/SBError.h"
1717
#include "lldb/API/SBEvent.h"
1818
#include "lldb/API/SBExecutionContext.h"
19+
#include "lldb/API/SBFrameList.h"
1920
#include "lldb/API/SBLaunchInfo.h"
2021
#include "lldb/API/SBMemoryRegionInfo.h"
2122
#include "lldb/API/SBStream.h"
@@ -28,6 +29,7 @@
2829
#include "lldb/Host/StreamFile.h"
2930
#include "lldb/Interpreter/Interfaces/OperatingSystemInterface.h"
3031
#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
32+
#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h"
3133
#include "lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h"
3234
#include "lldb/Interpreter/Interfaces/ScriptedProcessInterface.h"
3335
#include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h"
@@ -537,6 +539,11 @@ class ScriptInterpreter : public PluginInterface {
537539
return {};
538540
}
539541

542+
virtual lldb::ScriptedFrameProviderInterfaceSP
543+
CreateScriptedFrameProviderInterface() {
544+
return {};
545+
}
546+
540547
virtual lldb::ScriptedThreadPlanInterfaceSP
541548
CreateScriptedThreadPlanInterface() {
542549
return {};
@@ -596,6 +603,9 @@ class ScriptInterpreter : public PluginInterface {
596603
lldb::ExecutionContextRefSP GetOpaqueTypeFromSBExecutionContext(
597604
const lldb::SBExecutionContext &exe_ctx) const;
598605

606+
lldb::StackFrameListSP
607+
GetOpaqueTypeFromSBFrameList(const lldb::SBFrameList &exe_ctx) const;
608+
599609
protected:
600610
Debugger &m_debugger;
601611
lldb::ScriptLanguage m_script_lang;

lldb/include/lldb/lldb-forward.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ class Scalar;
188188
class ScriptInterpreter;
189189
class ScriptInterpreterLocker;
190190
class ScriptedFrameInterface;
191+
class ScriptedFrameProviderInterface;
191192
class ScriptedMetadata;
192193
class ScriptedBreakpointInterface;
193194
class ScriptedPlatformInterface;
@@ -412,6 +413,8 @@ typedef std::shared_ptr<lldb_private::ScriptSummaryFormat>
412413
typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP;
413414
typedef std::shared_ptr<lldb_private::ScriptedFrameInterface>
414415
ScriptedFrameInterfaceSP;
416+
typedef std::shared_ptr<lldb_private::ScriptedFrameProviderInterface>
417+
ScriptedFrameProviderInterfaceSP;
415418
typedef std::shared_ptr<lldb_private::SyntheticFrameProvider>
416419
SyntheticFrameProviderSP;
417420
typedef std::shared_ptr<lldb_private::ScriptedMetadata> ScriptedMetadataSP;

lldb/source/Interpreter/ScriptInterpreter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ ScriptInterpreter::GetOpaqueTypeFromSBExecutionContext(
150150
return exe_ctx.m_exe_ctx_sp;
151151
}
152152

153+
lldb::StackFrameListSP ScriptInterpreter::GetOpaqueTypeFromSBFrameList(
154+
const lldb::SBFrameList &frame_list) const {
155+
return frame_list.m_opaque_sp;
156+
}
157+
153158
lldb::ScriptLanguage
154159
ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
155160
if (language.equals_insensitive(LanguageToString(eScriptLanguageNone)))

lldb/source/Plugins/Process/scripted/ScriptedFrame.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#ifndef LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H
1010
#define LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H
1111

12-
#include "Plugins/Process/Utility/RegisterContextMemory.h"
1312
#include "ScriptedThread.h"
1413
#include "lldb/Interpreter/ScriptInterpreter.h"
1514
#include "lldb/Target/DynamicRegisterInfo.h"

0 commit comments

Comments
 (0)