Skip to content

Commit 2cff3de

Browse files
committed
[lldb/bindings] Add Python ScriptedProcess base class to lldb module
In order to facilitate the writting of Scripted Processes, this patch introduces a `ScriptedProcess` python base class in the lldb module. The base class holds the python interface with all the - abstract - methods that need to be implemented by the inherited class but also some methods that can be overwritten. This patch also provides an example of a Scripted Process with the `MyScriptedProcess` class. rdar://65508855 Differential Revision: https://reviews.llvm.org/D95712 Signed-off-by: Med Ismail Bennani <[email protected]>
1 parent 182f0d1 commit 2cff3de

File tree

6 files changed

+250
-0
lines changed

6 files changed

+250
-0
lines changed

lldb/bindings/python/CMakeLists.txt

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

107+
create_python_package(
108+
${swig_target}
109+
${lldb_python_target_dir}
110+
"plugins"
111+
FILES
112+
"${LLDB_SOURCE_DIR}/examples/python/scripted_process/scripted_process.py")
113+
107114
if(APPLE)
108115
create_python_package(
109116
${swig_target}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import os
2+
3+
import lldb
4+
from lldb.plugins.scripted_process import ScriptedProcess
5+
6+
class MyScriptedProcess(ScriptedProcess):
7+
def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
8+
super().__init__(target, args)
9+
10+
def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo:
11+
return self.memory_regions[0]
12+
13+
def get_thread_with_id(self, tid: int):
14+
return {}
15+
16+
def get_registers_for_thread(self, tid: int):
17+
return {}
18+
19+
def read_memory_at_address(self, addr: int, size: int) -> lldb.SBData:
20+
data = lldb.SBData().CreateDataFromCString(
21+
self.target.GetByteOrder(),
22+
self.target.GetCodeByteSize(),
23+
"Hello, world!")
24+
return data
25+
26+
def get_loaded_images(self):
27+
return self.loaded_images
28+
29+
def get_process_id(self) -> int:
30+
return 42
31+
32+
def is_alive(self) -> bool:
33+
return True
34+
35+
def __lldb_init_module(debugger, dict):
36+
if not 'SKIP_SCRIPTED_PROCESS_LAUNCH' in os.environ:
37+
debugger.HandleCommand(
38+
"process launch -C %s.%s" % (__name__,
39+
MyScriptedProcess.__name__))
40+
else:
41+
print("Name of the class that will manage the scripted process: '%s.%s'"
42+
% (__name__, MyScriptedProcess.__name__))
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
from abc import ABCMeta, abstractmethod
2+
import six
3+
4+
import lldb
5+
6+
@six.add_metaclass(ABCMeta)
7+
class ScriptedProcess:
8+
9+
"""
10+
The base class for a scripted process.
11+
12+
Most of the base class methods are `@abstractmethod` that need to be
13+
overwritten by the inheriting class.
14+
15+
DISCLAIMER: THIS INTERFACE IS STILL UNDER DEVELOPMENT AND NOT STABLE.
16+
THE METHODS EXPOSED MIGHT CHANGE IN THE FUTURE.
17+
"""
18+
19+
@abstractmethod
20+
def __init__(self, target, args):
21+
""" Construct a scripted process.
22+
23+
Args:
24+
target (lldb.SBTarget): The target launching the scripted process.
25+
args (lldb.SBStructuredData): A Dictionary holding arbitrary
26+
key/value pairs used by the scripted process.
27+
"""
28+
self.target = None
29+
self.args = None
30+
if isinstance(target, lldb.SBTarget) and target.IsValid():
31+
self.target = target
32+
if isinstance(args, lldb.SBStructuredData) and args.IsValid():
33+
self.args = args
34+
35+
@abstractmethod
36+
def get_memory_region_containing_address(addr):
37+
""" Get the memory region for the scripted process, containing a
38+
specific address.
39+
40+
Args:
41+
addr (int): Address to look for in the scripted process memory
42+
regions.
43+
44+
Returns:
45+
lldb.SBMemoryRegionInfo: The memory region containing the address.
46+
None if out of bounds.
47+
"""
48+
pass
49+
50+
@abstractmethod
51+
def get_thread_with_id(tid):
52+
""" Get the scripted process thread with a specific ID.
53+
54+
Args:
55+
tid (int): Thread ID to look for in the scripted process.
56+
57+
Returns:
58+
Dict: The thread represented as a dictionary, withr the
59+
tid thread ID. None if tid doesn't match any of the scripted
60+
process threads.
61+
"""
62+
pass
63+
64+
@abstractmethod
65+
def get_registers_for_thread(tid):
66+
""" Get the register context dictionary for a certain thread of
67+
the scripted process.
68+
69+
Args:
70+
tid (int): Thread ID for the thread's register context.
71+
72+
Returns:
73+
Dict: The register context represented as a dictionary, for the
74+
tid thread. None if tid doesn't match any of the scripted
75+
process threads.
76+
"""
77+
pass
78+
79+
@abstractmethod
80+
def read_memory_at_address(addr, size):
81+
""" Get a memory buffer from the scripted process at a certain address,
82+
of a certain size.
83+
84+
Args:
85+
addr (int): Address from which we should start reading.
86+
size (int): Size of the memory to read.
87+
88+
Returns:
89+
lldb.SBData: An `lldb.SBData` buffer with the target byte size and
90+
byte order storing the memory read.
91+
"""
92+
pass
93+
94+
@abstractmethod
95+
def get_loaded_images(self):
96+
""" Get the list of loaded images for the scripted process.
97+
98+
```
99+
class ScriptedProcessImage:
100+
def __init__(name, file_spec, uuid, load_address):
101+
self.name = name
102+
self.file_spec = file_spec
103+
self.uuid = uuid
104+
self.load_address = load_address
105+
```
106+
107+
Returns:
108+
List[ScriptedProcessImage]: A list of `ScriptedProcessImage`
109+
containing for each entry, the name of the library, a UUID,
110+
an `lldb.SBFileSpec` and a load address.
111+
None if the list is empty.
112+
"""
113+
pass
114+
115+
def get_process_id(self):
116+
""" Get the scripted process identifier.
117+
118+
Returns:
119+
int: The scripted process identifier.
120+
"""
121+
return 0
122+
123+
124+
def launch(self):
125+
""" Simulate the scripted process launch.
126+
127+
Returns:
128+
lldb.SBError: An `lldb.SBError` with error code 0.
129+
"""
130+
return lldb.SBError()
131+
132+
def resume(self):
133+
""" Simulate the scripted process resume.
134+
135+
Returns:
136+
lldb.SBError: An `lldb.SBError` with error code 0.
137+
"""
138+
return lldb.SBError()
139+
140+
@abstractmethod
141+
def is_alive(self):
142+
""" Check if the scripted process is alive.
143+
144+
Returns:
145+
bool: True if scripted process is alive. False otherwise.
146+
"""
147+
pass
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
4+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""
2+
Test python scripted process in lldb
3+
"""
4+
5+
import os
6+
7+
import lldb
8+
from lldbsuite.test.decorators import *
9+
from lldbsuite.test.lldbtest import *
10+
from lldbsuite.test import lldbutil
11+
from lldbsuite.test import lldbtest
12+
13+
14+
class PlatformProcessCrashInfoTestCase(TestBase):
15+
16+
mydir = TestBase.compute_mydir(__file__)
17+
18+
def setUp(self):
19+
TestBase.setUp(self)
20+
self.source = "main.c"
21+
22+
def tearDown(self):
23+
TestBase.tearDown(self)
24+
25+
def test_python_plugin_package(self):
26+
"""Test that the lldb python module has a `plugins.scripted_process`
27+
package."""
28+
self.expect('script import lldb.plugins',
29+
substrs=["ModuleNotFoundError"], matching=False)
30+
31+
self.expect('script dir(lldb.plugins)',
32+
substrs=["scripted_process"])
33+
34+
self.expect('script import lldb.plugins.scripted_process',
35+
substrs=["ModuleNotFoundError"], matching=False)
36+
37+
self.expect('script dir(lldb.plugins.scripted_process)',
38+
substrs=["ScriptedProcess"])
39+
40+
self.expect('script from lldb.plugins.scripted_process import ScriptedProcess',
41+
substrs=["ImportError"], matching=False)
42+
43+
self.expect('script dir(ScriptedProcess)',
44+
substrs=["launch"])
45+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#include <stdlib.h>
2+
3+
int main() {
4+
return 0; // break here
5+
}

0 commit comments

Comments
 (0)