xref: /llvm-project/lldb/test/API/python_api/process/TestProcessAPI.py (revision 66b829ac7b6864f4546771668739d80fa34a7e17)
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    @skipIfRemote
324    def test_get_process_info(self):
325        """Test SBProcess::GetProcessInfo() API with a locally launched 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        # Launch the process and stop at the entry point.
334        launch_info = target.GetLaunchInfo()
335        launch_info.SetWorkingDirectory(self.get_process_working_directory())
336        launch_flags = launch_info.GetLaunchFlags()
337        launch_flags |= lldb.eLaunchFlagStopAtEntry
338        launch_info.SetLaunchFlags(launch_flags)
339        error = lldb.SBError()
340        process = target.Launch(launch_info, error)
341
342        if not error.Success():
343            self.fail("Failed to launch process")
344
345        # Verify basic process info can be retrieved successfully
346        process_info = process.GetProcessInfo()
347        self.assertTrue(process_info.IsValid())
348        file_spec = process_info.GetExecutableFile()
349        self.assertTrue(file_spec.IsValid())
350        process_name = process_info.GetName()
351        self.assertIsNotNone(process_name, "Process has a name")
352        self.assertGreater(len(process_name), 0, "Process name isn't blank")
353        self.assertEqual(file_spec.GetFilename(), "a.out")
354        self.assertNotEqual(
355            process_info.GetProcessID(), lldb.LLDB_INVALID_PROCESS_ID,
356            "Process ID is valid")
357        triple = process_info.GetTriple()
358        self.assertIsNotNone(triple, "Process has a triple")
359
360        # Additional process info varies by platform, so just check that
361        # whatever info was retrieved is consistent and nothing blows up.
362        if process_info.UserIDIsValid():
363            self.assertNotEqual(
364                process_info.GetUserID(), lldb.UINT32_MAX,
365                "Process user ID is valid")
366        else:
367            self.assertEqual(
368                process_info.GetUserID(), lldb.UINT32_MAX,
369                "Process user ID is invalid")
370
371        if process_info.GroupIDIsValid():
372            self.assertNotEqual(
373                process_info.GetGroupID(), lldb.UINT32_MAX,
374                "Process group ID is valid")
375        else:
376            self.assertEqual(
377                process_info.GetGroupID(), lldb.UINT32_MAX,
378                "Process group ID is invalid")
379
380        if process_info.EffectiveUserIDIsValid():
381            self.assertNotEqual(
382                process_info.GetEffectiveUserID(), lldb.UINT32_MAX,
383                "Process effective user ID is valid")
384        else:
385            self.assertEqual(
386                process_info.GetEffectiveUserID(), lldb.UINT32_MAX,
387                "Process effective user ID is invalid")
388
389        if process_info.EffectiveGroupIDIsValid():
390            self.assertNotEqual(
391                process_info.GetEffectiveGroupID(), lldb.UINT32_MAX,
392                "Process effective group ID is valid")
393        else:
394            self.assertEqual(
395                process_info.GetEffectiveGroupID(), lldb.UINT32_MAX,
396                "Process effective group ID is invalid")
397
398        process_info.GetParentProcessID()
399
400    def test_allocate_deallocate_memory(self):
401        """Test Python SBProcess.AllocateMemory() and SBProcess.DeallocateMemory() APIs."""
402        self.build()
403        (target, process, main_thread, main_breakpoint) = lldbutil.run_to_source_breakpoint(
404            self, "// Set break point at this line", lldb.SBFileSpec("main.cpp"))
405
406        # Allocate a block of memory in the target process
407        error = lldb.SBError()
408        addr = process.AllocateMemory(16384, lldb.ePermissionsReadable, error)
409        if not error.Success() or addr == lldb.LLDB_INVALID_ADDRESS:
410            self.fail("SBProcess.AllocateMemory() failed")
411
412        # Now use WriteMemory() API to write 'a' into the allocated
413        # memory. Note that the debugger can do this even though the
414        # block is not set writable.
415        result = process.WriteMemory(addr, 'a', error)
416        if not error.Success() or result != 1:
417            self.fail("SBProcess.WriteMemory() failed")
418
419        # Read from the memory location.  This time it should be 'a'.
420        # Due to the typemap magic (see lldb.swig), we pass in 1 to ReadMemory and
421        # expect to get a Python string as the result object!
422        content = process.ReadMemory(addr, 1, error)
423        if not error.Success():
424            self.fail("SBProcess.ReadMemory() failed")
425        if self.TraceOn():
426            print("memory content:", content)
427
428        self.expect(
429            content,
430            "Result from SBProcess.ReadMemory() matches our expected output: 'a'",
431            exe=False,
432            startstr=b'a')
433
434        # Verify that the process itself can read the allocated memory
435        frame = main_thread.GetFrameAtIndex(0)
436        val = frame.EvaluateExpression(
437            "test_read(reinterpret_cast<char *>({:#x}))".format(addr))
438        self.expect(val.GetValue(),
439                    "Result of test_read() matches expected output 'a'",
440                    exe=False,
441                    startstr="'a'")
442
443        # Verify that the process cannot write into the block
444        val = frame.EvaluateExpression(
445            "test_write(reinterpret_cast<char *>({:#x}), 'b')".format(addr))
446        if val.GetError().Success():
447            self.fail(
448                "test_write() to allocated memory without write permission unexpectedly succeeded")
449
450        # Deallocate the memory
451        error = process.DeallocateMemory(addr)
452        if not error.Success():
453            self.fail("SBProcess.DeallocateMemory() failed")
454