Skip to content

Commit 11ab69d

Browse files
committed
[lldb/test] Update TestScriptedProcess to use skinny corefiles
This patch changes the ScriptedProcess test to use a stack-only skinny corefile as a backing store. The corefile is saved as a temporary file at the beginning of the test, and a second target is created for the ScriptedProcess. To do so, we use the SBAPI from the ScriptedProcess' python script to interact with the corefile process. This patch also makes some small adjustments to the other ScriptedProcess scripts to resolve some inconsistencies and removes the raw memory dump that was previously checked in. Differential Revision: https://reviews.llvm.org/D112047 Signed-off-by: Med Ismail Bennani <[email protected]>
1 parent 77b929d commit 11ab69d

File tree

5 files changed

+191
-25
lines changed

5 files changed

+191
-25
lines changed
Binary file not shown.

lldb/examples/python/scripted_process/my_scripted_process.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def get_scripted_thread_plugin(self):
6969

7070

7171
class MyScriptedThread(ScriptedThread):
72-
registers = {
72+
register_ctx = {
7373
"rax":0x00000000000006e4,
7474
"rbx":0x00000001040b6060,
7575
"rcx":0x00000001040b2e00,
@@ -123,7 +123,7 @@ def __init__(idx, cfa, pc, symbol_ctx):
123123
return self.frame_zero[0:0]
124124

125125
def get_register_context(self) -> str:
126-
return struct.pack("{}Q".format(len(self.registers)), *self.registers.values())
126+
return struct.pack("{}Q".format(len(self.register_ctx)), *self.register_ctx.values())
127127

128128

129129
def __lldb_init_module(debugger, dict):

lldb/examples/python/scripted_process/scripted_process.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def __init__(self, target, args):
3737
self.args = args
3838

3939
@abstractmethod
40-
def get_memory_region_containing_address(addr):
40+
def get_memory_region_containing_address(self, addr):
4141
""" Get the memory region for the scripted process, containing a
4242
specific address.
4343
@@ -52,7 +52,7 @@ def get_memory_region_containing_address(addr):
5252
pass
5353

5454
@abstractmethod
55-
def get_thread_with_id(tid):
55+
def get_thread_with_id(self, tid):
5656
""" Get the scripted process thread with a specific ID.
5757
5858
Args:
@@ -66,7 +66,7 @@ def get_thread_with_id(tid):
6666
pass
6767

6868
@abstractmethod
69-
def get_registers_for_thread(tid):
69+
def get_registers_for_thread(self, tid):
7070
""" Get the register context dictionary for a certain thread of
7171
the scripted process.
7272
@@ -81,7 +81,7 @@ def get_registers_for_thread(tid):
8181
pass
8282

8383
@abstractmethod
84-
def read_memory_at_address(addr, size):
84+
def read_memory_at_address(self, addr, size):
8585
""" Get a memory buffer from the scripted process at a certain address,
8686
of a certain size.
8787
@@ -211,7 +211,7 @@ def __init__(self, process, args):
211211
self.state = None
212212
self.stop_reason = None
213213
self.register_info = None
214-
self.register_ctx = []
214+
self.register_ctx = {}
215215
self.frames = []
216216

217217
@abstractmethod
@@ -294,7 +294,7 @@ def get_register_info(self):
294294
if triple:
295295
arch = triple.split('-')[0]
296296
if arch == 'x86_64':
297-
self.register_info['sets'] = ['GPR', 'FPU', 'EXC']
297+
self.register_info['sets'] = ['General Purpose Registers']
298298
self.register_info['registers'] = [
299299
{'name': 'rax', 'bitsize': 64, 'offset': 0, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 0, 'dwarf': 0},
300300
{'name': 'rbx', 'bitsize': 64, 'offset': 8, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 3, 'dwarf': 3},

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

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,20 @@
22
Test python scripted process in lldb
33
"""
44

5-
import os
5+
import os, json, tempfile
66

77
import lldb
88
from lldbsuite.test.decorators import *
99
from lldbsuite.test.lldbtest import *
1010
from lldbsuite.test import lldbutil
1111
from lldbsuite.test import lldbtest
1212

13-
1413
class ScriptedProcesTestCase(TestBase):
1514

1615
mydir = TestBase.compute_mydir(__file__)
1716

1817
def setUp(self):
1918
TestBase.setUp(self)
20-
self.source = "main.c"
2119

2220
def tearDown(self):
2321
TestBase.tearDown(self)
@@ -43,7 +41,7 @@ def test_python_plugin_package(self):
4341
self.expect('script dir(ScriptedProcess)',
4442
substrs=["launch"])
4543

46-
@skipIf(oslist=["linux"], archs=["arm", "aarch64"])
44+
@skipIf(archs=no_match(['x86_64']))
4745
def test_scripted_process_and_scripted_thread(self):
4846
"""Test that we can launch an lldb scripted process using the SBAPI,
4947
check its process ID, read string from memory, check scripted thread
@@ -78,19 +76,29 @@ def test_scripted_process_and_scripted_thread(self):
7876
self.assertGreater(thread.GetNumFrames(), 0)
7977

8078
frame = thread.GetFrameAtIndex(0)
79+
GPRs = None
8180
register_set = frame.registers # Returns an SBValueList.
8281
for regs in register_set:
83-
if 'GPR' in regs.name:
84-
registers = regs
82+
if 'general purpose' in regs.name.lower():
83+
GPRs = regs
8584
break
8685

87-
self.assertTrue(registers, "Invalid General Purpose Registers Set")
88-
self.assertEqual(registers.GetNumChildren(), 21)
89-
for idx, reg in enumerate(registers, start=1):
86+
self.assertTrue(GPRs, "Invalid General Purpose Registers Set")
87+
self.assertEqual(GPRs.GetNumChildren(), 21)
88+
for idx, reg in enumerate(GPRs, start=1):
9089
self.assertEqual(idx, int(reg.value, 16))
9190

92-
@skipIfDarwin
91+
def create_stack_skinny_corefile(self, file):
92+
self.build()
93+
target, process, thread, _ = lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c"))
94+
self.assertTrue(process.IsValid(), "Process is invalid.")
95+
# FIXME: Use SBAPI to save the process corefile.
96+
self.runCmd("process save-core -s stack " + file)
97+
self.assertTrue(os.path.exists(file), "No stack-only corefile found.")
98+
self.assertTrue(self.dbg.DeleteTarget(target), "Couldn't delete target")
99+
93100
@skipUnlessDarwin
101+
@skipIf(archs=no_match(['x86_64']))
94102
def test_launch_scripted_process_stack_frames(self):
95103
"""Test that we can launch an lldb scripted process from the command
96104
line, check its process ID and read string from memory."""
@@ -101,26 +109,45 @@ def test_launch_scripted_process_stack_frames(self):
101109
for module in target.modules:
102110
if 'a.out' in module.GetFileSpec().GetFilename():
103111
main_module = module
112+
break
104113

105114
self.assertTrue(main_module, "Invalid main module.")
106115
error = target.SetModuleLoadAddress(main_module, 0)
107116
self.assertTrue(error.Success(), "Reloading main module at offset 0 failed.")
108117

109-
scripted_process_example_relpath = ['..','..','..','..','examples','python','scripted_process','my_scripted_process.py']
118+
scripted_process_example_relpath = 'stack_core_scripted_process.py'
119+
os.environ['SKIP_SCRIPTED_PROCESS_LAUNCH'] = '1'
110120
self.runCmd("command script import " + os.path.join(self.getSourceDir(),
111-
*scripted_process_example_relpath))
121+
scripted_process_example_relpath))
122+
123+
corefile_process = None
124+
with tempfile.NamedTemporaryFile() as file:
125+
self.create_stack_skinny_corefile(file.name)
126+
corefile_target = self.dbg.CreateTarget(None)
127+
corefile_process = corefile_target.LoadCore(self.getBuildArtifact(file.name))
128+
self.assertTrue(corefile_process, PROCESS_IS_VALID)
129+
130+
structured_data = lldb.SBStructuredData()
131+
structured_data.SetFromJSON(json.dumps({
132+
"backing_target_idx" : self.dbg.GetIndexOfTarget(corefile_process.GetTarget())
133+
}))
134+
launch_info = lldb.SBLaunchInfo(None)
135+
launch_info.SetProcessPluginName("ScriptedProcess")
136+
launch_info.SetScriptedProcessClassName("stack_core_scripted_process.StackCoreScriptedProcess")
137+
launch_info.SetScriptedProcessDictionary(structured_data)
112138

113-
process = target.GetProcess()
139+
error = lldb.SBError()
140+
process = target.Launch(launch_info, error)
141+
self.assertTrue(error.Success(), error.GetCString())
114142
self.assertTrue(process, PROCESS_IS_VALID)
115143
self.assertEqual(process.GetProcessID(), 42)
116-
self.assertEqual(process.GetNumThreads(), 1)
117144

145+
self.assertEqual(process.GetNumThreads(), 1)
118146
thread = process.GetSelectedThread()
119147
self.assertTrue(thread, "Invalid thread.")
120-
self.assertEqual(thread.GetThreadID(), 0x19)
121-
self.assertEqual(thread.GetName(), "MyScriptedThread.thread-1")
148+
self.assertEqual(thread.GetName(), "StackCoreScriptedThread.thread-1")
122149

123-
self.assertEqual(thread.GetNumFrames(), 4)
150+
self.assertEqual(thread.GetNumFrames(), 3)
124151
frame = thread.GetSelectedFrame()
125152
self.assertTrue(frame, "Invalid frame.")
126153
self.assertEqual(frame.GetFunctionName(), "bar")
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import os,struct,signal
2+
3+
from typing import Any, Dict
4+
5+
import lldb
6+
from lldb.plugins.scripted_process import ScriptedProcess
7+
from lldb.plugins.scripted_process import ScriptedThread
8+
9+
class StackCoreScriptedProcess(ScriptedProcess):
10+
def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
11+
super().__init__(target, args)
12+
13+
self.backing_target_idx = args.GetValueForKey("backing_target_idx")
14+
15+
self.corefile_target = None
16+
self.corefile_process = None
17+
if (self.backing_target_idx and self.backing_target_idx.IsValid()):
18+
if self.backing_target_idx.GetType() == lldb.eStructuredDataTypeInteger:
19+
idx = self.backing_target_idx.GetIntegerValue(42)
20+
if self.backing_target_idx.GetType() == lldb.eStructuredDataTypeString:
21+
idx = int(self.backing_target_idx.GetStringValue(100))
22+
self.corefile_target = target.GetDebugger().GetTargetAtIndex(idx)
23+
self.corefile_process = self.corefile_target.GetProcess()
24+
25+
def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo:
26+
mem_region = lldb.SBMemoryRegionInfo()
27+
error = self.corefile_process.GetMemoryRegionInfo(addr, mem_region)
28+
if error.Fail():
29+
return None
30+
return mem_region
31+
32+
def get_thread_with_id(self, tid: int):
33+
return {}
34+
35+
def get_registers_for_thread(self, tid: int):
36+
return {}
37+
38+
def read_memory_at_address(self, addr: int, size: int) -> lldb.SBData:
39+
data = lldb.SBData()
40+
error = lldb.SBError()
41+
bytes_read = self.corefile_process.ReadMemory(addr, size, error)
42+
43+
if error.Fail():
44+
return data
45+
46+
data.SetData(error, bytes_read, self.corefile_target.GetByteOrder(),
47+
self.corefile_target.GetAddressByteSize())
48+
49+
return data
50+
51+
def get_loaded_images(self):
52+
# TODO: Iterate over corefile_target modules and build a data structure
53+
# from it.
54+
return self.loaded_images
55+
56+
def get_process_id(self) -> int:
57+
return 42
58+
59+
def should_stop(self) -> bool:
60+
return True
61+
62+
def is_alive(self) -> bool:
63+
return True
64+
65+
def get_scripted_thread_plugin(self):
66+
return StackCoreScriptedThread.__module__ + "." + StackCoreScriptedThread.__name__
67+
68+
69+
class StackCoreScriptedThread(ScriptedThread):
70+
def __init__(self, process, args):
71+
super().__init__(process, args)
72+
self.backing_target_idx = args.GetValueForKey("backing_target_idx")
73+
74+
self.corefile_target = None
75+
self.corefile_process = None
76+
if (self.backing_target_idx and self.backing_target_idx.IsValid()):
77+
if self.backing_target_idx.GetType() == lldb.eStructuredDataTypeInteger:
78+
idx = self.backing_target_idx.GetIntegerValue(42)
79+
if self.backing_target_idx.GetType() == lldb.eStructuredDataTypeString:
80+
idx = int(self.backing_target_idx.GetStringValue(100))
81+
self.corefile_target = self.target.GetDebugger().GetTargetAtIndex(idx)
82+
self.corefile_process = self.corefile_target.GetProcess()
83+
84+
def get_thread_id(self) -> int:
85+
return 0x19
86+
87+
def get_name(self) -> str:
88+
return StackCoreScriptedThread.__name__ + ".thread-1"
89+
90+
def get_stop_reason(self) -> Dict[str, Any]:
91+
return { "type": lldb.eStopReasonSignal, "data": {
92+
"signal": signal.SIGINT
93+
} }
94+
95+
def get_stackframes(self):
96+
class ScriptedStackFrame:
97+
def __init__(idx, cfa, pc, symbol_ctx):
98+
self.idx = idx
99+
self.cfa = cfa
100+
self.pc = pc
101+
self.symbol_ctx = symbol_ctx
102+
103+
104+
symbol_ctx = lldb.SBSymbolContext()
105+
frame_zero = ScriptedStackFrame(0, 0x42424242, 0x5000000, symbol_ctx)
106+
self.frames.append(frame_zero)
107+
108+
return self.frame_zero[0:0]
109+
110+
def get_register_context(self) -> str:
111+
thread = self.corefile_process.GetSelectedThread()
112+
if not thread or thread.GetNumFrames() == 0:
113+
return None
114+
frame = thread.GetFrameAtIndex(0)
115+
116+
GPRs = None
117+
registerSet = frame.registers # Returns an SBValueList.
118+
for regs in registerSet:
119+
if 'general purpose' in regs.name.lower():
120+
GPRs = regs
121+
break
122+
123+
if not GPRs:
124+
return None
125+
126+
for reg in GPRs:
127+
self.register_ctx[reg.name] = int(reg.value, base=16)
128+
129+
return struct.pack("{}Q".format(len(self.register_ctx)), *self.register_ctx.values())
130+
131+
132+
def __lldb_init_module(debugger, dict):
133+
if not 'SKIP_SCRIPTED_PROCESS_LAUNCH' in os.environ:
134+
debugger.HandleCommand(
135+
"process launch -C %s.%s" % (__name__,
136+
StackCoreScriptedProcess.__name__))
137+
else:
138+
print("Name of the class that will manage the scripted process: '%s.%s'"
139+
% (__name__, StackCoreScriptedProcess.__name__))

0 commit comments

Comments
 (0)