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