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