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 def setUp(self): 14 # Call super's setUp(). 15 TestBase.setUp(self) 16 # Find the line number to break inside main(). 17 self.line = line_number( 18 "main.cpp", "// Set break point at this line and check variable 'my_char'." 19 ) 20 21 def test_scripted_implementation(self): 22 self.build() 23 exe = self.getBuildArtifact("a.out") 24 25 (target, process, _, _) = lldbutil.run_to_source_breakpoint( 26 self, "Set break point", lldb.SBFileSpec("main.cpp") 27 ) 28 29 self.assertTrue(process, PROCESS_IS_VALID) 30 self.assertEqual(process.GetScriptedImplementation(), None) 31 32 def test_read_memory(self): 33 """Test Python SBProcess.ReadMemory() API.""" 34 self.build() 35 exe = self.getBuildArtifact("a.out") 36 37 target = self.dbg.CreateTarget(exe) 38 self.assertTrue(target, VALID_TARGET) 39 40 breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line) 41 self.assertTrue(breakpoint, VALID_BREAKPOINT) 42 43 # Launch the process, and do not stop at the entry point. 44 process = target.LaunchSimple(None, None, self.get_process_working_directory()) 45 46 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) 47 self.assertTrue( 48 thread.IsValid(), "There should be a thread stopped due to breakpoint" 49 ) 50 frame = thread.GetFrameAtIndex(0) 51 52 # Get the SBValue for the file static variable 'my_char'. 53 val = frame.FindValue("my_char", lldb.eValueTypeVariableStatic) 54 self.DebugSBValue(val) 55 56 # Due to the typemap magic (see lldb.swig), we pass in 1 to ReadMemory and 57 # expect to get a Python string as the result object! 58 error = lldb.SBError() 59 self.assertFalse(val.TypeIsPointerType()) 60 content = process.ReadMemory(val.AddressOf().GetValueAsUnsigned(), 1, error) 61 if not error.Success(): 62 self.fail("SBProcess.ReadMemory() failed") 63 if self.TraceOn(): 64 print("memory content:", content) 65 66 self.expect( 67 content, 68 "Result from SBProcess.ReadMemory() matches our expected output: 'x'", 69 exe=False, 70 startstr=b"x", 71 ) 72 73 # Read (char *)my_char_ptr. 74 val = frame.FindValue("my_char_ptr", lldb.eValueTypeVariableGlobal) 75 self.DebugSBValue(val) 76 cstring = process.ReadCStringFromMemory(val.GetValueAsUnsigned(), 256, error) 77 if not error.Success(): 78 self.fail("SBProcess.ReadCStringFromMemory() failed") 79 if self.TraceOn(): 80 print("cstring read is:", cstring) 81 82 self.expect( 83 cstring, 84 "Result from SBProcess.ReadCStringFromMemory() matches our expected output", 85 exe=False, 86 startstr="Does it work?", 87 ) 88 89 # Get the SBValue for the global variable 'my_cstring'. 90 val = frame.FindValue("my_cstring", lldb.eValueTypeVariableGlobal) 91 self.DebugSBValue(val) 92 93 # Due to the typemap magic (see lldb.swig), we pass in 256 to read at most 256 bytes 94 # from the address, and expect to get a Python string as the result 95 # object! 96 self.assertFalse(val.TypeIsPointerType()) 97 cstring = process.ReadCStringFromMemory( 98 val.AddressOf().GetValueAsUnsigned(), 256, error 99 ) 100 if not error.Success(): 101 self.fail("SBProcess.ReadCStringFromMemory() failed") 102 if self.TraceOn(): 103 print("cstring read is:", cstring) 104 105 self.expect( 106 cstring, 107 "Result from SBProcess.ReadCStringFromMemory() matches our expected output", 108 exe=False, 109 startstr="lldb.SBProcess.ReadCStringFromMemory() works!", 110 ) 111 112 # Get the SBValue for the global variable 'my_uint32'. 113 val = frame.FindValue("my_uint32", lldb.eValueTypeVariableGlobal) 114 self.DebugSBValue(val) 115 116 # Due to the typemap magic (see lldb.swig), we pass in 4 to read 4 bytes 117 # from the address, and expect to get an int as the result! 118 self.assertFalse(val.TypeIsPointerType()) 119 my_uint32 = process.ReadUnsignedFromMemory( 120 val.AddressOf().GetValueAsUnsigned(), 4, error 121 ) 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 132 def test_write_memory(self): 133 """Test Python SBProcess.WriteMemory() API.""" 134 self.build() 135 exe = self.getBuildArtifact("a.out") 136 137 target = self.dbg.CreateTarget(exe) 138 self.assertTrue(target, VALID_TARGET) 139 140 breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line) 141 self.assertTrue(breakpoint, VALID_BREAKPOINT) 142 143 # Launch the process, and do not stop at the entry point. 144 process = target.LaunchSimple(None, None, self.get_process_working_directory()) 145 146 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) 147 self.assertTrue( 148 thread.IsValid(), "There should be a thread stopped due to breakpoint" 149 ) 150 frame = thread.GetFrameAtIndex(0) 151 152 # Get the SBValue for the static variable 'my_char'. 153 val = frame.FindValue("my_char", lldb.eValueTypeVariableStatic) 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 190 # Get the SBValue for the global variable 'my_cstring'. 191 val = frame.FindValue("my_cstring", lldb.eValueTypeVariableGlobal) 192 self.DebugSBValue(val) 193 194 addr = val.AddressOf().GetValueAsUnsigned() 195 196 # Write an empty string to memory 197 bytes_written = process.WriteMemoryAsCString(addr, "", error) 198 self.assertEqual(bytes_written, 0) 199 if not error.Success(): 200 self.fail("SBProcess.WriteMemoryAsCString() failed") 201 202 message = "Hello!" 203 bytes_written = process.WriteMemoryAsCString(addr, message, error) 204 self.assertEqual(bytes_written, len(message) + 1) 205 if not error.Success(): 206 self.fail("SBProcess.WriteMemoryAsCString() failed") 207 208 cstring = process.ReadCStringFromMemory( 209 val.AddressOf().GetValueAsUnsigned(), 256, error 210 ) 211 if not error.Success(): 212 self.fail("SBProcess.ReadCStringFromMemory() failed") 213 214 self.assertEqual(cstring, message) 215 216 def test_access_my_int(self): 217 """Test access 'my_int' using Python SBProcess.GetByteOrder() and other APIs.""" 218 self.build() 219 exe = self.getBuildArtifact("a.out") 220 221 target = self.dbg.CreateTarget(exe) 222 self.assertTrue(target, VALID_TARGET) 223 224 breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line) 225 self.assertTrue(breakpoint, VALID_BREAKPOINT) 226 227 # Launch the process, and do not stop at the entry point. 228 process = target.LaunchSimple(None, None, self.get_process_working_directory()) 229 230 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) 231 self.assertTrue( 232 thread.IsValid(), "There should be a thread stopped due to breakpoint" 233 ) 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 283 # And for grins, get the SBValue for the global variable 'my_int' 284 # again, to make sure that also tracks the new value: 285 val = frame.FindValue("my_int", lldb.eValueTypeVariableGlobal) 286 self.expect( 287 val.GetValue(), 288 "SBProcess.ReadMemory() successfully writes (int)256 to the memory location for 'my_int'", 289 exe=False, 290 startstr="256", 291 ) 292 293 # Now read the memory content. The bytearray should have (byte)1 as 294 # the second element. 295 content = process.ReadMemory(location, byteSize, error) 296 if not error.Success(): 297 self.fail("SBProcess.ReadMemory() failed") 298 299 # The bytearray_to_int utility function expects a little endian 300 # bytearray. 301 if byteOrder == lldb.eByteOrderBig: 302 content = bytearray(content, "ascii") 303 content.reverse() 304 305 new_value = bytearray_to_int(content, byteSize) 306 if new_value != 256: 307 self.fail("Memory content read from 'my_int' does not match (int)256") 308 309 # Dump the memory content.... 310 if self.TraceOn(): 311 for i in content: 312 print("byte:", i) 313 314 def test_remote_launch(self): 315 """Test SBProcess.RemoteLaunch() API with a process not in eStateConnected, and it should fail.""" 316 self.build() 317 exe = self.getBuildArtifact("a.out") 318 319 target = self.dbg.CreateTarget(exe) 320 self.assertTrue(target, VALID_TARGET) 321 322 # Launch the process, and do not stop at the entry point. 323 process = target.LaunchSimple(None, None, self.get_process_working_directory()) 324 325 if self.TraceOn(): 326 print("process state:", state_type_to_str(process.GetState())) 327 self.assertNotEqual(process.GetState(), lldb.eStateConnected) 328 329 error = lldb.SBError() 330 success = process.RemoteLaunch( 331 None, None, None, None, None, None, 0, False, error 332 ) 333 self.assertTrue( 334 not success, 335 "RemoteLaunch() should fail for process state != eStateConnected", 336 ) 337 338 def test_get_num_supported_hardware_watchpoints(self): 339 """Test SBProcess.GetNumSupportedHardwareWatchpoints() API with a process.""" 340 self.build() 341 exe = self.getBuildArtifact("a.out") 342 self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) 343 344 target = self.dbg.CreateTarget(exe) 345 self.assertTrue(target, VALID_TARGET) 346 347 breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line) 348 self.assertTrue(breakpoint, VALID_BREAKPOINT) 349 350 # Launch the process, and do not stop at the entry point. 351 process = target.LaunchSimple(None, None, self.get_process_working_directory()) 352 353 error = lldb.SBError() 354 num = process.GetNumSupportedHardwareWatchpoints(error) 355 if self.TraceOn() and error.Success(): 356 print("Number of supported hardware watchpoints: %d" % num) 357 358 @no_debug_info_test 359 @skipIfRemote 360 def test_get_process_info(self): 361 """Test SBProcess::GetProcessInfo() API with a locally launched process.""" 362 self.build() 363 exe = self.getBuildArtifact("a.out") 364 self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) 365 366 target = self.dbg.CreateTarget(exe) 367 self.assertTrue(target, VALID_TARGET) 368 369 # Launch the process and stop at the entry point. 370 launch_info = target.GetLaunchInfo() 371 launch_info.SetWorkingDirectory(self.get_process_working_directory()) 372 launch_flags = launch_info.GetLaunchFlags() 373 launch_flags |= lldb.eLaunchFlagStopAtEntry 374 launch_info.SetLaunchFlags(launch_flags) 375 error = lldb.SBError() 376 process = target.Launch(launch_info, error) 377 378 if not error.Success(): 379 self.fail("Failed to launch process") 380 381 # Verify basic process info can be retrieved successfully 382 process_info = process.GetProcessInfo() 383 self.assertTrue(process_info.IsValid()) 384 file_spec = process_info.GetExecutableFile() 385 self.assertTrue(file_spec.IsValid()) 386 process_name = process_info.GetName() 387 self.assertIsNotNone(process_name, "Process has a name") 388 self.assertGreater(len(process_name), 0, "Process name isn't blank") 389 self.assertEqual(file_spec.GetFilename(), "a.out") 390 self.assertNotEqual( 391 process_info.GetProcessID(), 392 lldb.LLDB_INVALID_PROCESS_ID, 393 "Process ID is valid", 394 ) 395 triple = process_info.GetTriple() 396 self.assertIsNotNone(triple, "Process has a triple") 397 398 # Additional process info varies by platform, so just check that 399 # whatever info was retrieved is consistent and nothing blows up. 400 if process_info.UserIDIsValid(): 401 self.assertNotEqual( 402 process_info.GetUserID(), lldb.UINT32_MAX, "Process user ID is valid" 403 ) 404 else: 405 self.assertEqual( 406 process_info.GetUserID(), lldb.UINT32_MAX, "Process user ID is invalid" 407 ) 408 409 if process_info.GroupIDIsValid(): 410 self.assertNotEqual( 411 process_info.GetGroupID(), lldb.UINT32_MAX, "Process group ID is valid" 412 ) 413 else: 414 self.assertEqual( 415 process_info.GetGroupID(), 416 lldb.UINT32_MAX, 417 "Process group ID is invalid", 418 ) 419 420 if process_info.EffectiveUserIDIsValid(): 421 self.assertNotEqual( 422 process_info.GetEffectiveUserID(), 423 lldb.UINT32_MAX, 424 "Process effective user ID is valid", 425 ) 426 else: 427 self.assertEqual( 428 process_info.GetEffectiveUserID(), 429 lldb.UINT32_MAX, 430 "Process effective user ID is invalid", 431 ) 432 433 if process_info.EffectiveGroupIDIsValid(): 434 self.assertNotEqual( 435 process_info.GetEffectiveGroupID(), 436 lldb.UINT32_MAX, 437 "Process effective group ID is valid", 438 ) 439 else: 440 self.assertEqual( 441 process_info.GetEffectiveGroupID(), 442 lldb.UINT32_MAX, 443 "Process effective group ID is invalid", 444 ) 445 446 process_info.GetParentProcessID() 447 448 def test_allocate_deallocate_memory(self): 449 """Test Python SBProcess.AllocateMemory() and SBProcess.DeallocateMemory() APIs.""" 450 self.build() 451 ( 452 target, 453 process, 454 main_thread, 455 main_breakpoint, 456 ) = lldbutil.run_to_source_breakpoint( 457 self, "// Set break point at this line", lldb.SBFileSpec("main.cpp") 458 ) 459 460 # Allocate a block of memory in the target process 461 error = lldb.SBError() 462 addr = process.AllocateMemory(16384, lldb.ePermissionsReadable, error) 463 if not error.Success() or addr == lldb.LLDB_INVALID_ADDRESS: 464 self.fail("SBProcess.AllocateMemory() failed") 465 466 # Now use WriteMemory() API to write 'a' into the allocated 467 # memory. Note that the debugger can do this even though the 468 # block is not set writable. 469 result = process.WriteMemory(addr, "a", error) 470 if not error.Success() or result != 1: 471 self.fail("SBProcess.WriteMemory() failed") 472 473 # Read from the memory location. This time it should be 'a'. 474 # Due to the typemap magic (see lldb.swig), we pass in 1 to ReadMemory and 475 # expect to get a Python string as the result object! 476 content = process.ReadMemory(addr, 1, error) 477 if not error.Success(): 478 self.fail("SBProcess.ReadMemory() failed") 479 if self.TraceOn(): 480 print("memory content:", content) 481 482 self.expect( 483 content, 484 "Result from SBProcess.ReadMemory() matches our expected output: 'a'", 485 exe=False, 486 startstr=b"a", 487 ) 488 489 # Verify that the process itself can read the allocated memory 490 frame = main_thread.GetFrameAtIndex(0) 491 val = frame.EvaluateExpression( 492 "test_read(reinterpret_cast<char *>({:#x}))".format(addr) 493 ) 494 self.expect( 495 val.GetValue(), 496 "Result of test_read() matches expected output 'a'", 497 exe=False, 498 startstr="'a'", 499 ) 500 501 # Verify that the process cannot write into the block 502 val = frame.EvaluateExpression( 503 "test_write(reinterpret_cast<char *>({:#x}), 'b')".format(addr) 504 ) 505 if val.GetError().Success(): 506 self.fail( 507 "test_write() to allocated memory without write permission unexpectedly succeeded" 508 ) 509 510 # Deallocate the memory 511 error = process.DeallocateMemory(addr) 512 if not error.Success(): 513 self.fail("SBProcess.DeallocateMemory() failed") 514