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