xref: /llvm-project/lldb/test/API/python_api/process/TestProcessAPI.py (revision 49470f1eacca6aa5ab0f860347aa282a79323161)
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        # Read (char *)my_char_ptr.
77        val = frame.FindValue("my_char_ptr", lldb.eValueTypeVariableGlobal)
78        self.DebugSBValue(val)
79        cstring = process.ReadCStringFromMemory(
80            val.GetValueAsUnsigned(), 256, error)
81        if not error.Success():
82            self.fail("SBProcess.ReadCStringFromMemory() failed")
83        if self.TraceOn():
84            print("cstring read is:", cstring)
85
86        self.expect(
87            cstring,
88            "Result from SBProcess.ReadCStringFromMemory() matches our expected output",
89            exe=False,
90            startstr='Does it work?')
91
92        # Get the SBValue for the global variable 'my_cstring'.
93        val = frame.FindValue("my_cstring", lldb.eValueTypeVariableGlobal)
94        self.DebugSBValue(val)
95
96        # Due to the typemap magic (see lldb.swig), we pass in 256 to read at most 256 bytes
97        # from the address, and expect to get a Python string as the result
98        # object!
99        self.assertFalse(val.TypeIsPointerType())
100        cstring = process.ReadCStringFromMemory(
101            val.AddressOf().GetValueAsUnsigned(), 256, error)
102        if not error.Success():
103            self.fail("SBProcess.ReadCStringFromMemory() failed")
104        if self.TraceOn():
105            print("cstring read is:", cstring)
106
107        self.expect(
108            cstring,
109            "Result from SBProcess.ReadCStringFromMemory() matches our expected output",
110            exe=False,
111            startstr='lldb.SBProcess.ReadCStringFromMemory() works!')
112
113        # Get the SBValue for the global variable 'my_uint32'.
114        val = frame.FindValue("my_uint32", lldb.eValueTypeVariableGlobal)
115        self.DebugSBValue(val)
116
117        # Due to the typemap magic (see lldb.swig), we pass in 4 to read 4 bytes
118        # from the address, and expect to get an int as the result!
119        self.assertFalse(val.TypeIsPointerType())
120        my_uint32 = process.ReadUnsignedFromMemory(
121            val.AddressOf().GetValueAsUnsigned(), 4, error)
122        if not error.Success():
123            self.fail("SBProcess.ReadCStringFromMemory() failed")
124        if self.TraceOn():
125            print("uint32 read is:", my_uint32)
126
127        if my_uint32 != 12345:
128            self.fail(
129                "Result from SBProcess.ReadUnsignedFromMemory() does not match our expected output")
130
131    def test_write_memory(self):
132        """Test Python SBProcess.WriteMemory() API."""
133        self.build()
134        exe = self.getBuildArtifact("a.out")
135
136        target = self.dbg.CreateTarget(exe)
137        self.assertTrue(target, VALID_TARGET)
138
139        breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line)
140        self.assertTrue(breakpoint, VALID_BREAKPOINT)
141
142        # Launch the process, and do not stop at the entry point.
143        process = target.LaunchSimple(
144            None, None, self.get_process_working_directory())
145
146        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
147        self.assertTrue(
148            thread.IsValid(),
149            "There should be a thread stopped due to breakpoint")
150        frame = thread.GetFrameAtIndex(0)
151
152        # Get the SBValue for the global variable 'my_char'.
153        val = frame.FindValue("my_char", lldb.eValueTypeVariableGlobal)
154        self.DebugSBValue(val)
155
156        # If the variable does not have a load address, there's no sense
157        # continuing.
158        if not val.GetLocation().startswith("0x"):
159            return
160
161        # OK, let's get the hex location of the variable.
162        location = int(val.GetLocation(), 16)
163
164        # The program logic makes the 'my_char' variable to have memory content as 'x'.
165        # But we want to use the WriteMemory() API to assign 'a' to the
166        # variable.
167
168        # Now use WriteMemory() API to write 'a' into the global variable.
169        error = lldb.SBError()
170        result = process.WriteMemory(location, 'a', error)
171        if not error.Success() or result != 1:
172            self.fail("SBProcess.WriteMemory() failed")
173
174        # Read from the memory location.  This time it should be 'a'.
175        # Due to the typemap magic (see lldb.swig), we pass in 1 to ReadMemory and
176        # expect to get a Python string as the result object!
177        content = process.ReadMemory(location, 1, error)
178        if not error.Success():
179            self.fail("SBProcess.ReadMemory() failed")
180        if self.TraceOn():
181            print("memory content:", content)
182
183        self.expect(
184            content,
185            "Result from SBProcess.ReadMemory() matches our expected output: 'a'",
186            exe=False,
187            startstr=b'a')
188
189    def test_access_my_int(self):
190        """Test access 'my_int' using Python SBProcess.GetByteOrder() and other APIs."""
191        self.build()
192        exe = self.getBuildArtifact("a.out")
193
194        target = self.dbg.CreateTarget(exe)
195        self.assertTrue(target, VALID_TARGET)
196
197        breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line)
198        self.assertTrue(breakpoint, VALID_BREAKPOINT)
199
200        # Launch the process, and do not stop at the entry point.
201        process = target.LaunchSimple(
202            None, None, self.get_process_working_directory())
203
204        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
205        self.assertTrue(
206            thread.IsValid(),
207            "There should be a thread stopped due to breakpoint")
208        frame = thread.GetFrameAtIndex(0)
209
210        # Get the SBValue for the global variable 'my_int'.
211        val = frame.FindValue("my_int", lldb.eValueTypeVariableGlobal)
212        self.DebugSBValue(val)
213
214        # If the variable does not have a load address, there's no sense
215        # continuing.
216        if not val.GetLocation().startswith("0x"):
217            return
218
219        # OK, let's get the hex location of the variable.
220        location = int(val.GetLocation(), 16)
221
222        # Note that the canonical from of the bytearray is little endian.
223        from lldbsuite.test.lldbutil import int_to_bytearray, bytearray_to_int
224
225        byteSize = val.GetByteSize()
226        bytes = int_to_bytearray(256, byteSize)
227
228        byteOrder = process.GetByteOrder()
229        if byteOrder == lldb.eByteOrderBig:
230            bytes.reverse()
231        elif byteOrder == lldb.eByteOrderLittle:
232            pass
233        else:
234            # Neither big endian nor little endian?  Return for now.
235            # Add more logic here if we want to handle other types.
236            return
237
238        # The program logic makes the 'my_int' variable to have int type and value of 0.
239        # But we want to use the WriteMemory() API to assign 256 to the
240        # variable.
241
242        # Now use WriteMemory() API to write 256 into the global variable.
243        error = lldb.SBError()
244        result = process.WriteMemory(location, bytes, error)
245        if not error.Success() or result != byteSize:
246            self.fail("SBProcess.WriteMemory() failed")
247
248        # Make sure that the val we got originally updates itself to notice the
249        # change:
250        self.expect(
251            val.GetValue(),
252            "SBProcess.ReadMemory() successfully writes (int)256 to the memory location for 'my_int'",
253            exe=False,
254            startstr='256')
255
256        # And for grins, get the SBValue for the global variable 'my_int'
257        # again, to make sure that also tracks the new value:
258        val = frame.FindValue("my_int", lldb.eValueTypeVariableGlobal)
259        self.expect(
260            val.GetValue(),
261            "SBProcess.ReadMemory() successfully writes (int)256 to the memory location for 'my_int'",
262            exe=False,
263            startstr='256')
264
265        # Now read the memory content.  The bytearray should have (byte)1 as
266        # the second element.
267        content = process.ReadMemory(location, byteSize, error)
268        if not error.Success():
269            self.fail("SBProcess.ReadMemory() failed")
270
271        # The bytearray_to_int utility function expects a little endian
272        # bytearray.
273        if byteOrder == lldb.eByteOrderBig:
274            content = bytearray(content, 'ascii')
275            content.reverse()
276
277        new_value = bytearray_to_int(content, byteSize)
278        if new_value != 256:
279            self.fail("Memory content read from 'my_int' does not match (int)256")
280
281        # Dump the memory content....
282        if self.TraceOn():
283            for i in content:
284                print("byte:", i)
285
286    def test_remote_launch(self):
287        """Test SBProcess.RemoteLaunch() API with a process not in eStateConnected, and it should fail."""
288        self.build()
289        exe = self.getBuildArtifact("a.out")
290
291        target = self.dbg.CreateTarget(exe)
292        self.assertTrue(target, VALID_TARGET)
293
294        # Launch the process, and do not stop at the entry point.
295        process = target.LaunchSimple(
296            None, None, self.get_process_working_directory())
297
298        if self.TraceOn():
299            print("process state:", state_type_to_str(process.GetState()))
300        self.assertTrue(process.GetState() != lldb.eStateConnected)
301
302        error = lldb.SBError()
303        success = process.RemoteLaunch(
304            None, None, None, None, None, None, 0, False, error)
305        self.assertTrue(
306            not success,
307            "RemoteLaunch() should fail for process state != eStateConnected")
308
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    @no_debug_info_test
331    @skipIfRemote
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 = target.GetLaunchInfo()
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        triple = process_info.GetTriple()
366        self.assertIsNotNone(triple, "Process has a triple")
367
368        # Additional process info varies by platform, so just check that
369        # whatever info was retrieved is consistent and nothing blows up.
370        if process_info.UserIDIsValid():
371            self.assertNotEqual(
372                process_info.GetUserID(), lldb.UINT32_MAX,
373                "Process user ID is valid")
374        else:
375            self.assertEqual(
376                process_info.GetUserID(), lldb.UINT32_MAX,
377                "Process user ID is invalid")
378
379        if process_info.GroupIDIsValid():
380            self.assertNotEqual(
381                process_info.GetGroupID(), lldb.UINT32_MAX,
382                "Process group ID is valid")
383        else:
384            self.assertEqual(
385                process_info.GetGroupID(), lldb.UINT32_MAX,
386                "Process group ID is invalid")
387
388        if process_info.EffectiveUserIDIsValid():
389            self.assertNotEqual(
390                process_info.GetEffectiveUserID(), lldb.UINT32_MAX,
391                "Process effective user ID is valid")
392        else:
393            self.assertEqual(
394                process_info.GetEffectiveUserID(), lldb.UINT32_MAX,
395                "Process effective user ID is invalid")
396
397        if process_info.EffectiveGroupIDIsValid():
398            self.assertNotEqual(
399                process_info.GetEffectiveGroupID(), lldb.UINT32_MAX,
400                "Process effective group ID is valid")
401        else:
402            self.assertEqual(
403                process_info.GetEffectiveGroupID(), lldb.UINT32_MAX,
404                "Process effective group ID is invalid")
405
406        process_info.GetParentProcessID()
407
408    def test_allocate_deallocate_memory(self):
409        """Test Python SBProcess.AllocateMemory() and SBProcess.DeallocateMemory() APIs."""
410        self.build()
411        (target, process, main_thread, main_breakpoint) = lldbutil.run_to_source_breakpoint(
412            self, "// Set break point at this line", lldb.SBFileSpec("main.cpp"))
413
414        # Allocate a block of memory in the target process
415        error = lldb.SBError()
416        addr = process.AllocateMemory(16384, lldb.ePermissionsReadable, error)
417        if not error.Success() or addr == lldb.LLDB_INVALID_ADDRESS:
418            self.fail("SBProcess.AllocateMemory() failed")
419
420        # Now use WriteMemory() API to write 'a' into the allocated
421        # memory. Note that the debugger can do this even though the
422        # block is not set writable.
423        result = process.WriteMemory(addr, 'a', error)
424        if not error.Success() or result != 1:
425            self.fail("SBProcess.WriteMemory() failed")
426
427        # Read from the memory location.  This time it should be 'a'.
428        # Due to the typemap magic (see lldb.swig), we pass in 1 to ReadMemory and
429        # expect to get a Python string as the result object!
430        content = process.ReadMemory(addr, 1, error)
431        if not error.Success():
432            self.fail("SBProcess.ReadMemory() failed")
433        if self.TraceOn():
434            print("memory content:", content)
435
436        self.expect(
437            content,
438            "Result from SBProcess.ReadMemory() matches our expected output: 'a'",
439            exe=False,
440            startstr=b'a')
441
442        # Verify that the process itself can read the allocated memory
443        frame = main_thread.GetFrameAtIndex(0)
444        val = frame.EvaluateExpression(
445            "test_read(reinterpret_cast<char *>({:#x}))".format(addr))
446        self.expect(val.GetValue(),
447                    "Result of test_read() matches expected output 'a'",
448                    exe=False,
449                    startstr="'a'")
450
451        # Verify that the process cannot write into the block
452        val = frame.EvaluateExpression(
453            "test_write(reinterpret_cast<char *>({:#x}), 'b')".format(addr))
454        if val.GetError().Success():
455            self.fail(
456                "test_write() to allocated memory without write permission unexpectedly succeeded")
457
458        # Deallocate the memory
459        error = process.DeallocateMemory(addr)
460        if not error.Success():
461            self.fail("SBProcess.DeallocateMemory() failed")
462