xref: /llvm-project/lldb/test/API/python_api/process/TestProcessAPI.py (revision 8d33437d030af27fff21dd3fd0e66893b0148217)
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_info.SetEnvironmentEntries(["FOO=BAR"], False)
339        launch_info.SetArguments(["--abc"], False)
340        launch_flags = launch_info.GetLaunchFlags()
341        launch_flags |= lldb.eLaunchFlagStopAtEntry
342        launch_info.SetLaunchFlags(launch_flags)
343        error = lldb.SBError()
344        process = target.Launch(launch_info, error)
345
346        if not error.Success():
347            self.fail("Failed to launch process")
348
349        # Verify basic process info can be retrieved successfully
350        process_info = process.GetProcessInfo()
351        self.assertTrue(process_info.IsValid())
352        file_spec = process_info.GetExecutableFile()
353        self.assertTrue(file_spec.IsValid())
354        process_name = process_info.GetName()
355        self.assertIsNotNone(process_name, "Process has a name")
356        self.assertGreater(len(process_name), 0, "Process name isn't blank")
357        self.assertEqual(file_spec.GetFilename(), "a.out")
358        self.assertNotEqual(
359            process_info.GetProcessID(), lldb.LLDB_INVALID_PROCESS_ID,
360            "Process ID is valid")
361        triple = process_info.GetTriple()
362        self.assertIsNotNone(triple, "Process has a triple")
363        env = process_info.GetEnvironment()
364        self.assertGreater(env.GetNumValues(), 0)
365        self.assertEqual("BAR", env.Get("FOO"))
366        self.assertEqual(process_info.GetNumArguments(), 1)
367        self.assertEqual("--abc", process_info.GetArgumentAtIndex(0))
368
369        # Additional process info varies by platform, so just check that
370        # whatever info was retrieved is consistent and nothing blows up.
371        if process_info.UserIDIsValid():
372            self.assertNotEqual(
373                process_info.GetUserID(), lldb.UINT32_MAX,
374                "Process user ID is valid")
375        else:
376            self.assertEqual(
377                process_info.GetUserID(), lldb.UINT32_MAX,
378                "Process user ID is invalid")
379
380        if process_info.GroupIDIsValid():
381            self.assertNotEqual(
382                process_info.GetGroupID(), lldb.UINT32_MAX,
383                "Process group ID is valid")
384        else:
385            self.assertEqual(
386                process_info.GetGroupID(), lldb.UINT32_MAX,
387                "Process group ID is invalid")
388
389        if process_info.EffectiveUserIDIsValid():
390            self.assertNotEqual(
391                process_info.GetEffectiveUserID(), lldb.UINT32_MAX,
392                "Process effective user ID is valid")
393        else:
394            self.assertEqual(
395                process_info.GetEffectiveUserID(), lldb.UINT32_MAX,
396                "Process effective user ID is invalid")
397
398        if process_info.EffectiveGroupIDIsValid():
399            self.assertNotEqual(
400                process_info.GetEffectiveGroupID(), lldb.UINT32_MAX,
401                "Process effective group ID is valid")
402        else:
403            self.assertEqual(
404                process_info.GetEffectiveGroupID(), lldb.UINT32_MAX,
405                "Process effective group ID is invalid")
406
407        process_info.GetParentProcessID()
408