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