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.get_resolved_path_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 # Check for the module by UUID first in case it has been already loaded in LLDB 232 self.module = lldb.target.AddModule (None, None, str(self.uuid)) 233 if not self.module: 234 if self.fetch_symboled_executable_and_dsym (): 235 resolved_path = self.get_resolved_path(); 236 path_spec = lldb.SBFileSpec (resolved_path) 237 #print 'target.AddModule (path="%s", arch="%s", uuid=%s)' % (resolved_path, self.arch, self.uuid) 238 self.module = lldb.target.AddModule (resolved_path, self.arch, self.uuid) 239 if self.module: 240 err = self.load_module() 241 if err: 242 print err; 243 else: 244 return None 245 else: 246 return 'error: unable to get module for (%s) "%s"' % (self.arch, resolved_path) 247 else: 248 return 'error: invalid target' 249 250 def __init__(self, path): 251 """CrashLog constructor that take a path to a darwin crash log file""" 252 self.path = os.path.expanduser(path); 253 self.info_lines = list() 254 self.system_profile = list() 255 self.threads = list() 256 self.images = list() 257 self.idents = list() # A list of the required identifiers for doing all stack backtraces 258 self.crashed_thread_idx = -1 259 self.version = -1 260 self.error = None 261 # With possible initial component of ~ or ~user replaced by that user's home directory. 262 try: 263 f = open(self.path) 264 except IOError: 265 self.error = 'error: cannot open "%s"' % self.path 266 return 267 268 self.file_lines = f.read().splitlines() 269 parse_mode = PARSE_MODE_NORMAL 270 thread = None 271 for line in self.file_lines: 272 # print line 273 line_len = len(line) 274 if line_len == 0: 275 if thread: 276 if parse_mode == PARSE_MODE_THREAD: 277 if thread.index == self.crashed_thread_idx: 278 thread.reason = '' 279 if self.thread_exception: 280 thread.reason += self.thread_exception 281 if self.thread_exception_data: 282 thread.reason += " (%s)" % self.thread_exception_data 283 self.threads.append(thread) 284 thread = None 285 else: 286 # only append an extra empty line if the previous line 287 # in the info_lines wasn't empty 288 if len(self.info_lines) > 0 and len(self.info_lines[-1]): 289 self.info_lines.append(line) 290 parse_mode = PARSE_MODE_NORMAL 291 # print 'PARSE_MODE_NORMAL' 292 elif parse_mode == PARSE_MODE_NORMAL: 293 if line.startswith ('Process:'): 294 (self.process_name, pid_with_brackets) = line[8:].strip().split() 295 self.process_id = pid_with_brackets.strip('[]') 296 elif line.startswith ('Path:'): 297 self.process_path = line[5:].strip() 298 elif line.startswith ('Identifier:'): 299 self.process_identifier = line[11:].strip() 300 elif line.startswith ('Version:'): 301 (self.process_version, compatability_version) = line[8:].strip().split() 302 self.process_compatability_version = compatability_version.strip('()') 303 elif line.startswith ('Parent Process:'): 304 (self.parent_process_name, pid_with_brackets) = line[15:].strip().split() 305 self.parent_process_id = pid_with_brackets.strip('[]') 306 elif line.startswith ('Exception Type:'): 307 self.thread_exception = line[15:].strip() 308 continue 309 elif line.startswith ('Exception Codes:'): 310 self.thread_exception_data = line[16:].strip() 311 continue 312 elif line.startswith ('Crashed Thread:'): 313 self.crashed_thread_idx = int(line[15:].strip().split()[0]) 314 continue 315 elif line.startswith ('Report Version:'): 316 self.version = int(line[15:].strip()) 317 continue 318 elif line.startswith ('System Profile:'): 319 parse_mode = PARSE_MODE_SYSTEM 320 continue 321 elif (line.startswith ('Interval Since Last Report:') or 322 line.startswith ('Crashes Since Last Report:') or 323 line.startswith ('Per-App Interval Since Last Report:') or 324 line.startswith ('Per-App Crashes Since Last Report:') or 325 line.startswith ('Sleep/Wake UUID:') or 326 line.startswith ('Anonymous UUID:')): 327 # ignore these 328 continue 329 elif line.startswith ('Thread'): 330 thread_state_match = self.thread_state_regex.search (line) 331 if thread_state_match: 332 thread_state_match = self.thread_regex.search (line) 333 thread_idx = int(thread_state_match.group(1)) 334 parse_mode = PARSE_MODE_THREGS 335 thread = self.threads[thread_idx] 336 else: 337 thread_match = self.thread_regex.search (line) 338 if thread_match: 339 # print 'PARSE_MODE_THREAD' 340 parse_mode = PARSE_MODE_THREAD 341 thread_idx = int(thread_match.group(1)) 342 thread = CrashLog.Thread(thread_idx) 343 continue 344 elif line.startswith ('Binary Images:'): 345 parse_mode = PARSE_MODE_IMAGES 346 continue 347 self.info_lines.append(line.strip()) 348 elif parse_mode == PARSE_MODE_THREAD: 349 frame_match = self.frame_regex.search(line) 350 if frame_match: 351 ident = frame_match.group(2) 352 if not ident in self.idents: 353 self.idents.append(ident) 354 thread.frames.append (CrashLog.Frame(int(frame_match.group(1)), int(frame_match.group(3), 0), frame_match.group(4))) 355 else: 356 print 'error: frame regex failed for line: "%s"' % line 357 elif parse_mode == PARSE_MODE_IMAGES: 358 image_match = self.image_regex_uuid.search (line) 359 if image_match: 360 image = CrashLog.Image (int(image_match.group(1),0), 361 int(image_match.group(2),0), 362 image_match.group(3).strip(), 363 image_match.group(4).strip(), 364 image_match.group(5), 365 image_match.group(6)) 366 self.images.append (image) 367 else: 368 image_match = self.image_regex_no_uuid.search (line) 369 if image_match: 370 image = CrashLog.Image (int(image_match.group(1),0), 371 int(image_match.group(2),0), 372 image_match.group(3).strip(), 373 image_match.group(4).strip(), 374 None, 375 image_match.group(5)) 376 self.images.append (image) 377 else: 378 print "error: image regex failed for: %s" % line 379 380 elif parse_mode == PARSE_MODE_THREGS: 381 stripped_line = line.strip() 382 reg_values = stripped_line.split(' ') 383 for reg_value in reg_values: 384 (reg, value) = reg_value.split(': ') 385 thread.registers[reg.strip()] = int(value, 0) 386 elif parse_mode == PARSE_MODE_SYSTEM: 387 self.system_profile.append(line) 388 f.close() 389 390 def dump(self): 391 print "Crash Log File: %s" % (self.path) 392 print "\nThreads:" 393 for thread in self.threads: 394 thread.dump(' ') 395 print "\nImages:" 396 for image in self.images: 397 image.dump(' ') 398 399 def find_image_with_identifier(self, ident): 400 for image in self.images: 401 if image.ident == ident: 402 return image 403 return None 404 405 def create_target(self): 406 if not self.images: 407 return 'error: no images in crash log' 408 exe_path = self.images[0].get_resolved_path() 409 err = self.images[0].create_target () 410 if not err: 411 return None # success 412 # We weren't able to open the main executable as, but we can still symbolicate 413 if self.idents: 414 for ident in self.idents: 415 image = self.find_image_with_identifier (ident) 416 if image: 417 err = image.create_target () 418 if not err: 419 return None # success 420 for image in self.images: 421 err = image.create_target () 422 if not err: 423 return None # success 424 return 'error: unable to locate any executables from the crash log' 425 426def disassemble_instructions (instructions, pc, options, non_zeroeth_frame): 427 lines = list() 428 pc_index = -1 429 comment_column = 50 430 for inst_idx, inst in enumerate(instructions): 431 inst_pc = inst.GetAddress().GetLoadAddress(lldb.target); 432 if pc == inst_pc: 433 pc_index = inst_idx 434 mnemonic = inst.GetMnemonic (lldb.target) 435 operands = inst.GetOperands (lldb.target) 436 comment = inst.GetComment (lldb.target) 437 #data = inst.GetData (lldb.target) 438 lines.append ("%#16.16x: %8s %s" % (inst_pc, mnemonic, operands)) 439 if comment: 440 line_len = len(lines[-1]) 441 if line_len < comment_column: 442 lines[-1] += ' ' * (comment_column - line_len) 443 lines[-1] += "; %s" % comment 444 445 if pc_index >= 0: 446 # If we are disassembling the non-zeroeth frame, we need to backup the PC by 1 447 if non_zeroeth_frame and pc_index > 0: 448 pc_index = pc_index - 1 449 if options.disassemble_before == -1: 450 start_idx = 0 451 else: 452 start_idx = pc_index - options.disassemble_before 453 if start_idx < 0: 454 start_idx = 0 455 if options.disassemble_before == -1: 456 end_idx = inst_idx 457 else: 458 end_idx = pc_index + options.disassemble_after 459 if end_idx > inst_idx: 460 end_idx = inst_idx 461 for i in range(start_idx, end_idx+1): 462 if i == pc_index: 463 print ' -> ', lines[i] 464 else: 465 print ' ', lines[i] 466 467def print_module_section_data (section): 468 print section 469 section_data = section.GetSectionData() 470 if section_data: 471 ostream = lldb.SBStream() 472 section_data.GetDescription (ostream, section.GetFileAddress()) 473 print ostream.GetData() 474 475def print_module_section (section, depth): 476 print section 477 478 if depth > 0: 479 num_sub_sections = section.GetNumSubSections() 480 for sect_idx in range(num_sub_sections): 481 print_module_section (section.GetSubSectionAtIndex(sect_idx), depth - 1) 482 483def print_module_sections (module, depth): 484 for sect in module.section_iter(): 485 print_module_section (sect, depth) 486 487def print_module_symbols (module): 488 for sym in module: 489 print sym 490 491def usage(): 492 print "Usage: lldb-symbolicate.py [-n name] executable-image" 493 sys.exit(0) 494 495def Symbolicate(debugger, command, result, dict): 496 try: 497 SymbolicateCrashLog (shlex.split(command)) 498 except: 499 result.PutCString ("error: python exception %s" % sys.exc_info()[0]) 500 501def SymbolicateCrashLog(command_args): 502 usage = "usage: %prog [options] <FILE> [FILE ...]" 503 description='''Symbolicate one or more darwin crash log files to provide source file and line information, 504inlined stack frames back to the concrete functions, and disassemble the location of the crash 505for the first frame of the crashed thread. 506If this script is imported into the LLDB command interpreter, a "crashlog" command will be added to the interpreter 507for use at the LLDB command line. After a crash log has been parsed and symbolicated, a target will have been 508created that has all of the shared libraries loaded at the load addresses found in the crash log file. This allows 509you to explore the program as if it were stopped at the locations described in the crash log and functions can 510be disassembled and lookups can be performed using the addresses found in the crash log.''' 511 parser = optparse.OptionParser(description=description, prog='crashlog.py',usage=usage) 512 parser.add_option('--platform', type='string', metavar='platform', dest='platform', help='specify one platform by name') 513 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) 514 parser.add_option('--no-images', action='store_false', dest='show_images', help='don\'t show images in stack frames', default=True) 515 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) 516 parser.add_option('--image-list', action='store_true', dest='dump_image_list', help='show image list', default=False) 517 parser.add_option('-g', '--debug-delay', type='int', dest='debug_delay', metavar='NSEC', help='pause for NSEC seconds for debugger', default=0) 518 parser.add_option('-c', '--crashed-only', action='store_true', dest='crashed_only', help='only symbolicate the crashed thread', default=False) 519 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) 520 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) 521 parser.add_option('-B', '--disasm-before', type='int', dest='disassemble_before', help='the number of instructions to disassemble before the frame PC', default=4) 522 parser.add_option('-A', '--disasm-after', type='int', dest='disassemble_after', help='the number of instructions to disassemble after the frame PC', default=4) 523 loaded_addresses = False 524 try: 525 (options, args) = parser.parse_args(command_args) 526 except: 527 return 528 529 if options.verbose: 530 print 'command_args = %s' % command_args 531 print 'options', options 532 print 'args', args 533 534 if options.debug_delay > 0: 535 print "Waiting %u seconds for debugger to attach..." % options.debug_delay 536 time.sleep(options.debug_delay) 537 error = lldb.SBError() 538 if args: 539 for crash_log_file in args: 540 crash_log = CrashLog(crash_log_file) 541 if crash_log.error: 542 print crash_log.error 543 return 544 if options.verbose: 545 crash_log.dump() 546 if not crash_log.images: 547 print 'error: no images in crash log' 548 return 549 550 err = crash_log.create_target () 551 if err: 552 print err 553 return 554 555 exe_module = lldb.target.GetModuleAtIndex(0) 556 images_to_load = list() 557 loaded_image_paths = list() 558 if options.load_all_images: 559 # --load-all option was specified, load everything up 560 for image in crash_log.images: 561 images_to_load.append(image) 562 else: 563 # Only load the images found in stack frames for the crashed threads 564 for ident in crash_log.idents: 565 image = crash_log.find_image_with_identifier (ident) 566 if image: 567 images_to_load.append(image) 568 else: 569 print 'error: can\'t find image for identifier "%s"' % ident 570 571 for image in images_to_load: 572 if image.path in loaded_image_paths: 573 print "warning: skipping %s loaded at %#16.16x duplicate entry (probably commpage)" % (image.path, image.text_addr_lo) 574 else: 575 err = image.add_target_module () 576 if err: 577 print err 578 else: 579 loaded_image_paths.append(image.path) 580 581 for line in crash_log.info_lines: 582 print line 583 584 # Reconstruct inlined frames for all threads for anything that has debug info 585 for thread in crash_log.threads: 586 if options.crashed_only and thread.did_crash() == False: 587 continue 588 # start a new frame list that we will fixup for each thread 589 new_thread_frames = list() 590 # Iterate through all concrete frames for a thread and resolve 591 # any parent frames of inlined functions 592 for frame_idx, frame in enumerate(thread.frames): 593 # Resolve the frame's pc into a section + offset address 'pc_addr' 594 pc_addr = lldb.target.ResolveLoadAddress (frame.pc) 595 # Check to see if we were able to resolve the address 596 if pc_addr: 597 # We were able to resolve the frame's PC into a section offset 598 # address. 599 600 # Resolve the frame's PC value into a symbol context. A symbol 601 # context can resolve a module, compile unit, function, block, 602 # line table entry and/or symbol. If the frame has a block, then 603 # we can look for inlined frames, which are represented by blocks 604 # that have inlined information in them 605 frame.sym_ctx = lldb.target.ResolveSymbolContextForAddress (pc_addr, lldb.eSymbolContextEverything); 606 607 # dump if the verbose option was specified 608 if options.verbose: 609 print "frame.pc = %#16.16x (file_addr = %#16.16x)" % (frame.pc, pc_addr.GetFileAddress()) 610 print "frame.pc_addr = ", pc_addr 611 print "frame.sym_ctx = " 612 print frame.sym_ctx 613 print 614 615 # Append the frame we already had from the crash log to the new 616 # frames list 617 new_thread_frames.append(frame) 618 619 new_frame = CrashLog.Frame (frame.index, -1, None) 620 621 # Try and use the current frame's symbol context to calculate a 622 # parent frame for an inlined function. If the curent frame is 623 # inlined, it will return a valid symbol context for the parent 624 # frame of the current inlined function 625 parent_pc_addr = lldb.SBAddress() 626 new_frame.sym_ctx = frame.sym_ctx.GetParentOfInlinedScope (pc_addr, parent_pc_addr) 627 628 # See if we were able to reconstruct anything? 629 while new_frame.sym_ctx: 630 # We have a parent frame of an inlined frame, create a new frame 631 # Convert the section + offset 'parent_pc_addr' to a load address 632 new_frame.pc = parent_pc_addr.GetLoadAddress(lldb.target) 633 # push the new frame onto the new frame stack 634 new_thread_frames.append (new_frame) 635 # dump if the verbose option was specified 636 if options.verbose: 637 print "new_frame.pc = %#16.16x (%s)" % (new_frame.pc, parent_pc_addr) 638 print "new_frame.sym_ctx = " 639 print new_frame.sym_ctx 640 print 641 # Create another new frame in case we have multiple inlined frames 642 prev_new_frame = new_frame 643 new_frame = CrashLog.Frame (frame.index, -1, None) 644 # Swap the addresses so we can try another inlined lookup 645 pc_addr = parent_pc_addr; 646 new_frame.sym_ctx = prev_new_frame.sym_ctx.GetParentOfInlinedScope (pc_addr, parent_pc_addr) 647 # Replace our thread frames with our new list that includes parent 648 # frames for inlined functions 649 thread.frames = new_thread_frames 650 # Now iterate through all threads and display our richer stack backtraces 651 for thread in crash_log.threads: 652 this_thread_crashed = thread.did_crash() 653 if options.crashed_only and this_thread_crashed == False: 654 continue 655 print "%s" % thread 656 prev_frame_index = -1 657 for frame_idx, frame in enumerate(thread.frames): 658 details = ' %s' % frame.details 659 module = frame.sym_ctx.GetModule() 660 instructions = None 661 if module: 662 module_basename = module.GetFileSpec().GetFilename(); 663 function_start_load_addr = -1 664 function_name = None 665 function = frame.sym_ctx.GetFunction() 666 block = frame.sym_ctx.GetBlock() 667 line_entry = frame.sym_ctx.GetLineEntry() 668 symbol = frame.sym_ctx.GetSymbol() 669 inlined_block = block.GetContainingInlinedBlock(); 670 disassemble = (this_thread_crashed or options.disassemble_all_threads) and frame_idx < options.disassemble_depth; 671 if inlined_block: 672 function_name = inlined_block.GetInlinedName(); 673 block_range_idx = inlined_block.GetRangeIndexForBlockAddress (lldb.target.ResolveLoadAddress (frame.pc)) 674 if block_range_idx < lldb.UINT32_MAX: 675 block_range_start_addr = inlined_block.GetRangeStartAddress (block_range_idx) 676 function_start_load_addr = block_range_start_addr.GetLoadAddress (lldb.target) 677 else: 678 function_start_load_addr = frame.pc 679 if disassemble: 680 instructions = function.GetInstructions(lldb.target) 681 elif function: 682 function_name = function.GetName() 683 function_start_load_addr = function.GetStartAddress().GetLoadAddress (lldb.target) 684 if disassemble: 685 instructions = function.GetInstructions(lldb.target) 686 elif symbol: 687 function_name = symbol.GetName() 688 function_start_load_addr = symbol.GetStartAddress().GetLoadAddress (lldb.target) 689 if disassemble: 690 instructions = symbol.GetInstructions(lldb.target) 691 692 if function_name: 693 # Print the function or symbol name and annotate if it was inlined 694 inline_suffix = '' 695 if inlined_block: 696 inline_suffix = '[inlined] ' 697 else: 698 inline_suffix = ' ' 699 if options.show_images: 700 details = "%s%s`%s" % (inline_suffix, module_basename, function_name) 701 else: 702 details = "%s" % (function_name) 703 # Dump the offset from the current function or symbol if it is non zero 704 function_offset = frame.pc - function_start_load_addr 705 if function_offset > 0: 706 details += " + %u" % (function_offset) 707 elif function_offset < 0: 708 defaults += " %i (invalid negative offset, file a bug) " % function_offset 709 # Print out any line information if any is available 710 if line_entry.GetFileSpec(): 711 details += ' at %s' % line_entry.GetFileSpec().GetFilename() 712 details += ':%u' % line_entry.GetLine () 713 column = line_entry.GetColumn() 714 if column > 0: 715 details += ':%u' % column 716 717 718 # Only print out the concrete frame index if it changes. 719 # if prev_frame_index != frame.index: 720 # print "[%2u] %#16.16x %s" % (frame.index, frame.pc, details) 721 # else: 722 # print " %#16.16x %s" % (frame.pc, details) 723 print "[%2u] %#16.16x %s" % (frame.index, frame.pc, details) 724 prev_frame_index = frame.index 725 if instructions: 726 print 727 disassemble_instructions (instructions, frame.pc, options, frame.index > 0) 728 print 729 730 print 731 732 if options.dump_image_list: 733 print "Binary Images:" 734 for image in crash_log.images: 735 print image 736 737 738if __name__ == '__main__': 739 # Create a new debugger instance 740 lldb.debugger = lldb.SBDebugger.Create() 741 SymbolicateCrashLog (sys.argv) 742elif lldb.debugger: 743 lldb.debugger.HandleCommand('command script add -f crashlog.Symbolicate crashlog') 744 print '"crashlog" command installed, type "crashlog --help" for detailed help' 745 746