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_flags = launch_info.GetLaunchFlags() 339 launch_flags |= lldb.eLaunchFlagStopAtEntry 340 launch_info.SetLaunchFlags(launch_flags) 341 error = lldb.SBError() 342 process = target.Launch(launch_info, error) 343 344 if not error.Success(): 345 self.fail("Failed to launch process") 346 347 # Verify basic process info can be retrieved successfully 348 process_info = process.GetProcessInfo() 349 self.assertTrue(process_info.IsValid()) 350 file_spec = process_info.GetExecutableFile() 351 self.assertTrue(file_spec.IsValid()) 352 process_name = process_info.GetName() 353 self.assertIsNotNone(process_name, "Process has a name") 354 self.assertGreater(len(process_name), 0, "Process name isn't blank") 355 self.assertEqual(file_spec.GetFilename(), "a.out") 356 self.assertNotEqual( 357 process_info.GetProcessID(), lldb.LLDB_INVALID_PROCESS_ID, 358 "Process ID is valid") 359 triple = process_info.GetTriple() 360 self.assertIsNotNone(triple, "Process has a triple") 361 362 # Additional process info varies by platform, so just check that 363 # whatever info was retrieved is consistent and nothing blows up. 364 if process_info.UserIDIsValid(): 365 self.assertNotEqual( 366 process_info.GetUserID(), lldb.UINT32_MAX, 367 "Process user ID is valid") 368 else: 369 self.assertEqual( 370 process_info.GetUserID(), lldb.UINT32_MAX, 371 "Process user ID is invalid") 372 373 if process_info.GroupIDIsValid(): 374 self.assertNotEqual( 375 process_info.GetGroupID(), lldb.UINT32_MAX, 376 "Process group ID is valid") 377 else: 378 self.assertEqual( 379 process_info.GetGroupID(), lldb.UINT32_MAX, 380 "Process group ID is invalid") 381 382 if process_info.EffectiveUserIDIsValid(): 383 self.assertNotEqual( 384 process_info.GetEffectiveUserID(), lldb.UINT32_MAX, 385 "Process effective user ID is valid") 386 else: 387 self.assertEqual( 388 process_info.GetEffectiveUserID(), lldb.UINT32_MAX, 389 "Process effective user ID is invalid") 390 391 if process_info.EffectiveGroupIDIsValid(): 392 self.assertNotEqual( 393 process_info.GetEffectiveGroupID(), lldb.UINT32_MAX, 394 "Process effective group ID is valid") 395 else: 396 self.assertEqual( 397 process_info.GetEffectiveGroupID(), lldb.UINT32_MAX, 398 "Process effective group ID is invalid") 399 400 process_info.GetParentProcessID() 401 402 def test_allocate_deallocate_memory(self): 403 """Test Python SBProcess.AllocateMemory() and SBProcess.DeallocateMemory() APIs.""" 404 self.build() 405 (target, process, main_thread, main_breakpoint) = lldbutil.run_to_source_breakpoint( 406 self, "// Set break point at this line", lldb.SBFileSpec("main.cpp")) 407 408 # Allocate a block of memory in the target process 409 error = lldb.SBError() 410 addr = process.AllocateMemory(16384, lldb.ePermissionsReadable, error) 411 if not error.Success() or addr == lldb.LLDB_INVALID_ADDRESS: 412 self.fail("SBProcess.AllocateMemory() failed") 413 414 # Now use WriteMemory() API to write 'a' into the allocated 415 # memory. Note that the debugger can do this even though the 416 # block is not set writable. 417 result = process.WriteMemory(addr, 'a', error) 418 if not error.Success() or result != 1: 419 self.fail("SBProcess.WriteMemory() failed") 420 421 # Read from the memory location. This time it should be 'a'. 422 # Due to the typemap magic (see lldb.swig), we pass in 1 to ReadMemory and 423 # expect to get a Python string as the result object! 424 content = process.ReadMemory(addr, 1, error) 425 if not error.Success(): 426 self.fail("SBProcess.ReadMemory() failed") 427 if self.TraceOn(): 428 print("memory content:", content) 429 430 self.expect( 431 content, 432 "Result from SBProcess.ReadMemory() matches our expected output: 'a'", 433 exe=False, 434 startstr=b'a') 435 436 # Verify that the process itself can read the allocated memory 437 frame = main_thread.GetFrameAtIndex(0) 438 val = frame.EvaluateExpression( 439 "test_read(reinterpret_cast<char *>({:#x}))".format(addr)) 440 self.expect(val.GetValue(), 441 "Result of test_read() matches expected output 'a'", 442 exe=False, 443 startstr="'a'") 444 445 # Verify that the process cannot write into the block 446 val = frame.EvaluateExpression( 447 "test_write(reinterpret_cast<char *>({:#x}), 'b')".format(addr)) 448 if val.GetError().Success(): 449 self.fail( 450 "test_write() to allocated memory without write permission unexpectedly succeeded") 451 452 # Deallocate the memory 453 error = process.DeallocateMemory(addr) 454 if not error.Success(): 455 self.fail("SBProcess.DeallocateMemory() failed") 456