1#!/usr/bin/python 2 3#---------------------------------------------------------------------- 4# Be sure to add the python path that points to the LLDB shared library. 5# 6# To use this in the embedded python interpreter using "lldb": 7# 8# cd /path/containing/crashlog.py 9# lldb 10# (lldb) script import crashlog 11# "crashlog" command installed, type "crashlog --help" for detailed help 12# (lldb) crashlog ~/Library/Logs/DiagnosticReports/a.crash 13# 14# The benefit of running the crashlog command inside lldb in the 15# embedded python interpreter is when the command completes, there 16# will be a target with all of the files loaded at the locations 17# described in the crash log. Only the files that have stack frames 18# in the backtrace will be loaded unless the "--load-all" option 19# has been specified. This allows users to explore the program in the 20# state it was in right at crash time. 21# 22# On MacOSX csh, tcsh: 23# ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash ) 24# 25# On MacOSX sh, bash: 26# PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash 27#---------------------------------------------------------------------- 28 29import lldb 30import commands 31import optparse 32import os 33import plistlib 34#import pprint # pp = pprint.PrettyPrinter(indent=4); pp.pprint(command_args) 35import re 36import shlex 37import sys 38import time 39import uuid 40 41 42PARSE_MODE_NORMAL = 0 43PARSE_MODE_THREAD = 1 44PARSE_MODE_IMAGES = 2 45PARSE_MODE_THREGS = 3 46PARSE_MODE_SYSTEM = 4 47 48class CrashLog: 49 """Class that does parses darwin crash logs""" 50 thread_state_regex = re.compile('^Thread ([0-9]+) crashed with') 51 thread_regex = re.compile('^Thread ([0-9]+)([^:]*):(.*)') 52 frame_regex = re.compile('^([0-9]+) +([^ ]+) *\t(0x[0-9a-fA-F]+) +(.*)') 53 image_regex_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^<]+)<([-0-9a-fA-F]+)> (.*)'); 54 image_regex_no_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^/]+)/(.*)'); 55 empty_line_regex = re.compile('^$') 56 57 class Thread: 58 """Class that represents a thread in a darwin crash log""" 59 def __init__(self, index): 60 self.index = index 61 self.frames = list() 62 self.registers = dict() 63 self.reason = None 64 self.queue = None 65 66 def dump(self, prefix): 67 print "%sThread[%u] %s" % (prefix, self.index, self.reason) 68 if self.frames: 69 print "%s Frames:" % (prefix) 70 for frame in self.frames: 71 frame.dump(prefix + ' ') 72 if self.registers: 73 print "%s Registers:" % (prefix) 74 for reg in self.registers.keys(): 75 print "%s %-5s = %#16.16x" % (prefix, reg, self.registers[reg]) 76 77 def did_crash(self): 78 return self.reason != None 79 80 def __str__(self): 81 s = "Thread[%u]" % self.index 82 if self.reason: 83 s += ' %s' % self.reason 84 return s 85 86 87 class Frame: 88 """Class that represents a stack frame in a thread in a darwin crash log""" 89 def __init__(self, index, pc, details): 90 self.index = index 91 self.pc = pc 92 self.sym_ctx = None 93 self.details = details 94 95 def __str__(self): 96 return "[%2u] %#16.16x %s" % (self.index, self.pc, self.details) 97 98 def dump(self, prefix): 99 print "%s%s" % (prefix, self) 100 101 102 class Image: 103 """Class that represents a binary images in a darwin crash log""" 104 dsymForUUIDBinary = os.path.expanduser('~rc/bin/dsymForUUID') 105 if not os.path.exists(dsymForUUIDBinary): 106 dsymForUUIDBinary = commands.getoutput('which dsymForUUID') 107 108 dwarfdump_uuid_regex = re.compile('UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*') 109 110 def __init__(self, text_addr_lo, text_addr_hi, ident, version, uuid, path): 111 self.text_addr_lo = text_addr_lo 112 self.text_addr_hi = text_addr_hi 113 self.ident = ident 114 self.version = version 115 self.arch = None 116 self.uuid = uuid 117 self.path = path 118 self.resolved_path = None 119 self.dsym = None 120 self.module = None 121 122 def dump(self, prefix): 123 print "%s%s" % (prefix, self) 124 125 def __str__(self): 126 return "%#16.16x %s %s" % (self.text_addr_lo, self.uuid, self.get_resolved_path()) 127 128 def get_resolved_path(self): 129 if self.resolved_path: 130 return self.resolved_path 131 elif self.path: 132 return self.path 133 return None 134 135 def get_resolved_path_basename(self): 136 path = self.get_resolved_path() 137 if path: 138 return os.path.basename(path) 139 return None 140 141 def dsym_basename(self): 142 if self.dsym: 143 return os.path.basename(self.dsym) 144 return None 145 146 def fetch_symboled_executable_and_dsym(self): 147 if self.resolved_path: 148 # Don't load a module twice... 149 return 0 150 print 'Locating %s %s...' % (self.uuid, self.path), 151 if os.path.exists(self.dsymForUUIDBinary): 152 dsym_for_uuid_command = '%s %s' % (self.dsymForUUIDBinary, self.uuid) 153 s = commands.getoutput(dsym_for_uuid_command) 154 if s: 155 plist_root = plistlib.readPlistFromString (s) 156 if plist_root: 157 plist = plist_root[self.uuid] 158 if plist: 159 if 'DBGArchitecture' in plist: 160 self.arch = plist['DBGArchitecture'] 161 if 'DBGDSYMPath' in plist: 162 self.dsym = os.path.realpath(plist['DBGDSYMPath']) 163 if 'DBGSymbolRichExecutable' in plist: 164 self.resolved_path = os.path.expanduser (plist['DBGSymbolRichExecutable']) 165 if not self.resolved_path and os.path.exists(self.path): 166 dwarfdump_cmd_output = commands.getoutput('dwarfdump --uuid "%s"' % self.path) 167 self_uuid = uuid.UUID(self.uuid) 168 for line in dwarfdump_cmd_output.splitlines(): 169 match = self.dwarfdump_uuid_regex.search (line) 170 if match: 171 dwarf_uuid_str = match.group(1) 172 dwarf_uuid = uuid.UUID(dwarf_uuid_str) 173 if self_uuid == dwarf_uuid: 174 self.resolved_path = self.path 175 self.arch = match.group(2) 176 break; 177 if not self.resolved_path: 178 print "error: file %s '%s' doesn't match the UUID in the installed file" % (self.uuid, self.path) 179 return 0 180 if (self.resolved_path and os.path.exists(self.resolved_path)) or (self.path and os.path.exists(self.path)): 181 print 'ok' 182 if self.path != self.resolved_path: 183 print ' exe = "%s"' % self.resolved_path 184 if self.dsym: 185 print ' dsym = "%s"' % self.dsym 186 return 1 187 else: 188 return 0 189 190 def load_module(self): 191 if not lldb.target: 192 return 'error: no target' 193 if self.module: 194 text_section = self.module.FindSection ("__TEXT") 195 if text_section: 196 error = lldb.target.SetSectionLoadAddress (text_section, self.text_addr_lo) 197 if error.Success(): 198 #print 'Success: loaded %s.__TEXT = 0x%x' % (self.basename(), self.text_addr_lo) 199 return None 200 else: 201 return 'error: %s' % error.GetCString() 202 else: 203 return 'error: unable to find "__TEXT" section in "%s"' % self.get_resolved_path() 204 else: 205 return 'error: invalid module' 206 207 def create_target(self): 208 if self.fetch_symboled_executable_and_dsym (): 209 resolved_path = self.get_resolved_path(); 210 path_spec = lldb.SBFileSpec (resolved_path) 211 #result.PutCString ('plist[%s] = %s' % (uuid, self.plist)) 212 error = lldb.SBError() 213 lldb.target = lldb.debugger.CreateTarget (resolved_path, self.arch, None, False, error); 214 if lldb.target: 215 self.module = lldb.target.FindModule (path_spec) 216 if self.module: 217 err = self.load_module() 218 if err: 219 print err 220 else: 221 return None 222 else: 223 return 'error: unable to get module for (%s) "%s"' % (self.arch, resolved_path) 224 else: 225 return 'error: unable to create target for (%s) "%s"' % (self.arch, resolved_path) 226 else: 227 return 'error: unable to locate main executable (%s) "%s"' % (self.arch, self.path) 228 229 def add_target_module(self): 230 if lldb.target: 231 if self.fetch_symboled_executable_and_dsym (): 232 resolved_path = self.get_resolved_path(); 233 path_spec = lldb.SBFileSpec (resolved_path) 234 #print 'target.AddModule (path="%s", arch="%s", uuid=%s)' % (resolved_path, self.arch, self.uuid) 235 self.module = lldb.target.AddModule (resolved_path, self.arch, self.uuid) 236 if self.module: 237 err = self.load_module() 238 if err: 239 print err; 240 else: 241 return None 242 else: 243 return 'error: unable to get module for (%s) "%s"' % (self.arch, resolved_path) 244 else: 245 return 'error: invalid target' 246 247 def __init__(self, path): 248 """CrashLog constructor that take a path to a darwin crash log file""" 249 self.path = os.path.expanduser(path); 250 self.info_lines = list() 251 self.system_profile = list() 252 self.threads = list() 253 self.images = list() 254 self.idents = list() # A list of the required identifiers for doing all stack backtraces 255 self.crashed_thread_idx = -1 256 self.version = -1 257 self.error = None 258 # With possible initial component of ~ or ~user replaced by that user's home directory. 259 try: 260 f = open(self.path) 261 except IOError: 262 self.error = 'error: cannot open "%s"' % self.path 263 return 264 265 self.file_lines = f.read().splitlines() 266 parse_mode = PARSE_MODE_NORMAL 267 thread = None 268 for line in self.file_lines: 269 # print line 270 line_len = len(line) 271 if line_len == 0: 272 if thread: 273 if parse_mode == PARSE_MODE_THREAD: 274 if thread.index == self.crashed_thread_idx: 275 thread.reason = '' 276 if self.thread_exception: 277 thread.reason += self.thread_exception 278 if self.thread_exception_data: 279 thread.reason += " (%s)" % self.thread_exception_data 280 self.threads.append(thread) 281 thread = None 282 else: 283 # only append an extra empty line if the previous line 284 # in the info_lines wasn't empty 285 if len(self.info_lines) > 0 and len(self.info_lines[-1]): 286 self.info_lines.append(line) 287 parse_mode = PARSE_MODE_NORMAL 288 # print 'PARSE_MODE_NORMAL' 289 elif parse_mode == PARSE_MODE_NORMAL: 290 if line.startswith ('Process:'): 291 (self.process_name, pid_with_brackets) = line[8:].strip().split() 292 self.process_id = pid_with_brackets.strip('[]') 293 elif line.startswith ('Path:'): 294 self.process_path = line[5:].strip() 295 elif line.startswith ('Identifier:'): 296 self.process_identifier = line[11:].strip() 297 elif line.startswith ('Version:'): 298 (self.process_version, compatability_version) = line[8:].strip().split() 299 self.process_compatability_version = compatability_version.strip('()') 300 elif line.startswith ('Parent Process:'): 301 (self.parent_process_name, pid_with_brackets) = line[15:].strip().split() 302 self.parent_process_id = pid_with_brackets.strip('[]') 303 elif line.startswith ('Exception Type:'): 304 self.thread_exception = line[15:].strip() 305 continue 306 elif line.startswith ('Exception Codes:'): 307 self.thread_exception_data = line[16:].strip() 308 continue 309 elif line.startswith ('Crashed Thread:'): 310 self.crashed_thread_idx = int(line[15:].strip().split()[0]) 311 continue 312 elif line.startswith ('Report Version:'): 313 self.version = int(line[15:].strip()) 314 continue 315 elif line.startswith ('System Profile:'): 316 parse_mode = PARSE_MODE_SYSTEM 317 continue 318 elif (line.startswith ('Interval Since Last Report:') or 319 line.startswith ('Crashes Since Last Report:') or 320 line.startswith ('Per-App Interval Since Last Report:') or 321 line.startswith ('Per-App Crashes Since Last Report:') or 322 line.startswith ('Sleep/Wake UUID:') or 323 line.startswith ('Anonymous UUID:')): 324 # ignore these 325 continue 326 elif line.startswith ('Thread'): 327 thread_state_match = self.thread_state_regex.search (line) 328 if thread_state_match: 329 thread_state_match = self.thread_regex.search (line) 330 thread_idx = int(thread_state_match.group(1)) 331 parse_mode = PARSE_MODE_THREGS 332 thread = self.threads[thread_idx] 333 else: 334 thread_match = self.thread_regex.search (line) 335 if thread_match: 336 # print 'PARSE_MODE_THREAD' 337 parse_mode = PARSE_MODE_THREAD 338 thread_idx = int(thread_match.group(1)) 339 thread = CrashLog.Thread(thread_idx) 340 continue 341 elif line.startswith ('Binary Images:'): 342 parse_mode = PARSE_MODE_IMAGES 343 continue 344 self.info_lines.append(line.strip()) 345 elif parse_mode == PARSE_MODE_THREAD: 346 frame_match = self.frame_regex.search(line) 347 if frame_match: 348 ident = frame_match.group(2) 349 if not ident in self.idents: 350 self.idents.append(ident) 351 thread.frames.append (CrashLog.Frame(int(frame_match.group(1)), int(frame_match.group(3), 0), frame_match.group(4))) 352 else: 353 print 'error: frame regex failed for line: "%s"' % line 354 elif parse_mode == PARSE_MODE_IMAGES: 355 image_match = self.image_regex_uuid.search (line) 356 if image_match: 357 image = CrashLog.Image (int(image_match.group(1),0), 358 int(image_match.group(2),0), 359 image_match.group(3).strip(), 360 image_match.group(4).strip(), 361 image_match.group(5), 362 image_match.group(6)) 363 self.images.append (image) 364 else: 365 image_match = self.image_regex_no_uuid.search (line) 366 if image_match: 367 image = CrashLog.Image (int(image_match.group(1),0), 368 int(image_match.group(2),0), 369 image_match.group(3).strip(), 370 image_match.group(4).strip(), 371 None, 372 image_match.group(5)) 373 self.images.append (image) 374 else: 375 print "error: image regex failed for: %s" % line 376 377 elif parse_mode == PARSE_MODE_THREGS: 378 stripped_line = line.strip() 379 reg_values = stripped_line.split(' ') 380 for reg_value in reg_values: 381 (reg, value) = reg_value.split(': ') 382 thread.registers[reg.strip()] = int(value, 0) 383 elif parse_mode == PARSE_MODE_SYSTEM: 384 self.system_profile.append(line) 385 f.close() 386 387 def dump(self): 388 print "Crash Log File: %s" % (self.path) 389 print "\nThreads:" 390 for thread in self.threads: 391 thread.dump(' ') 392 print "\nImages:" 393 for image in self.images: 394 image.dump(' ') 395 396 def find_image_with_identifier(self, ident): 397 for image in self.images: 398 if image.ident == ident: 399 return image 400 return None 401 402 def create_target(self): 403 if not self.images: 404 return 'error: no images in crash log' 405 exe_path = self.images[0].get_resolved_path() 406 err = self.images[0].create_target () 407 if not err: 408 return None # success 409 # We weren't able to open the main executable as, but we can still symbolicate 410 if self.idents: 411 for ident in self.idents: 412 image = self.find_image_with_identifier (ident) 413 if image: 414 err = image.create_target () 415 if not err: 416 return None # success 417 for image in self.images: 418 err = image.create_target () 419 if not err: 420 return None # success 421 return 'error: unable to locate any executables from the crash log' 422 423def disassemble_instructions (instructions, pc, options, non_zeroeth_frame): 424 lines = list() 425 pc_index = -1 426 comment_column = 50 427 for inst_idx, inst in enumerate(instructions): 428 inst_pc = inst.GetAddress().GetLoadAddress(lldb.target); 429 if pc == inst_pc: 430 pc_index = inst_idx 431 mnemonic = inst.GetMnemonic (lldb.target) 432 operands = inst.GetOperands (lldb.target) 433 comment = inst.GetComment (lldb.target) 434 #data = inst.GetData (lldb.target) 435 lines.append ("%#16.16x: %8s %s" % (inst_pc, mnemonic, operands)) 436 if comment: 437 line_len = len(lines[-1]) 438 if line_len < comment_column: 439 lines[-1] += ' ' * (comment_column - line_len) 440 lines[-1] += "; %s" % comment 441 442 if pc_index >= 0: 443 # If we are disassembling the non-zeroeth frame, we need to backup the PC by 1 444 if non_zeroeth_frame and pc_index > 0: 445 pc_index = pc_index - 1 446 if options.disassemble_before == -1: 447 start_idx = 0 448 else: 449 start_idx = pc_index - options.disassemble_before 450 if start_idx < 0: 451 start_idx = 0 452 if options.disassemble_before == -1: 453 end_idx = inst_idx 454 else: 455 end_idx = pc_index + options.disassemble_after 456 if end_idx > inst_idx: 457 end_idx = inst_idx 458 for i in range(start_idx, end_idx+1): 459 if i == pc_index: 460 print ' -> ', lines[i] 461 else: 462 print ' ', lines[i] 463 464def print_module_section_data (section): 465 print section 466 section_data = section.GetSectionData() 467 if section_data: 468 ostream = lldb.SBStream() 469 section_data.GetDescription (ostream, section.GetFileAddress()) 470 print ostream.GetData() 471 472def print_module_section (section, depth): 473 print section 474 475 if depth > 0: 476 num_sub_sections = section.GetNumSubSections() 477 for sect_idx in range(num_sub_sections): 478 print_module_section (section.GetSubSectionAtIndex(sect_idx), depth - 1) 479 480def print_module_sections (module, depth): 481 for sect in module.section_iter(): 482 print_module_section (sect, depth) 483 484def print_module_symbols (module): 485 for sym in module: 486 print sym 487 488def usage(): 489 print "Usage: lldb-symbolicate.py [-n name] executable-image" 490 sys.exit(0) 491 492def Symbolicate(debugger, command, result, dict): 493 try: 494 SymbolicateCrashLog (shlex.split(command)) 495 except: 496 result.PutCString ("error: python exception %s" % sys.exc_info()[0]) 497 498def SymbolicateCrashLog(command_args): 499 usage = "usage: %prog [options] <FILE> [FILE ...]" 500 description='''Symbolicate one or more darwin crash log files to provide source file and line information, 501inlined stack frames back to the concrete functions, and disassemble the location of the crash 502for the first frame of the crashed thread. 503If this script is imported into the LLDB command interpreter, a "crashlog" command will be added to the interpreter 504for use at the LLDB command line. After a crash log has been parsed and symbolicated, a target will have been 505created that has all of the shared libraries loaded at the load addresses found in the crash log file. This allows 506you to explore the program as if it were stopped at the locations described in the crash log and functions can 507be disassembled and lookups can be performed using the addresses found in the crash log.''' 508 parser = optparse.OptionParser(description=description, prog='crashlog.py',usage=usage) 509 parser.add_option('--platform', type='string', metavar='platform', dest='platform', help='specify one platform by name') 510 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) 511 parser.add_option('--no-images', action='store_false', dest='show_images', help='don\'t show images in stack frames', default=True) 512 parser.add_option('-a', '--load-all', action='store_true', dest='load_all_images', help='load all executable images, not just the images found in the crashed stack frames', default=False) 513 parser.add_option('--image-list', action='store_true', dest='dump_image_list', help='show image list', default=False) 514 parser.add_option('-g', '--debug-delay', type='int', dest='debug_delay', metavar='NSEC', help='pause for NSEC seconds for debugger', default=0) 515 parser.add_option('-c', '--crashed-only', action='store_true', dest='crashed_only', help='only symbolicate the crashed thread', default=False) 516 parser.add_option('-d', '--disasm-depth', type='int', dest='disassemble_depth', help='set the depth in stack frames that should be disassembled (default is 1)', default=1) 517 parser.add_option('-D', '--disasm-all', action='store_true', dest='disassemble_all_threads', help='enabled disassembly of frames on all threads (not just the crashed thread)', default=False) 518 parser.add_option('-B', '--disasm-before', type='int', dest='disassemble_before', help='the number of instructions to disassemble before the frame PC', default=4) 519 parser.add_option('-A', '--disasm-after', type='int', dest='disassemble_after', help='the number of instructions to disassemble after the frame PC', default=4) 520 loaded_addresses = False 521 try: 522 (options, args) = parser.parse_args(command_args) 523 except: 524 return 525 526 if options.verbose: 527 print 'command_args = %s' % command_args 528 print 'options', options 529 print 'args', args 530 531 if options.debug_delay > 0: 532 print "Waiting %u seconds for debugger to attach..." % options.debug_delay 533 time.sleep(options.debug_delay) 534 error = lldb.SBError() 535 if args: 536 for crash_log_file in args: 537 crash_log = CrashLog(crash_log_file) 538 if crash_log.error: 539 print crash_log.error 540 return 541 if options.verbose: 542 crash_log.dump() 543 if not crash_log.images: 544 print 'error: no images in crash log' 545 return 546 547 err = crash_log.create_target () 548 if err: 549 print err 550 return 551 552 exe_module = lldb.target.GetModuleAtIndex(0) 553 images_to_load = list() 554 loaded_image_paths = list() 555 if options.load_all_images: 556 # --load-all option was specified, load everything up 557 for image in crash_log.images: 558 images_to_load.append(image) 559 else: 560 # Only load the images found in stack frames for the crashed threads 561 for ident in crash_log.idents: 562 image = crash_log.find_image_with_identifier (ident) 563 if image: 564 images_to_load.append(image) 565 else: 566 print 'error: can\'t find image for identifier "%s"' % ident 567 568 for image in images_to_load: 569 if image.path in loaded_image_paths: 570 print "warning: skipping %s loaded at %#16.16x duplicate entry (probably commpage)" % (image.path, image.text_addr_lo) 571 else: 572 err = image.add_target_module () 573 if err: 574 print err 575 else: 576 loaded_image_paths.append(image.path) 577 578 for line in crash_log.info_lines: 579 print line 580 581 # Reconstruct inlined frames for all threads for anything that has debug info 582 for thread in crash_log.threads: 583 if options.crashed_only and thread.did_crash() == False: 584 continue 585 # start a new frame list that we will fixup for each thread 586 new_thread_frames = list() 587 # Iterate through all concrete frames for a thread and resolve 588 # any parent frames of inlined functions 589 for frame_idx, frame in enumerate(thread.frames): 590 # Resolve the frame's pc into a section + offset address 'pc_addr' 591 pc_addr = lldb.target.ResolveLoadAddress (frame.pc) 592 # Check to see if we were able to resolve the address 593 if pc_addr: 594 # We were able to resolve the frame's PC into a section offset 595 # address. 596 597 # Resolve the frame's PC value into a symbol context. A symbol 598 # context can resolve a module, compile unit, function, block, 599 # line table entry and/or symbol. If the frame has a block, then 600 # we can look for inlined frames, which are represented by blocks 601 # that have inlined information in them 602 frame.sym_ctx = lldb.target.ResolveSymbolContextForAddress (pc_addr, lldb.eSymbolContextEverything); 603 604 # dump if the verbose option was specified 605 if options.verbose: 606 print "frame.pc = %#16.16x (file_addr = %#16.16x)" % (frame.pc, pc_addr.GetFileAddress()) 607 print "frame.pc_addr = ", pc_addr 608 print "frame.sym_ctx = " 609 print frame.sym_ctx 610 print 611 612 # Append the frame we already had from the crash log to the new 613 # frames list 614 new_thread_frames.append(frame) 615 616 new_frame = CrashLog.Frame (frame.index, -1, None) 617 618 # Try and use the current frame's symbol context to calculate a 619 # parent frame for an inlined function. If the curent frame is 620 # inlined, it will return a valid symbol context for the parent 621 # frame of the current inlined function 622 parent_pc_addr = lldb.SBAddress() 623 new_frame.sym_ctx = frame.sym_ctx.GetParentOfInlinedScope (pc_addr, parent_pc_addr) 624 625 # See if we were able to reconstruct anything? 626 while new_frame.sym_ctx: 627 # We have a parent frame of an inlined frame, create a new frame 628 # Convert the section + offset 'parent_pc_addr' to a load address 629 new_frame.pc = parent_pc_addr.GetLoadAddress(lldb.target) 630 # push the new frame onto the new frame stack 631 new_thread_frames.append (new_frame) 632 # dump if the verbose option was specified 633 if options.verbose: 634 print "new_frame.pc = %#16.16x (%s)" % (new_frame.pc, parent_pc_addr) 635 print "new_frame.sym_ctx = " 636 print new_frame.sym_ctx 637 print 638 # Create another new frame in case we have multiple inlined frames 639 prev_new_frame = new_frame 640 new_frame = CrashLog.Frame (frame.index, -1, None) 641 # Swap the addresses so we can try another inlined lookup 642 pc_addr = parent_pc_addr; 643 new_frame.sym_ctx = prev_new_frame.sym_ctx.GetParentOfInlinedScope (pc_addr, parent_pc_addr) 644 # Replace our thread frames with our new list that includes parent 645 # frames for inlined functions 646 thread.frames = new_thread_frames 647 # Now iterate through all threads and display our richer stack backtraces 648 for thread in crash_log.threads: 649 this_thread_crashed = thread.did_crash() 650 if options.crashed_only and this_thread_crashed == False: 651 continue 652 print "%s" % thread 653 prev_frame_index = -1 654 for frame_idx, frame in enumerate(thread.frames): 655 details = ' %s' % frame.details 656 module = frame.sym_ctx.GetModule() 657 instructions = None 658 if module: 659 module_basename = module.GetFileSpec().GetFilename(); 660 function_start_load_addr = -1 661 function_name = None 662 function = frame.sym_ctx.GetFunction() 663 block = frame.sym_ctx.GetBlock() 664 line_entry = frame.sym_ctx.GetLineEntry() 665 symbol = frame.sym_ctx.GetSymbol() 666 inlined_block = block.GetContainingInlinedBlock(); 667 disassemble = (this_thread_crashed or options.disassemble_all_threads) and frame_idx < options.disassemble_depth; 668 if inlined_block: 669 function_name = inlined_block.GetInlinedName(); 670 block_range_idx = inlined_block.GetRangeIndexForBlockAddress (lldb.target.ResolveLoadAddress (frame.pc)) 671 if block_range_idx < lldb.UINT32_MAX: 672 block_range_start_addr = inlined_block.GetRangeStartAddress (block_range_idx) 673 function_start_load_addr = block_range_start_addr.GetLoadAddress (lldb.target) 674 else: 675 function_start_load_addr = frame.pc 676 if disassemble: 677 instructions = function.GetInstructions(lldb.target) 678 elif function: 679 function_name = function.GetName() 680 function_start_load_addr = function.GetStartAddress().GetLoadAddress (lldb.target) 681 if disassemble: 682 instructions = function.GetInstructions(lldb.target) 683 elif symbol: 684 function_name = symbol.GetName() 685 function_start_load_addr = symbol.GetStartAddress().GetLoadAddress (lldb.target) 686 if disassemble: 687 instructions = symbol.GetInstructions(lldb.target) 688 689 if function_name: 690 # Print the function or symbol name and annotate if it was inlined 691 inline_suffix = '' 692 if inlined_block: 693 inline_suffix = '[inlined] ' 694 else: 695 inline_suffix = ' ' 696 if options.show_images: 697 details = "%s%s`%s" % (inline_suffix, module_basename, function_name) 698 else: 699 details = "%s" % (function_name) 700 # Dump the offset from the current function or symbol if it is non zero 701 function_offset = frame.pc - function_start_load_addr 702 if function_offset > 0: 703 details += " + %u" % (function_offset) 704 elif function_offset < 0: 705 defaults += " %i (invalid negative offset, file a bug) " % function_offset 706 # Print out any line information if any is available 707 if line_entry.GetFileSpec(): 708 details += ' at %s' % line_entry.GetFileSpec().GetFilename() 709 details += ':%u' % line_entry.GetLine () 710 column = line_entry.GetColumn() 711 if column > 0: 712 details += ':%u' % column 713 714 715 # Only print out the concrete frame index if it changes. 716 # if prev_frame_index != frame.index: 717 # print "[%2u] %#16.16x %s" % (frame.index, frame.pc, details) 718 # else: 719 # print " %#16.16x %s" % (frame.pc, details) 720 print "[%2u] %#16.16x %s" % (frame.index, frame.pc, details) 721 prev_frame_index = frame.index 722 if instructions: 723 print 724 disassemble_instructions (instructions, frame.pc, options, frame.index > 0) 725 print 726 727 print 728 729 if options.dump_image_list: 730 print "Binary Images:" 731 for image in crash_log.images: 732 print image 733 734 735if __name__ == '__main__': 736 # Create a new debugger instance 737 lldb.debugger = lldb.SBDebugger.Create() 738 SymbolicateCrashLog (sys.argv) 739elif lldb.debugger: 740 lldb.debugger.HandleCommand('command script add -f crashlog.Symbolicate crashlog') 741 print '"crashlog" command installed, type "crashlog --help" for detailed help' 742 743