Skip to content

Commit 026c6be

Browse files
committed
[lldb] Add ScriptedPlatform python implementation
This patch introduces both the Scripted Platform python base implementation and an example for it. The base implementation is embedded in lldb python module under `lldb.plugins.scripted_platform`. This patch also refactor the various SWIG methods to create scripted objects into a single method, that is now shared between the Scripted Platform, Process and Thread. It also replaces the target argument by a execution context object. Differential Revision: https://reviews.llvm.org/D139250 Signed-off-by: Med Ismail Bennani <[email protected]>
1 parent 3fbd3b8 commit 026c6be

File tree

13 files changed

+176
-82
lines changed

13 files changed

+176
-82
lines changed

lldb/bindings/python/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ function(finish_swig_python swig_target lldb_python_bindings_dir lldb_python_tar
106106
FILES
107107
"${LLDB_SOURCE_DIR}/examples/python/scripted_process/scripted_process.py")
108108

109+
create_python_package(
110+
${swig_target}
111+
${lldb_python_target_dir}
112+
"plugins"
113+
FILES
114+
"${LLDB_SOURCE_DIR}/examples/python/scripted_process/scripted_platform.py")
115+
109116
if(APPLE)
110117
create_python_package(
111118
${swig_target}

lldb/bindings/python/python-wrapper.swig

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,9 @@ PythonObject lldb_private::LLDBSwigPythonCreateCommandObject(
203203
return pfunc(ToSWIGWrapper(std::move(debugger_sp)), dict);
204204
}
205205

206-
PythonObject lldb_private::LLDBSwigPythonCreateScriptedProcess(
206+
PythonObject lldb_private::LLDBSwigPythonCreateScriptedObject(
207207
const char *python_class_name, const char *session_dictionary_name,
208-
const lldb::TargetSP &target_sp,
208+
lldb::ExecutionContextRefSP exe_ctx_sp,
209209
const lldb_private::StructuredDataImpl &args_impl,
210210
std::string &error_string) {
211211
if (python_class_name == NULL || python_class_name[0] == '\0' ||
@@ -225,8 +225,6 @@ PythonObject lldb_private::LLDBSwigPythonCreateScriptedProcess(
225225
return PythonObject();
226226
}
227227

228-
PythonObject target_arg = ToSWIGWrapper(target_sp);
229-
230228
llvm::Expected<PythonCallable::ArgInfo> arg_info = pfunc.GetArgInfo();
231229
if (!arg_info) {
232230
llvm::handleAllErrors(
@@ -240,54 +238,14 @@ PythonObject lldb_private::LLDBSwigPythonCreateScriptedProcess(
240238

241239
PythonObject result = {};
242240
if (arg_info.get().max_positional_args == 2) {
243-
result = pfunc(target_arg, ToSWIGWrapper(args_impl));
241+
result = pfunc(ToSWIGWrapper(exe_ctx_sp), ToSWIGWrapper(args_impl));
244242
} else {
245243
error_string.assign("wrong number of arguments in __init__, should be 2 "
246244
"(not including self)");
247245
}
248246
return result;
249247
}
250248

251-
PythonObject lldb_private::LLDBSwigPythonCreateScriptedThread(
252-
const char *python_class_name, const char *session_dictionary_name,
253-
const lldb::ProcessSP &process_sp, const StructuredDataImpl &args_impl,
254-
std::string &error_string) {
255-
if (python_class_name == NULL || python_class_name[0] == '\0' ||
256-
!session_dictionary_name)
257-
return PythonObject();
258-
259-
PyErr_Cleaner py_err_cleaner(true);
260-
261-
auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(
262-
session_dictionary_name);
263-
auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(
264-
python_class_name, dict);
265-
266-
if (!pfunc.IsAllocated()) {
267-
error_string.append("could not find script class: ");
268-
error_string.append(python_class_name);
269-
return PythonObject();
270-
}
271-
272-
llvm::Expected<PythonCallable::ArgInfo> arg_info = pfunc.GetArgInfo();
273-
if (!arg_info) {
274-
llvm::handleAllErrors(
275-
arg_info.takeError(),
276-
[&](PythonException &E) { error_string.append(E.ReadBacktrace()); },
277-
[&](const llvm::ErrorInfoBase &E) {
278-
error_string.append(E.message());
279-
});
280-
return PythonObject();
281-
}
282-
283-
if (arg_info.get().max_positional_args == 2)
284-
return pfunc(ToSWIGWrapper(process_sp), ToSWIGWrapper(args_impl));
285-
286-
error_string.assign("wrong number of arguments in __init__, should be 2 "
287-
"(not including self)");
288-
return PythonObject();
289-
}
290-
291249
PythonObject lldb_private::LLDBSwigPythonCreateScriptedThreadPlan(
292250
const char *python_class_name, const char *session_dictionary_name,
293251
const lldb_private::StructuredDataImpl &args_impl,

lldb/examples/python/scripted_process/crashlog_scripted_process.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ def load_images(self, images):
6262
self.addr_mask,
6363
self.target)
6464

65-
def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
66-
super().__init__(target, args)
65+
def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData):
66+
super().__init__(exe_ctx, args)
6767

6868
if not self.target or not self.target.IsValid():
6969
# Return error
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
from abc import ABCMeta, abstractmethod
2+
3+
import lldb
4+
5+
class ScriptedPlatform(metaclass=ABCMeta):
6+
7+
"""
8+
The base class for a scripted platform.
9+
10+
Most of the base class methods are `@abstractmethod` that need to be
11+
overwritten by the inheriting class.
12+
13+
DISCLAIMER: THIS INTERFACE IS STILL UNDER DEVELOPMENT AND NOT STABLE.
14+
THE METHODS EXPOSED MIGHT CHANGE IN THE FUTURE.
15+
"""
16+
17+
processes = None
18+
19+
@abstractmethod
20+
def __init__(self, exe_ctx, args):
21+
""" Construct a scripted platform.
22+
23+
Args:
24+
exe_ctx (lldb.SBExecutionContext): The execution context for the scripted platform
25+
args (lldb.SBStructuredData): A Dictionary holding arbitrary
26+
key/value pairs used by the scripted platform.
27+
"""
28+
processes = []
29+
30+
@abstractmethod
31+
def list_processes(self):
32+
""" Get a list of processes that are running or that can be attached to on the platform.
33+
34+
processes = {
35+
420: {
36+
name: a.out,
37+
arch: aarch64,
38+
pid: 420,
39+
parent_pid: 42 (optional),
40+
uid: 0 (optional),
41+
gid: 0 (optional),
42+
},
43+
}
44+
45+
Returns:
46+
Dict: The processes represented as a dictionary, with at least the
47+
process ID, name, architecture. Optionally, the user can also
48+
provide the parent process ID and the user and group IDs.
49+
The dictionary can be empty.
50+
"""
51+
pass
52+
53+
def get_process_info(self, pid):
54+
""" Get the dictionary describing the process.
55+
56+
Returns:
57+
Dict: The dictionary of process info that matched process ID.
58+
None if the process doesn't exists
59+
"""
60+
pass
61+
62+
@abstractmethod
63+
def attach_to_process(self, attach_info):
64+
""" Attach to a process.
65+
66+
Args:
67+
attach_info (lldb.SBAttachInfo): The information related to attach to a process.
68+
69+
Returns:
70+
lldb.SBError: A status object notifying if the attach succeeded.
71+
"""
72+
pass
73+
74+
@abstractmethod
75+
def launch_process(self, launch_info):
76+
""" Launch a process.
77+
78+
Args:
79+
launch_info (lldb.SBLaunchInfo): The information related to the process launch.
80+
81+
Returns:
82+
lldb.SBError: A status object notifying if the launch succeeded.
83+
"""
84+
pass
85+
86+
@abstractmethod
87+
def kill_process(self, pid):
88+
""" Kill a process.
89+
90+
Args:
91+
pid (int): Process ID for the process to be killed.
92+
93+
Returns:
94+
lldb.SBError: A status object notifying if the shutdown succeeded.
95+
"""
96+
pass

lldb/examples/python/scripted_process/scripted_process.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,20 @@ class ScriptedProcess(metaclass=ABCMeta):
2121
metadata = None
2222

2323
@abstractmethod
24-
def __init__(self, target, args):
24+
def __init__(self, exe_ctx, args):
2525
""" Construct a scripted process.
2626
2727
Args:
28-
target (lldb.SBTarget): The target launching the scripted process.
28+
exe_ctx (lldb.SBExecutionContext): The execution context for the scripted process.
2929
args (lldb.SBStructuredData): A Dictionary holding arbitrary
3030
key/value pairs used by the scripted process.
3131
"""
32+
target = None
3233
self.target = None
3334
self.args = None
3435
self.arch = None
36+
if isinstance(exe_ctx, lldb.SBExecutionContext):
37+
target = exe_ctx.target
3538
if isinstance(target, lldb.SBTarget) and target.IsValid():
3639
self.target = target
3740
triple = self.target.triple

lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,10 @@ void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data);
100100
// Although these are scripting-language specific, their definition depends on
101101
// the public API.
102102

103-
python::PythonObject LLDBSwigPythonCreateScriptedProcess(
103+
python::PythonObject LLDBSwigPythonCreateScriptedObject(
104104
const char *python_class_name, const char *session_dictionary_name,
105-
const lldb::TargetSP &target_sp, const StructuredDataImpl &args_impl,
106-
std::string &error_string);
107-
108-
python::PythonObject LLDBSwigPythonCreateScriptedThread(
109-
const char *python_class_name, const char *session_dictionary_name,
110-
const lldb::ProcessSP &process_sp, const StructuredDataImpl &args_impl,
105+
lldb::ExecutionContextRefSP exe_ctx_sp,
106+
const lldb_private::StructuredDataImpl &args_impl,
111107
std::string &error_string);
112108

113109
llvm::Expected<bool> LLDBSwigPythonBreakpointCallbackFunction(

lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,18 @@ StructuredData::GenericSP ScriptedProcessPythonInterface::CreatePluginObject(
3737
if (class_name.empty())
3838
return {};
3939

40-
TargetSP target_sp = exe_ctx.GetTargetSP();
4140
StructuredDataImpl args_impl(args_sp);
4241
std::string error_string;
4342

4443
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
4544
Locker::FreeLock);
4645

47-
PythonObject ret_val = LLDBSwigPythonCreateScriptedProcess(
48-
class_name.str().c_str(), m_interpreter.GetDictionaryName(), target_sp,
49-
args_impl, error_string);
46+
lldb::ExecutionContextRefSP exe_ctx_ref_sp =
47+
std::make_shared<ExecutionContextRef>(exe_ctx);
48+
49+
PythonObject ret_val = LLDBSwigPythonCreateScriptedObject(
50+
class_name.str().c_str(), m_interpreter.GetDictionaryName(),
51+
exe_ctx_ref_sp, args_impl, error_string);
5052

5153
m_object_instance_sp =
5254
StructuredData::GenericSP(new StructuredPythonObject(std::move(ret_val)));

lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ StructuredData::GenericSP ScriptedThreadPythonInterface::CreatePluginObject(
3434
if (class_name.empty() && !script_obj)
3535
return {};
3636

37-
ProcessSP process_sp = exe_ctx.GetProcessSP();
3837
StructuredDataImpl args_impl(args_sp);
3938
std::string error_string;
4039

@@ -43,11 +42,13 @@ StructuredData::GenericSP ScriptedThreadPythonInterface::CreatePluginObject(
4342

4443
PythonObject ret_val;
4544

46-
if (!script_obj)
47-
ret_val = LLDBSwigPythonCreateScriptedThread(
48-
class_name.str().c_str(), m_interpreter.GetDictionaryName(), process_sp,
49-
args_impl, error_string);
50-
else
45+
if (!script_obj) {
46+
lldb::ExecutionContextRefSP exe_ctx_ref_sp =
47+
std::make_shared<ExecutionContextRef>(exe_ctx);
48+
ret_val = LLDBSwigPythonCreateScriptedObject(
49+
class_name.str().c_str(), m_interpreter.GetDictionaryName(),
50+
exe_ctx_ref_sp, args_impl, error_string);
51+
} else
5152
ret_val = PythonObject(PyRefType::Borrowed,
5253
static_cast<PyObject *>(script_obj->GetValue()));
5354

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import os
2+
3+
import lldb
4+
from lldb.plugins.scripted_platform import ScriptedPlatform
5+
6+
class MyScriptedPlatform(ScriptedPlatform):
7+
8+
def __init__(self, exe_ctx, args):
9+
self.processes = {}
10+
11+
proc = {}
12+
proc['name'] = 'a.out'
13+
proc['arch'] = 'arm64-apple-macosx'
14+
proc['pid'] = 420
15+
proc['parent'] = 42
16+
proc['uid'] = 501
17+
proc['gid'] = 20
18+
self.processes[420] = proc
19+
20+
def list_processes(self):
21+
return self.processes
22+
23+
def get_process_info(self, pid):
24+
return self.processes[pid]
25+
26+
def launch_process(self, launch_info):
27+
return lldb.SBError()
28+
29+
def kill_process(self, pid):
30+
return lldb.SBError()
31+
32+
def __lldb_init_module(debugger, dict):
33+
if not 'SKIP_SCRIPTED_PLATFORM_SELECT' in os.environ:
34+
debugger.HandleCommand(
35+
"platform select scripted-platform -C %s.%s" % (__name__, MyScriptedPlatform.__name__))
36+
else:
37+
print("Name of the class that will manage the scripted platform: '%s.%s'"
38+
% (__name__, MyScriptedPlatform.__name__))

lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from lldb.plugins.scripted_process import ScriptedThread
88

99
class DummyScriptedProcess(ScriptedProcess):
10-
def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
11-
super().__init__(target, args)
10+
def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData):
11+
super().__init__(exe_ctx, args)
1212
self.threads[0] = DummyScriptedThread(self, None)
1313

1414
def read_memory_at_address(self, addr: int, size: int, error: lldb.SBError) -> lldb.SBData:

0 commit comments

Comments
 (0)