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