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