xref: /llvm-project/lldb/test/API/python_api/process/TestProcessAPI.py (revision 7606a54363d3d90802977c9f5fb9046d4d780835)
1"""
2Test SBProcess APIs, including ReadMemory(), WriteMemory(), and others.
3"""
4
5from __future__ import print_function
6
7
8import lldb
9from lldbsuite.test.decorators import *
10from lldbsuite.test.lldbtest import *
11from lldbsuite.test.lldbutil import get_stopped_thread, state_type_to_str
12
13
14class ProcessAPITestCase(TestBase):
15
16    mydir = TestBase.compute_mydir(__file__)
17
18    def setUp(self):
19        # Call super's setUp().
20        TestBase.setUp(self)
21        # Find the line number to break inside main().
22        self.line = line_number(
23            "main.cpp",
24            "// Set break point at this line and check variable 'my_char'.")
25
26    @add_test_categories(['pyapi'])
27    @skipIfReproducer # SBProcess::ReadMemory is not instrumented.
28    def test_read_memory(self):
29        """Test Python SBProcess.ReadMemory() API."""
30        self.build()
31        exe = self.getBuildArtifact("a.out")
32
33        target = self.dbg.CreateTarget(exe)
34        self.assertTrue(target, VALID_TARGET)
35
36        breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line)
37        self.assertTrue(breakpoint, VALID_BREAKPOINT)
38
39        # Launch the process, and do not stop at the entry point.
40        process = target.LaunchSimple(
41            None, None, self.get_process_working_directory())
42
43        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
44        self.assertTrue(
45            thread.IsValid(),
46            "There should be a thread stopped due to breakpoint")
47        frame = thread.GetFrameAtIndex(0)
48
49        # Get the SBValue for the global variable 'my_char'.
50        val = frame.FindValue("my_char", lldb.eValueTypeVariableGlobal)
51        self.DebugSBValue(val)
52
53        # Due to the typemap magic (see lldb.swig), we pass in 1 to ReadMemory and
54        # expect to get a Python string as the result object!
55        error = lldb.SBError()
56        self.assertFalse(val.TypeIsPointerType())
57        content = process.ReadMemory(
58            val.AddressOf().GetValueAsUnsigned(), 1, error)
59        if not error.Success():
60            self.fail("SBProcess.ReadMemory() failed")
61        if self.TraceOn():
62            print("memory content:", content)
63
64        self.expect(
65            content,
66            "Result from SBProcess.ReadMemory() matches our expected output: 'x'",
67            exe=False,
68            startstr=b'x')
69
70        # Read (char *)my_char_ptr.
71        val = frame.FindValue("my_char_ptr", lldb.eValueTypeVariableGlobal)
72        self.DebugSBValue(val)
73        cstring = process.ReadCStringFromMemory(
74            val.GetValueAsUnsigned(), 256, error)
75        if not error.Success():
76            self.fail("SBProcess.ReadCStringFromMemory() failed")
77        if self.TraceOn():
78            print("cstring read is:", cstring)
79
80        self.expect(
81            cstring,
82            "Result from SBProcess.ReadCStringFromMemory() matches our expected output",
83            exe=False,
84            startstr='Does it work?')
85
86        # Get the SBValue for the global variable 'my_cstring'.
87        val = frame.FindValue("my_cstring", lldb.eValueTypeVariableGlobal)
88        self.DebugSBValue(val)
89
90        # Due to the typemap magic (see lldb.swig), we pass in 256 to read at most 256 bytes
91        # from the address, and expect to get a Python string as the result
92        # object!
93        self.assertFalse(val.TypeIsPointerType())
94        cstring = process.ReadCStringFromMemory(
95            val.AddressOf().GetValueAsUnsigned(), 256, error)
96        if not error.Success():
97            self.fail("SBProcess.ReadCStringFromMemory() failed")
98        if self.TraceOn():
99            print("cstring read is:", cstring)
100
101        self.expect(
102            cstring,
103            "Result from SBProcess.ReadCStringFromMemory() matches our expected output",
104            exe=False,
105            startstr='lldb.SBProcess.ReadCStringFromMemory() works!')
106
107        # Get the SBValue for the global variable 'my_uint32'.
108        val = frame.FindValue("my_uint32", lldb.eValueTypeVariableGlobal)
109        self.DebugSBValue(val)
110
111        # Due to the typemap magic (see lldb.swig), we pass in 4 to read 4 bytes
112        # from the address, and expect to get an int as the result!
113        self.assertFalse(val.TypeIsPointerType())
114        my_uint32 = process.ReadUnsignedFromMemory(
115            val.AddressOf().GetValueAsUnsigned(), 4, error)
116        if not error.Success():
117            self.fail("SBProcess.ReadCStringFromMemory() failed")
118        if self.TraceOn():
119            print("uint32 read is:", my_uint32)
120
121        if my_uint32 != 12345:
122            self.fail(
123                "Result from SBProcess.ReadUnsignedFromMemory() does not match our expected output")
124
125    @add_test_categories(['pyapi'])
126    @skipIfReproducer # SBProcess::WriteMemory is not instrumented.
127    def test_write_memory(self):
128        """Test Python SBProcess.WriteMemory() API."""
129        self.build()
130        exe = self.getBuildArtifact("a.out")
131
132        target = self.dbg.CreateTarget(exe)
133        self.assertTrue(target, VALID_TARGET)
134
135        breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line)
136        self.assertTrue(breakpoint, VALID_BREAKPOINT)
137
138        # Launch the process, and do not stop at the entry point.
139        process = target.LaunchSimple(
140            None, None, self.get_process_working_directory())
141
142        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
143        self.assertTrue(
144            thread.IsValid(),
145            "There should be a thread stopped due to breakpoint")
146        frame = thread.GetFrameAtIndex(0)
147
148        # Get the SBValue for the global variable 'my_char'.
149        val = frame.FindValue("my_char", lldb.eValueTypeVariableGlobal)
150        self.DebugSBValue(val)
151
152        # If the variable does not have a load address, there's no sense
153        # continuing.
154        if not val.GetLocation().startswith("0x"):
155            return
156
157        # OK, let's get the hex location of the variable.
158        location = int(val.GetLocation(), 16)
159
160        # The program logic makes the 'my_char' variable to have memory content as 'x'.
161        # But we want to use the WriteMemory() API to assign 'a' to the
162        # variable.
163
164        # Now use WriteMemory() API to write 'a' into the global variable.
165        error = lldb.SBError()
166        result = process.WriteMemory(location, 'a', error)
167        if not error.Success() or result != 1:
168            self.fail("SBProcess.WriteMemory() failed")
169
170        # Read from the memory location.  This time it should be 'a'.
171        # Due to the typemap magic (see lldb.swig), we pass in 1 to ReadMemory and
172        # expect to get a Python string as the result object!
173        content = process.ReadMemory(location, 1, error)
174        if not error.Success():
175            self.fail("SBProcess.ReadMemory() failed")
176        if self.TraceOn():
177            print("memory content:", content)
178
179        self.expect(
180            content,
181            "Result from SBProcess.ReadMemory() matches our expected output: 'a'",
182            exe=False,
183            startstr=b'a')
184
185    @add_test_categories(['pyapi'])
186    @skipIfReproducer # SBProcess::WriteMemory is not instrumented.
187    def test_access_my_int(self):
188        """Test access 'my_int' using Python SBProcess.GetByteOrder() and other APIs."""
189        self.build()
190        exe = self.getBuildArtifact("a.out")
191
192        target = self.dbg.CreateTarget(exe)
193        self.assertTrue(target, VALID_TARGET)
194
195        breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line)
196        self.assertTrue(breakpoint, VALID_BREAKPOINT)
197
198        # Launch the process, and do not stop at the entry point.
199        process = target.LaunchSimple(
200            None, None, self.get_process_working_directory())
201
202        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
203        self.assertTrue(
204            thread.IsValid(),
205            "There should be a thread stopped due to breakpoint")
206        frame = thread.GetFrameAtIndex(0)
207
208        # Get the SBValue for the global variable 'my_int'.
209        val = frame.FindValue("my_int", lldb.eValueTypeVariableGlobal)
210        self.DebugSBValue(val)
211
212        # If the variable does not have a load address, there's no sense
213        # continuing.
214        if not val.GetLocation().startswith("0x"):
215            return
216
217        # OK, let's get the hex location of the variable.
218        location = int(val.GetLocation(), 16)
219
220        # Note that the canonical from of the bytearray is little endian.
221        from lldbsuite.test.lldbutil import int_to_bytearray, bytearray_to_int
222
223        byteSize = val.GetByteSize()
224        bytes = int_to_bytearray(256, byteSize)
225
226        byteOrder = process.GetByteOrder()
227        if byteOrder == lldb.eByteOrderBig:
228            bytes.reverse()
229        elif byteOrder == lldb.eByteOrderLittle:
230            pass
231        else:
232            # Neither big endian nor little endian?  Return for now.
233            # Add more logic here if we want to handle other types.
234            return
235
236        # The program logic makes the 'my_int' variable to have int type and value of 0.
237        # But we want to use the WriteMemory() API to assign 256 to the
238        # variable.
239
240        # Now use WriteMemory() API to write 256 into the global variable.
241        error = lldb.SBError()
242        result = process.WriteMemory(location, bytes, error)
243        if not error.Success() or result != byteSize:
244            self.fail("SBProcess.WriteMemory() failed")
245
246        # Make sure that the val we got originally updates itself to notice the
247        # change:
248        self.expect(
249            val.GetValue(),
250            "SBProcess.ReadMemory() successfully writes (int)256 to the memory location for 'my_int'",
251            exe=False,
252            startstr='256')
253
254        # And for grins, get the SBValue for the global variable 'my_int'
255        # again, to make sure that also tracks the new value:
256        val = frame.FindValue("my_int", lldb.eValueTypeVariableGlobal)
257        self.expect(
258            val.GetValue(),
259            "SBProcess.ReadMemory() successfully writes (int)256 to the memory location for 'my_int'",
260            exe=False,
261            startstr='256')
262
263        # Now read the memory content.  The bytearray should have (byte)1 as
264        # the second element.
265        content = process.ReadMemory(location, byteSize, error)
266        if not error.Success():
267            self.fail("SBProcess.ReadMemory() failed")
268
269        # The bytearray_to_int utility function expects a little endian
270        # bytearray.
271        if byteOrder == lldb.eByteOrderBig:
272            content = bytearray(content, 'ascii')
273            content.reverse()
274
275        new_value = bytearray_to_int(content, byteSize)
276        if new_value != 256:
277            self.fail("Memory content read from 'my_int' does not match (int)256")
278
279        # Dump the memory content....
280        if self.TraceOn():
281            for i in content:
282                print("byte:", i)
283
284    @add_test_categories(['pyapi'])
285    def test_remote_launch(self):
286        """Test SBProcess.RemoteLaunch() API with a process not in eStateConnected, and it should fail."""
287        self.build()
288        exe = self.getBuildArtifact("a.out")
289
290        target = self.dbg.CreateTarget(exe)
291        self.assertTrue(target, VALID_TARGET)
292
293        # Launch the process, and do not stop at the entry point.
294        process = target.LaunchSimple(
295            None, None, self.get_process_working_directory())
296
297        if self.TraceOn():
298            print("process state:", state_type_to_str(process.GetState()))
299        self.assertTrue(process.GetState() != lldb.eStateConnected)
300
301        error = lldb.SBError()
302        success = process.RemoteLaunch(
303            None, None, None, None, None, None, 0, False, error)
304        self.assertTrue(
305            not success,
306            "RemoteLaunch() should fail for process state != eStateConnected")
307
308    @add_test_categories(['pyapi'])
309    def test_get_num_supported_hardware_watchpoints(self):
310        """Test SBProcess.GetNumSupportedHardwareWatchpoints() API with a process."""
311        self.build()
312        exe = self.getBuildArtifact("a.out")
313        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
314
315        target = self.dbg.CreateTarget(exe)
316        self.assertTrue(target, VALID_TARGET)
317
318        breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line)
319        self.assertTrue(breakpoint, VALID_BREAKPOINT)
320
321        # Launch the process, and do not stop at the entry point.
322        process = target.LaunchSimple(
323            None, None, self.get_process_working_directory())
324
325        error = lldb.SBError()
326        num = process.GetNumSupportedHardwareWatchpoints(error)
327        if self.TraceOn() and error.Success():
328            print("Number of supported hardware watchpoints: %d" % num)
329
330    @add_test_categories(['pyapi'])
331    @no_debug_info_test
332    def test_get_process_info(self):
333        """Test SBProcess::GetProcessInfo() API with a locally launched process."""
334        self.build()
335        exe = self.getBuildArtifact("a.out")
336        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
337
338        target = self.dbg.CreateTarget(exe)
339        self.assertTrue(target, VALID_TARGET)
340
341        # Launch the process and stop at the entry point.
342        launch_info = lldb.SBLaunchInfo(None)
343        launch_info.SetWorkingDirectory(self.get_process_working_directory())
344        launch_flags = launch_info.GetLaunchFlags()
345        launch_flags |= lldb.eLaunchFlagStopAtEntry
346        launch_info.SetLaunchFlags(launch_flags)
347        error = lldb.SBError()
348        process = target.Launch(launch_info, error)
349
350        if not error.Success():
351            self.fail("Failed to launch process")
352
353        # Verify basic process info can be retrieved successfully
354        process_info = process.GetProcessInfo()
355        self.assertTrue(process_info.IsValid())
356        file_spec = process_info.GetExecutableFile()
357        self.assertTrue(file_spec.IsValid())
358        process_name = process_info.GetName()
359        self.assertIsNotNone(process_name, "Process has a name")
360        self.assertGreater(len(process_name), 0, "Process name isn't blank")
361        self.assertEqual(file_spec.GetFilename(), "a.out")
362        self.assertNotEqual(
363            process_info.GetProcessID(), lldb.LLDB_INVALID_PROCESS_ID,
364            "Process ID is valid")
365
366        # Additional process info varies by platform, so just check that
367        # whatever info was retrieved is consistent and nothing blows up.
368        if process_info.UserIDIsValid():
369            self.assertNotEqual(
370                process_info.GetUserID(), lldb.UINT32_MAX,
371                "Process user ID is valid")
372        else:
373            self.assertEqual(
374                process_info.GetUserID(), lldb.UINT32_MAX,
375                "Process user ID is invalid")
376
377        if process_info.GroupIDIsValid():
378            self.assertNotEqual(
379                process_info.GetGroupID(), lldb.UINT32_MAX,
380                "Process group ID is valid")
381        else:
382            self.assertEqual(
383                process_info.GetGroupID(), lldb.UINT32_MAX,
384                "Process group ID is invalid")
385
386        if process_info.EffectiveUserIDIsValid():
387            self.assertNotEqual(
388                process_info.GetEffectiveUserID(), lldb.UINT32_MAX,
389                "Process effective user ID is valid")
390        else:
391            self.assertEqual(
392                process_info.GetEffectiveUserID(), lldb.UINT32_MAX,
393                "Process effective user ID is invalid")
394
395        if process_info.EffectiveGroupIDIsValid():
396            self.assertNotEqual(
397                process_info.GetEffectiveGroupID(), lldb.UINT32_MAX,
398                "Process effective group ID is valid")
399        else:
400            self.assertEqual(
401                process_info.GetEffectiveGroupID(), lldb.UINT32_MAX,
402                "Process effective group ID is invalid")
403
404        process_info.GetParentProcessID()
405