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