1""" 2Test basics of Minidump debugging. 3""" 4 5from six import iteritems 6 7import shutil 8 9import lldb 10from lldbsuite.test.decorators import * 11from lldbsuite.test.lldbtest import * 12from lldbsuite.test import lldbutil 13 14 15class MiniDumpNewTestCase(TestBase): 16 17 NO_DEBUG_INFO_TESTCASE = True 18 19 _linux_x86_64_pid = 29917 20 _linux_x86_64_not_crashed_pid = 29939 21 _linux_x86_64_not_crashed_pid_offset = 0xD967 22 23 def process_from_yaml(self, yaml_file): 24 minidump_path = self.getBuildArtifact(os.path.basename(yaml_file) + ".dmp") 25 self.yaml2obj(yaml_file, minidump_path) 26 self.target = self.dbg.CreateTarget(None) 27 self.process = self.target.LoadCore(minidump_path) 28 return self.process 29 30 def check_state(self): 31 with open(os.devnull) as devnul: 32 # sanitize test output 33 self.dbg.SetOutputFileHandle(devnul, False) 34 self.dbg.SetErrorFileHandle(devnul, False) 35 36 self.assertTrue(self.process.is_stopped) 37 38 # Process.Continue 39 error = self.process.Continue() 40 self.assertFalse(error.Success()) 41 self.assertTrue(self.process.is_stopped) 42 43 # Thread.StepOut 44 thread = self.process.GetSelectedThread() 45 thread.StepOut() 46 self.assertTrue(self.process.is_stopped) 47 48 # command line 49 self.dbg.HandleCommand('s') 50 self.assertTrue(self.process.is_stopped) 51 self.dbg.HandleCommand('c') 52 self.assertTrue(self.process.is_stopped) 53 54 # restore file handles 55 self.dbg.SetOutputFileHandle(None, False) 56 self.dbg.SetErrorFileHandle(None, False) 57 58 def test_loadcore_error_status(self): 59 """Test the SBTarget.LoadCore(core, error) overload.""" 60 minidump_path = self.getBuildArtifact("linux-x86_64.dmp") 61 self.yaml2obj("linux-x86_64.yaml", minidump_path) 62 self.target = self.dbg.CreateTarget(None) 63 error = lldb.SBError() 64 self.process = self.target.LoadCore(minidump_path, error) 65 self.assertTrue(self.process, PROCESS_IS_VALID) 66 self.assertSuccess(error) 67 68 def test_loadcore_error_status_failure(self): 69 """Test the SBTarget.LoadCore(core, error) overload.""" 70 self.target = self.dbg.CreateTarget(None) 71 error = lldb.SBError() 72 self.process = self.target.LoadCore("non-existent.dmp", error) 73 self.assertFalse(self.process, PROCESS_IS_VALID) 74 self.assertTrue(error.Fail()) 75 76 def test_process_info_in_minidump(self): 77 """Test that lldb can read the process information from the Minidump.""" 78 self.process_from_yaml("linux-x86_64.yaml") 79 self.assertTrue(self.process, PROCESS_IS_VALID) 80 self.assertEqual(self.process.GetNumThreads(), 1) 81 self.assertEqual(self.process.GetProcessID(), self._linux_x86_64_pid) 82 self.check_state() 83 84 def test_memory_region_name(self): 85 self.process_from_yaml("regions-linux-map.yaml") 86 result = lldb.SBCommandReturnObject() 87 addr_region_name_pairs = [ 88 ("0x400d9000", "/system/bin/app_process"), 89 ("0x400db000", "/system/bin/app_process"), 90 ("0x400dd000", "/system/bin/linker"), 91 ("0x400ed000", "/system/bin/linker"), 92 ("0x400ee000", "/system/bin/linker"), 93 ("0x400fb000", "/system/lib/liblog.so"), 94 ("0x400fc000", "/system/lib/liblog.so"), 95 ("0x400fd000", "/system/lib/liblog.so"), 96 ("0x400ff000", "/system/lib/liblog.so"), 97 ("0x40100000", "/system/lib/liblog.so"), 98 ("0x40101000", "/system/lib/libc.so"), 99 ("0x40122000", "/system/lib/libc.so"), 100 ("0x40123000", "/system/lib/libc.so"), 101 ("0x40167000", "/system/lib/libc.so"), 102 ("0x40169000", "/system/lib/libc.so"), 103 ] 104 ci = self.dbg.GetCommandInterpreter() 105 for (addr, region_name) in addr_region_name_pairs: 106 command = 'memory region ' + addr 107 ci.HandleCommand(command, result, False) 108 message = 'Ensure memory "%s" shows up in output for "%s"' % ( 109 region_name, command) 110 self.assertIn(region_name, result.GetOutput(), message) 111 112 def test_thread_info_in_minidump(self): 113 """Test that lldb can read the thread information from the Minidump.""" 114 self.process_from_yaml("linux-x86_64.yaml") 115 self.check_state() 116 # This process crashed due to a segmentation fault in its 117 # one and only thread. 118 self.assertEqual(self.process.GetNumThreads(), 1) 119 thread = self.process.GetThreadAtIndex(0) 120 self.assertEqual(thread.GetStopReason(), lldb.eStopReasonSignal) 121 stop_description = thread.GetStopDescription(256) 122 self.assertIn("SIGSEGV", stop_description) 123 124 @skipIfLLVMTargetMissing("X86") 125 def test_stack_info_in_minidump(self): 126 """Test that we can see a trivial stack in a breakpad-generated Minidump.""" 127 # target create linux-x86_64 -c linux-x86_64.dmp 128 self.dbg.CreateTarget("linux-x86_64") 129 self.target = self.dbg.GetSelectedTarget() 130 self.process = self.target.LoadCore("linux-x86_64.dmp") 131 self.check_state() 132 self.assertEqual(self.process.GetNumThreads(), 1) 133 self.assertEqual(self.process.GetProcessID(), self._linux_x86_64_pid) 134 thread = self.process.GetThreadAtIndex(0) 135 # frame #0: linux-x86_64`crash() 136 # frame #1: linux-x86_64`_start 137 self.assertEqual(thread.GetNumFrames(), 2) 138 frame = thread.GetFrameAtIndex(0) 139 self.assertTrue(frame.IsValid()) 140 self.assertTrue(frame.GetModule().IsValid()) 141 pc = frame.GetPC() 142 eip = frame.FindRegister("pc") 143 self.assertTrue(eip.IsValid()) 144 self.assertEqual(pc, eip.GetValueAsUnsigned()) 145 146 def test_snapshot_minidump_dump_requested(self): 147 """Test that if we load a snapshot minidump file (meaning the process 148 did not crash) with exception code "DUMP_REQUESTED" there is no stop reason.""" 149 # target create -c linux-x86_64_not_crashed.dmp 150 self.dbg.CreateTarget(None) 151 self.target = self.dbg.GetSelectedTarget() 152 self.process = self.target.LoadCore("linux-x86_64_not_crashed.dmp") 153 self.check_state() 154 self.assertEqual(self.process.GetNumThreads(), 1) 155 thread = self.process.GetThreadAtIndex(0) 156 self.assertEqual(thread.GetStopReason(), lldb.eStopReasonNone) 157 stop_description = thread.GetStopDescription(256) 158 self.assertEqual(stop_description, "") 159 160 def test_snapshot_minidump_null_exn_code(self): 161 """Test that if we load a snapshot minidump file (meaning the process 162 did not crash) with exception code zero there is no stop reason.""" 163 self.process_from_yaml("linux-x86_64_null_signal.yaml") 164 self.check_state() 165 self.assertEqual(self.process.GetNumThreads(), 1) 166 thread = self.process.GetThreadAtIndex(0) 167 self.assertEqual(thread.GetStopReason(), lldb.eStopReasonNone) 168 stop_description = thread.GetStopDescription(256) 169 self.assertEqual(stop_description, "") 170 171 def check_register_unsigned(self, set, name, expected): 172 reg_value = set.GetChildMemberWithName(name) 173 self.assertTrue(reg_value.IsValid(), 174 'Verify we have a register named "%s"' % (name)) 175 self.assertEqual(reg_value.GetValueAsUnsigned(), expected, 176 'Verify "%s" == %i' % (name, expected)) 177 178 def check_register_string_value(self, set, name, expected, format): 179 reg_value = set.GetChildMemberWithName(name) 180 self.assertTrue(reg_value.IsValid(), 181 'Verify we have a register named "%s"' % (name)) 182 if format is not None: 183 reg_value.SetFormat(format) 184 self.assertEqual(reg_value.GetValue(), expected, 185 'Verify "%s" has string value "%s"' % (name, 186 expected)) 187 188 def test_arm64_registers(self): 189 """Test ARM64 registers from a breakpad created minidump.""" 190 self.process_from_yaml("arm64-macos.yaml") 191 self.check_state() 192 self.assertEqual(self.process.GetNumThreads(), 1) 193 thread = self.process.GetThreadAtIndex(0) 194 self.assertEqual(thread.GetStopReason(), lldb.eStopReasonNone) 195 stop_description = thread.GetStopDescription(256) 196 self.assertEqual(stop_description, "") 197 registers = thread.GetFrameAtIndex(0).GetRegisters() 198 # Verify the GPR registers are all correct 199 # Verify x0 - x31 register values 200 gpr = registers.GetValueAtIndex(0) 201 for i in range(32): 202 v = i+1 | i+2 << 32 | i+3 << 48 203 w = i+1 204 self.check_register_unsigned(gpr, 'x%i' % (i), v) 205 self.check_register_unsigned(gpr, 'w%i' % (i), w) 206 # Verify arg1 - arg8 register values 207 for i in range(1, 9): 208 v = i | i+1 << 32 | i+2 << 48 209 self.check_register_unsigned(gpr, 'arg%i' % (i), v) 210 i = 29 211 v = i+1 | i+2 << 32 | i+3 << 48 212 self.check_register_unsigned(gpr, 'fp', v) 213 i = 30 214 v = i+1 | i+2 << 32 | i+3 << 48 215 self.check_register_unsigned(gpr, 'lr', v) 216 i = 31 217 v = i+1 | i+2 << 32 | i+3 << 48 218 self.check_register_unsigned(gpr, 'sp', v) 219 self.check_register_unsigned(gpr, 'pc', 0x1000) 220 self.check_register_unsigned(gpr, 'cpsr', 0x11223344) 221 self.check_register_unsigned(gpr, 'psr', 0x11223344) 222 223 # Verify the FPR registers are all correct 224 fpr = registers.GetValueAtIndex(1) 225 for i in range(32): 226 v = "0x" 227 d = "0x" 228 s = "0x" 229 h = "0x" 230 for j in range(i+15, i-1, -1): 231 v += "%2.2x" % (j) 232 for j in range(i+7, i-1, -1): 233 d += "%2.2x" % (j) 234 for j in range(i+3, i-1, -1): 235 s += "%2.2x" % (j) 236 for j in range(i+1, i-1, -1): 237 h += "%2.2x" % (j) 238 self.check_register_string_value(fpr, "v%i" % (i), v, 239 lldb.eFormatHex) 240 self.check_register_string_value(fpr, "d%i" % (i), d, 241 lldb.eFormatHex) 242 self.check_register_string_value(fpr, "s%i" % (i), s, 243 lldb.eFormatHex) 244 self.check_register_string_value(fpr, "h%i" % (i), h, 245 lldb.eFormatHex) 246 self.check_register_unsigned(gpr, 'fpsr', 0x55667788) 247 self.check_register_unsigned(gpr, 'fpcr', 0x99aabbcc) 248 249 def verify_arm_registers(self, apple=False): 250 """ 251 Verify values of all ARM registers from a breakpad created 252 minidump. 253 """ 254 if apple: 255 self.process_from_yaml("arm-macos.yaml") 256 else: 257 self.process_from_yaml("arm-linux.yaml") 258 self.check_state() 259 self.assertEqual(self.process.GetNumThreads(), 1) 260 thread = self.process.GetThreadAtIndex(0) 261 self.assertEqual(thread.GetStopReason(), lldb.eStopReasonNone) 262 stop_description = thread.GetStopDescription(256) 263 self.assertEqual(stop_description, "") 264 registers = thread.GetFrameAtIndex(0).GetRegisters() 265 # Verify the GPR registers are all correct 266 # Verify x0 - x31 register values 267 gpr = registers.GetValueAtIndex(0) 268 for i in range(1, 16): 269 self.check_register_unsigned(gpr, 'r%i' % (i), i+1) 270 # Verify arg1 - arg4 register values 271 for i in range(1, 5): 272 self.check_register_unsigned(gpr, 'arg%i' % (i), i) 273 if apple: 274 self.check_register_unsigned(gpr, 'fp', 0x08) 275 else: 276 self.check_register_unsigned(gpr, 'fp', 0x0c) 277 self.check_register_unsigned(gpr, 'lr', 0x0f) 278 self.check_register_unsigned(gpr, 'sp', 0x0e) 279 self.check_register_unsigned(gpr, 'pc', 0x10) 280 self.check_register_unsigned(gpr, 'cpsr', 0x11223344) 281 282 # Verify the FPR registers are all correct 283 fpr = registers.GetValueAtIndex(1) 284 # Check d0 - d31 285 self.check_register_unsigned(gpr, 'fpscr', 0x55667788aabbccdd) 286 for i in range(32): 287 value = (i+1) | (i+1) << 8 | (i+1) << 32 | (i+1) << 48 288 self.check_register_unsigned(fpr, "d%i" % (i), value) 289 # Check s0 - s31 290 for i in range(32): 291 i_val = (i >> 1) + 1 292 if i & 1: 293 value = "%#8.8x" % (i_val | i_val << 16) 294 else: 295 value = "%#8.8x" % (i_val | i_val << 8) 296 self.check_register_string_value(fpr, "s%i" % (i), value, 297 lldb.eFormatHex) 298 # Check q0 - q15 299 for i in range(15): 300 a = i * 2 + 1 301 b = a + 1 302 value = ("0x00%2.2x00%2.2x0000%2.2x%2.2x" 303 "00%2.2x00%2.2x0000%2.2x%2.2x") % (b, b, b, b, a, a, a, a) 304 self.check_register_string_value(fpr, "q%i" % (i), value, 305 lldb.eFormatHex) 306 307 def test_linux_arm_registers(self): 308 """Test Linux ARM registers from a breakpad created minidump. 309 310 The frame pointer is R11 for linux. 311 """ 312 self.verify_arm_registers(apple=False) 313 314 def test_apple_arm_registers(self): 315 """Test Apple ARM registers from a breakpad created minidump. 316 317 The frame pointer is R7 for linux. 318 """ 319 self.verify_arm_registers(apple=True) 320 321 def do_test_deeper_stack(self, binary, core, pid): 322 target = self.dbg.CreateTarget(binary) 323 process = target.LoadCore(core) 324 thread = process.GetThreadAtIndex(0) 325 326 self.assertEqual(process.GetProcessID(), pid) 327 328 expected_stack = {1: 'bar', 2: 'foo', 3: '_start'} 329 self.assertGreaterEqual(thread.GetNumFrames(), len(expected_stack)) 330 for index, name in iteritems(expected_stack): 331 frame = thread.GetFrameAtIndex(index) 332 self.assertTrue(frame.IsValid()) 333 function_name = frame.GetFunctionName() 334 self.assertIn(name, function_name) 335 336 @skipIfLLVMTargetMissing("X86") 337 def test_deeper_stack_in_minidump(self): 338 """Test that we can examine a more interesting stack in a Minidump.""" 339 # Launch with the Minidump, and inspect the stack. 340 # target create linux-x86_64_not_crashed -c linux-x86_64_not_crashed.dmp 341 self.do_test_deeper_stack("linux-x86_64_not_crashed", 342 "linux-x86_64_not_crashed.dmp", 343 self._linux_x86_64_not_crashed_pid) 344 345 def do_change_pid_in_minidump(self, core, newcore, offset, oldpid, newpid): 346 """ This assumes that the minidump is breakpad generated on Linux - 347 meaning that the PID in the file will be an ascii string part of 348 /proc/PID/status which is written in the file 349 """ 350 shutil.copyfile(core, newcore) 351 with open(newcore, "rb+") as f: 352 f.seek(offset) 353 currentpid = f.read(5).decode('utf-8') 354 self.assertEqual(currentpid, oldpid) 355 356 f.seek(offset) 357 if len(newpid) < len(oldpid): 358 newpid += " " * (len(oldpid) - len(newpid)) 359 newpid += "\n" 360 f.write(newpid.encode('utf-8')) 361 362 @skipIfLLVMTargetMissing("X86") 363 def test_deeper_stack_in_minidump_with_same_pid_running(self): 364 """Test that we read the information from the core correctly even if we 365 have a running process with the same PID""" 366 new_core = self.getBuildArtifact("linux-x86_64_not_crashed-pid.dmp") 367 self.do_change_pid_in_minidump("linux-x86_64_not_crashed.dmp", 368 new_core, 369 self._linux_x86_64_not_crashed_pid_offset, 370 str(self._linux_x86_64_not_crashed_pid), 371 str(os.getpid())) 372 self.do_test_deeper_stack("linux-x86_64_not_crashed", new_core, os.getpid()) 373 374 @skipIfLLVMTargetMissing("X86") 375 def test_two_cores_same_pid(self): 376 """Test that we handle the situation if we have two core files with the same PID """ 377 new_core = self.getBuildArtifact("linux-x86_64_not_crashed-pid.dmp") 378 self.do_change_pid_in_minidump("linux-x86_64_not_crashed.dmp", 379 new_core, 380 self._linux_x86_64_not_crashed_pid_offset, 381 str(self._linux_x86_64_not_crashed_pid), 382 str(self._linux_x86_64_pid)) 383 self.do_test_deeper_stack("linux-x86_64_not_crashed", 384 new_core, self._linux_x86_64_pid) 385 self.test_stack_info_in_minidump() 386 387 @skipIfLLVMTargetMissing("X86") 388 def test_local_variables_in_minidump(self): 389 """Test that we can examine local variables in a Minidump.""" 390 # Launch with the Minidump, and inspect a local variable. 391 # target create linux-x86_64_not_crashed -c linux-x86_64_not_crashed.dmp 392 self.target = self.dbg.CreateTarget("linux-x86_64_not_crashed") 393 self.process = self.target.LoadCore("linux-x86_64_not_crashed.dmp") 394 self.check_state() 395 thread = self.process.GetThreadAtIndex(0) 396 frame = thread.GetFrameAtIndex(1) 397 value = frame.EvaluateExpression('x') 398 self.assertEqual(value.GetValueAsSigned(), 3) 399 400 def test_memory_regions_in_minidump(self): 401 """Test memory regions from a Minidump""" 402 self.process_from_yaml("regions-linux-map.yaml") 403 self.check_state() 404 405 regions_count = 19 406 region_info_list = self.process.GetMemoryRegions() 407 self.assertEqual(region_info_list.GetSize(), regions_count) 408 409 def check_region(index, start, end, read, write, execute, mapped, name): 410 region_info = lldb.SBMemoryRegionInfo() 411 self.assertTrue( 412 self.process.GetMemoryRegionInfo(start, region_info).Success()) 413 self.assertEqual(start, region_info.GetRegionBase()) 414 self.assertEqual(end, region_info.GetRegionEnd()) 415 self.assertEqual(read, region_info.IsReadable()) 416 self.assertEqual(write, region_info.IsWritable()) 417 self.assertEqual(execute, region_info.IsExecutable()) 418 self.assertEqual(mapped, region_info.IsMapped()) 419 self.assertEqual(name, region_info.GetName()) 420 421 # Ensure we have the same regions as SBMemoryRegionInfoList contains. 422 if index >= 0 and index < regions_count: 423 region_info_from_list = lldb.SBMemoryRegionInfo() 424 self.assertTrue(region_info_list.GetMemoryRegionAtIndex( 425 index, region_info_from_list)) 426 self.assertEqual(region_info_from_list, region_info) 427 428 a = "/system/bin/app_process" 429 b = "/system/bin/linker" 430 c = "/system/lib/liblog.so" 431 d = "/system/lib/libc.so" 432 n = None 433 max_int = 0xffffffffffffffff 434 435 # Test address before the first entry comes back with nothing mapped up 436 # to first valid region info 437 check_region(-1, 0x00000000, 0x400d9000, False, False, False, False, n) 438 check_region( 0, 0x400d9000, 0x400db000, True, False, True, True, a) 439 check_region( 1, 0x400db000, 0x400dc000, True, False, False, True, a) 440 check_region( 2, 0x400dc000, 0x400dd000, True, True, False, True, n) 441 check_region( 3, 0x400dd000, 0x400ec000, True, False, True, True, b) 442 check_region( 4, 0x400ec000, 0x400ed000, True, False, False, True, n) 443 check_region( 5, 0x400ed000, 0x400ee000, True, False, False, True, b) 444 check_region( 6, 0x400ee000, 0x400ef000, True, True, False, True, b) 445 check_region( 7, 0x400ef000, 0x400fb000, True, True, False, True, n) 446 check_region( 8, 0x400fb000, 0x400fc000, True, False, True, True, c) 447 check_region( 9, 0x400fc000, 0x400fd000, True, True, True, True, c) 448 check_region(10, 0x400fd000, 0x400ff000, True, False, True, True, c) 449 check_region(11, 0x400ff000, 0x40100000, True, False, False, True, c) 450 check_region(12, 0x40100000, 0x40101000, True, True, False, True, c) 451 check_region(13, 0x40101000, 0x40122000, True, False, True, True, d) 452 check_region(14, 0x40122000, 0x40123000, True, True, True, True, d) 453 check_region(15, 0x40123000, 0x40167000, True, False, True, True, d) 454 check_region(16, 0x40167000, 0x40169000, True, False, False, True, d) 455 check_region(17, 0x40169000, 0x4016b000, True, True, False, True, d) 456 check_region(18, 0x4016b000, 0x40176000, True, True, False, True, n) 457 check_region(-1, 0x40176000, max_int, False, False, False, False, n) 458 459 @skipIfLLVMTargetMissing("X86") 460 def test_minidump_sysroot(self): 461 """Test that lldb can find a module referenced in an i386 linux minidump using the sysroot.""" 462 463 # Copy linux-x86_64 executable to tmp_sysroot/temp/test/ (since it was compiled as 464 # /tmp/test/linux-x86_64) 465 tmp_sysroot = os.path.join( 466 self.getBuildDir(), "lldb_i386_mock_sysroot") 467 executable = os.path.join( 468 tmp_sysroot, "tmp", "test", "linux-x86_64") 469 exe_dir = os.path.dirname(executable) 470 lldbutil.mkdir_p(exe_dir) 471 shutil.copyfile("linux-x86_64", executable) 472 473 # Set sysroot and load core 474 self.runCmd("platform select remote-linux --sysroot '%s'" % 475 tmp_sysroot) 476 self.process_from_yaml("linux-x86_64.yaml") 477 self.check_state() 478 479 # Check that we loaded the module from the sysroot 480 self.assertEqual(self.target.GetNumModules(), 1) 481 module = self.target.GetModuleAtIndex(0) 482 spec_dir_norm = os.path.normcase(module.GetFileSpec().GetDirectory()) 483 exe_dir_norm = os.path.normcase(exe_dir) 484 self.assertEqual(spec_dir_norm, exe_dir_norm) 485