xref: /llvm-project/lldb/test/API/python_api/process/TestProcessAPI.py (revision 99451b4453688a94c6014cac233d371ab4cc342d)
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    @add_test_categories(['pyapi'])
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    @add_test_categories(['pyapi'])
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    @add_test_categories(['pyapi'])
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    @add_test_categories(['pyapi'])
282    def test_remote_launch(self):
283        """Test SBProcess.RemoteLaunch() API with a process not in eStateConnected, and it should fail."""
284        self.build()
285        exe = self.getBuildArtifact("a.out")
286
287        target = self.dbg.CreateTarget(exe)
288        self.assertTrue(target, VALID_TARGET)
289
290        # Launch the process, and do not stop at the entry point.
291        process = target.LaunchSimple(
292            None, None, self.get_process_working_directory())
293
294        if self.TraceOn():
295            print("process state:", state_type_to_str(process.GetState()))
296        self.assertTrue(process.GetState() != lldb.eStateConnected)
297
298        error = lldb.SBError()
299        success = process.RemoteLaunch(
300            None, None, None, None, None, None, 0, False, error)
301        self.assertTrue(
302            not success,
303            "RemoteLaunch() should fail for process state != eStateConnected")
304
305    @add_test_categories(['pyapi'])
306    def test_get_num_supported_hardware_watchpoints(self):
307        """Test SBProcess.GetNumSupportedHardwareWatchpoints() API with a process."""
308        self.build()
309        exe = self.getBuildArtifact("a.out")
310        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
311
312        target = self.dbg.CreateTarget(exe)
313        self.assertTrue(target, VALID_TARGET)
314
315        breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line)
316        self.assertTrue(breakpoint, VALID_BREAKPOINT)
317
318        # Launch the process, and do not stop at the entry point.
319        process = target.LaunchSimple(
320            None, None, self.get_process_working_directory())
321
322        error = lldb.SBError()
323        num = process.GetNumSupportedHardwareWatchpoints(error)
324        if self.TraceOn() and error.Success():
325            print("Number of supported hardware watchpoints: %d" % num)
326
327    @add_test_categories(['pyapi'])
328    @no_debug_info_test
329    def test_get_process_info(self):
330        """Test SBProcess::GetProcessInfo() API with a locally launched process."""
331        self.build()
332        exe = self.getBuildArtifact("a.out")
333        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
334
335        target = self.dbg.CreateTarget(exe)
336        self.assertTrue(target, VALID_TARGET)
337
338        # Launch the process and stop at the entry point.
339        launch_info = lldb.SBLaunchInfo(None)
340        launch_info.SetWorkingDirectory(self.get_process_working_directory())
341        launch_flags = launch_info.GetLaunchFlags()
342        launch_flags |= lldb.eLaunchFlagStopAtEntry
343        launch_info.SetLaunchFlags(launch_flags)
344        error = lldb.SBError()
345        process = target.Launch(launch_info, error)
346
347        if not error.Success():
348            self.fail("Failed to launch process")
349
350        # Verify basic process info can be retrieved successfully
351        process_info = process.GetProcessInfo()
352        self.assertTrue(process_info.IsValid())
353        file_spec = process_info.GetExecutableFile()
354        self.assertTrue(file_spec.IsValid())
355        process_name = process_info.GetName()
356        self.assertIsNotNone(process_name, "Process has a name")
357        self.assertGreater(len(process_name), 0, "Process name isn't blank")
358        self.assertEqual(file_spec.GetFilename(), "a.out")
359        self.assertNotEqual(
360            process_info.GetProcessID(), lldb.LLDB_INVALID_PROCESS_ID,
361            "Process ID is valid")
362
363        # Additional process info varies by platform, so just check that
364        # whatever info was retrieved is consistent and nothing blows up.
365        if process_info.UserIDIsValid():
366            self.assertNotEqual(
367                process_info.GetUserID(), lldb.UINT32_MAX,
368                "Process user ID is valid")
369        else:
370            self.assertEqual(
371                process_info.GetUserID(), lldb.UINT32_MAX,
372                "Process user ID is invalid")
373
374        if process_info.GroupIDIsValid():
375            self.assertNotEqual(
376                process_info.GetGroupID(), lldb.UINT32_MAX,
377                "Process group ID is valid")
378        else:
379            self.assertEqual(
380                process_info.GetGroupID(), lldb.UINT32_MAX,
381                "Process group ID is invalid")
382
383        if process_info.EffectiveUserIDIsValid():
384            self.assertNotEqual(
385                process_info.GetEffectiveUserID(), lldb.UINT32_MAX,
386                "Process effective user ID is valid")
387        else:
388            self.assertEqual(
389                process_info.GetEffectiveUserID(), lldb.UINT32_MAX,
390                "Process effective user ID is invalid")
391
392        if process_info.EffectiveGroupIDIsValid():
393            self.assertNotEqual(
394                process_info.GetEffectiveGroupID(), lldb.UINT32_MAX,
395                "Process effective group ID is valid")
396        else:
397            self.assertEqual(
398                process_info.GetEffectiveGroupID(), lldb.UINT32_MAX,
399                "Process effective group ID is invalid")
400
401        process_info.GetParentProcessID()
402