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