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