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