1a5c3f10bSJonas Devlieghere#!/usr/bin/env python3 2aa9d02b1SGreg Clayton 3aa9d02b1SGreg Clayton#---------------------------------------------------------------------- 4aa9d02b1SGreg Clayton# Be sure to add the python path that points to the LLDB shared library. 55c0f483eSGreg Clayton# 65c0f483eSGreg Clayton# To use this in the embedded python interpreter using "lldb": 75c0f483eSGreg Clayton# 85c0f483eSGreg Clayton# cd /path/containing/crashlog.py 95c0f483eSGreg Clayton# lldb 105c0f483eSGreg Clayton# (lldb) script import crashlog 115c0f483eSGreg Clayton# "crashlog" command installed, type "crashlog --help" for detailed help 125c0f483eSGreg Clayton# (lldb) crashlog ~/Library/Logs/DiagnosticReports/a.crash 135c0f483eSGreg Clayton# 145c0f483eSGreg Clayton# The benefit of running the crashlog command inside lldb in the 155c0f483eSGreg Clayton# embedded python interpreter is when the command completes, there 165c0f483eSGreg Clayton# will be a target with all of the files loaded at the locations 175c0f483eSGreg Clayton# described in the crash log. Only the files that have stack frames 185c0f483eSGreg Clayton# in the backtrace will be loaded unless the "--load-all" option 195c0f483eSGreg Clayton# has been specified. This allows users to explore the program in the 205c0f483eSGreg Clayton# state it was in right at crash time. 215c0f483eSGreg Clayton# 22aa9d02b1SGreg Clayton# On MacOSX csh, tcsh: 235c0f483eSGreg Clayton# ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash ) 245c0f483eSGreg Clayton# 25aa9d02b1SGreg Clayton# On MacOSX sh, bash: 265c0f483eSGreg Clayton# PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash 27aa9d02b1SGreg Clayton#---------------------------------------------------------------------- 28aa9d02b1SGreg Clayton 29a8abb695SJonas Devlieghereimport concurrent.futures 308d097a6bSMed Ismail Bennaniimport contextlib 31f47b2c26SGreg Claytonimport datetime 328d097a6bSMed Ismail Bennaniimport json 33aa9d02b1SGreg Claytonimport optparse 34aa9d02b1SGreg Claytonimport os 35f47b2c26SGreg Claytonimport platform 36aa9d02b1SGreg Claytonimport plistlib 37aa9d02b1SGreg Claytonimport re 3853b43b07SGreg Claytonimport shlex 39c8f73d7bSGreg Claytonimport string 40a658ab9fSDavide Italianoimport subprocess 41aa9d02b1SGreg Claytonimport sys 42a8abb695SJonas Devlieghereimport threading 43aa9d02b1SGreg Claytonimport time 443d8d3db3SGreg Claytonimport uuid 45aa4d4531SGreg Clayton 46a8abb695SJonas Devlieghere 47a8abb695SJonas Devlieghereprint_lock = threading.RLock() 48a8abb695SJonas Devlieghere 49fb6d1c02SJonas Devliegheretry: 50fb6d1c02SJonas Devlieghere # First try for LLDB in case PYTHONPATH is already correctly setup. 51fb6d1c02SJonas Devlieghere import lldb 52fb6d1c02SJonas Devlieghereexcept ImportError: 53fb6d1c02SJonas Devlieghere # Ask the command line driver for the path to the lldb module. Copy over 54fb6d1c02SJonas Devlieghere # the environment so that SDKROOT is propagated to xcrun. 55fb6d1c02SJonas Devlieghere command = ['xcrun', 'lldb', '-P'] if platform.system() == 'Darwin' else ['lldb', '-P'] 56fb6d1c02SJonas Devlieghere # Extend the PYTHONPATH if the path exists and isn't already there. 57d52866e1SJonas Devlieghere lldb_python_path = subprocess.check_output(command).decode("utf-8").strip() 58fb6d1c02SJonas Devlieghere if os.path.exists(lldb_python_path) and not sys.path.__contains__(lldb_python_path): 59fb6d1c02SJonas Devlieghere sys.path.append(lldb_python_path) 60fb6d1c02SJonas Devlieghere # Try importing LLDB again. 61fb6d1c02SJonas Devlieghere try: 62fb6d1c02SJonas Devlieghere import lldb 63fb6d1c02SJonas Devlieghere except ImportError: 64fb6d1c02SJonas Devlieghere print("error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly") 65fb6d1c02SJonas Devlieghere sys.exit(1) 66fb6d1c02SJonas Devlieghere 67fb6d1c02SJonas Devliegherefrom lldb.utils import symbolication 68fb6d1c02SJonas Devlieghere 698803124dSDavide Italianodef read_plist(s): 708803124dSDavide Italiano if sys.version_info.major == 3: 718803124dSDavide Italiano return plistlib.loads(s) 728803124dSDavide Italiano else: 738803124dSDavide Italiano return plistlib.readPlistFromString(s) 748803124dSDavide Italiano 75c8f73d7bSGreg Claytonclass CrashLog(symbolication.Symbolicator): 76814ad734SDavide Italiano class Thread: 77aa9d02b1SGreg Clayton """Class that represents a thread in a darwin crash log""" 78b9c1b51eSKate Stone 7948d157ddSGreg Clayton def __init__(self, index, app_specific_backtrace): 80aa9d02b1SGreg Clayton self.index = index 8112301d61SMed Ismail Bennani self.id = index 82aa9d02b1SGreg Clayton self.frames = list() 83563d0393SGreg Clayton self.idents = list() 84aa9d02b1SGreg Clayton self.registers = dict() 85aa9d02b1SGreg Clayton self.reason = None 8612301d61SMed Ismail Bennani self.name = None 87aa9d02b1SGreg Clayton self.queue = None 88b913065bSJonas Devlieghere self.crashed = False 8948d157ddSGreg Clayton self.app_specific_backtrace = app_specific_backtrace 90aa9d02b1SGreg Clayton 91aa9d02b1SGreg Clayton def dump(self, prefix): 9248d157ddSGreg Clayton if self.app_specific_backtrace: 93a658ab9fSDavide Italiano print("%Application Specific Backtrace[%u] %s" % (prefix, self.index, self.reason)) 9448d157ddSGreg Clayton else: 95a658ab9fSDavide Italiano print("%sThread[%u] %s" % (prefix, self.index, self.reason)) 96aa9d02b1SGreg Clayton if self.frames: 97a658ab9fSDavide Italiano print("%s Frames:" % (prefix)) 98aa9d02b1SGreg Clayton for frame in self.frames: 99aa9d02b1SGreg Clayton frame.dump(prefix + ' ') 100aa9d02b1SGreg Clayton if self.registers: 101a658ab9fSDavide Italiano print("%s Registers:" % (prefix)) 102814ad734SDavide Italiano for reg in self.registers.keys(): 10391d3f739SJonas Devlieghere print("%s %-8s = %#16.16x" % (prefix, reg, self.registers[reg])) 104aa9d02b1SGreg Clayton 10548d157ddSGreg Clayton def dump_symbolicated(self, crash_log, options): 10648d157ddSGreg Clayton this_thread_crashed = self.app_specific_backtrace 10748d157ddSGreg Clayton if not this_thread_crashed: 10848d157ddSGreg Clayton this_thread_crashed = self.did_crash() 10948d157ddSGreg Clayton if options.crashed_only and this_thread_crashed == False: 11048d157ddSGreg Clayton return 11148d157ddSGreg Clayton 112a658ab9fSDavide Italiano print("%s" % self) 11348d157ddSGreg Clayton display_frame_idx = -1 11448d157ddSGreg Clayton for frame_idx, frame in enumerate(self.frames): 115b9c1b51eSKate Stone disassemble = ( 116b9c1b51eSKate Stone this_thread_crashed or options.disassemble_all_threads) and frame_idx < options.disassemble_depth 1179defb3b4SJonas Devlieghere 1183e54ea0cSMed Ismail Bennani # Except for the zeroth frame, we should subtract 1 from every 1193e54ea0cSMed Ismail Bennani # frame pc to get the previous line entry. 1209defb3b4SJonas Devlieghere pc = frame.pc & crash_log.addr_mask 1219defb3b4SJonas Devlieghere pc = pc if frame_idx == 0 or pc == 0 else pc - 1 1229defb3b4SJonas Devlieghere symbolicated_frame_addresses = crash_log.symbolicate(pc, options.verbose) 12348d157ddSGreg Clayton 12448d157ddSGreg Clayton if symbolicated_frame_addresses: 12548d157ddSGreg Clayton symbolicated_frame_address_idx = 0 12648d157ddSGreg Clayton for symbolicated_frame_address in symbolicated_frame_addresses: 12748d157ddSGreg Clayton display_frame_idx += 1 128a658ab9fSDavide Italiano print('[%3u] %s' % (frame_idx, symbolicated_frame_address)) 129b9c1b51eSKate Stone if (options.source_all or self.did_crash( 130b9c1b51eSKate Stone )) and display_frame_idx < options.source_frames and options.source_context: 13148d157ddSGreg Clayton source_context = options.source_context 13248d157ddSGreg Clayton line_entry = symbolicated_frame_address.get_symbol_context().line_entry 13348d157ddSGreg Clayton if line_entry.IsValid(): 13448d157ddSGreg Clayton strm = lldb.SBStream() 13548d157ddSGreg Clayton if line_entry: 136c29c24beSJonas Devlieghere crash_log.debugger.GetSourceManager().DisplaySourceLinesWithLineNumbers( 137b9c1b51eSKate Stone line_entry.file, line_entry.line, source_context, source_context, "->", strm) 13848d157ddSGreg Clayton source_text = strm.GetData() 13948d157ddSGreg Clayton if source_text: 14048d157ddSGreg Clayton # Indent the source a bit 14148d157ddSGreg Clayton indent_str = ' ' 14248d157ddSGreg Clayton join_str = '\n' + indent_str 143a658ab9fSDavide Italiano print('%s%s' % (indent_str, join_str.join(source_text.split('\n')))) 14448d157ddSGreg Clayton if symbolicated_frame_address_idx == 0: 14548d157ddSGreg Clayton if disassemble: 14648d157ddSGreg Clayton instructions = symbolicated_frame_address.get_instructions() 14748d157ddSGreg Clayton if instructions: 148a658ab9fSDavide Italiano print() 149b9c1b51eSKate Stone symbolication.disassemble_instructions( 150b9c1b51eSKate Stone crash_log.get_target(), 15148d157ddSGreg Clayton instructions, 15248d157ddSGreg Clayton frame.pc, 15348d157ddSGreg Clayton options.disassemble_before, 154b9c1b51eSKate Stone options.disassemble_after, 155b9c1b51eSKate Stone frame.index > 0) 156a658ab9fSDavide Italiano print() 15748d157ddSGreg Clayton symbolicated_frame_address_idx += 1 15848d157ddSGreg Clayton else: 159a658ab9fSDavide Italiano print(frame) 16091d3f739SJonas Devlieghere if self.registers: 16191d3f739SJonas Devlieghere print() 16291d3f739SJonas Devlieghere for reg in self.registers.keys(): 16391d3f739SJonas Devlieghere print(" %-8s = %#16.16x" % (reg, self.registers[reg])) 164b913065bSJonas Devlieghere elif self.crashed: 165b913065bSJonas Devlieghere print() 166b913065bSJonas Devlieghere print("No thread state (register information) available") 16748d157ddSGreg Clayton 168563d0393SGreg Clayton def add_ident(self, ident): 169b9c1b51eSKate Stone if ident not in self.idents: 170563d0393SGreg Clayton self.idents.append(ident) 171563d0393SGreg Clayton 172aa9d02b1SGreg Clayton def did_crash(self): 173b9c1b51eSKate Stone return self.reason is not None 174aa9d02b1SGreg Clayton 175aa9d02b1SGreg Clayton def __str__(self): 17648d157ddSGreg Clayton if self.app_specific_backtrace: 17748d157ddSGreg Clayton s = "Application Specific Backtrace[%u]" % self.index 17848d157ddSGreg Clayton else: 179aa9d02b1SGreg Clayton s = "Thread[%u]" % self.index 180aa9d02b1SGreg Clayton if self.reason: 181aa9d02b1SGreg Clayton s += ' %s' % self.reason 182aa9d02b1SGreg Clayton return s 183aa9d02b1SGreg Clayton 184814ad734SDavide Italiano class Frame: 185aa9d02b1SGreg Clayton """Class that represents a stack frame in a thread in a darwin crash log""" 186b9c1b51eSKate Stone 1873c2c4bb2SGreg Clayton def __init__(self, index, pc, description): 188aa9d02b1SGreg Clayton self.pc = pc 1893c2c4bb2SGreg Clayton self.description = description 1903c2c4bb2SGreg Clayton self.index = index 191aa9d02b1SGreg Clayton 192aa9d02b1SGreg Clayton def __str__(self): 1933c2c4bb2SGreg Clayton if self.description: 194b9c1b51eSKate Stone return "[%3u] 0x%16.16x %s" % ( 195b9c1b51eSKate Stone self.index, self.pc, self.description) 1963c2c4bb2SGreg Clayton else: 1973c2c4bb2SGreg Clayton return "[%3u] 0x%16.16x" % (self.index, self.pc) 1988e1fd43aSJohnny Chen 1998e1fd43aSJohnny Chen def dump(self, prefix): 200a658ab9fSDavide Italiano print("%s%s" % (prefix, str(self))) 201aa9d02b1SGreg Clayton 202c8f73d7bSGreg Clayton class DarwinImage(symbolication.Image): 203aa9d02b1SGreg Clayton """Class that represents a binary images in a darwin crash log""" 204e3b5eba1SDavide Italiano dsymForUUIDBinary = '/usr/local/bin/dsymForUUID' 2053d8d3db3SGreg Clayton if not os.path.exists(dsymForUUIDBinary): 206f4d2fa3fSAdrian Prantl try: 207f4d2fa3fSAdrian Prantl dsymForUUIDBinary = subprocess.check_output('which dsymForUUID', 208573ffd88SAdrian Prantl shell=True).decode("utf-8").rstrip('\n') 209f4d2fa3fSAdrian Prantl except: 210f4d2fa3fSAdrian Prantl dsymForUUIDBinary = "" 2113d8d3db3SGreg Clayton 212b9c1b51eSKate Stone dwarfdump_uuid_regex = re.compile( 213b9c1b51eSKate Stone 'UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*') 214a32bfbeeSGreg Clayton 215b9c1b51eSKate Stone def __init__( 216b9c1b51eSKate Stone self, 217b9c1b51eSKate Stone text_addr_lo, 218b9c1b51eSKate Stone text_addr_hi, 219b9c1b51eSKate Stone identifier, 220b9c1b51eSKate Stone version, 221b9c1b51eSKate Stone uuid, 22238be2c65SAdrian Prantl path, 22338be2c65SAdrian Prantl verbose): 224b9c1b51eSKate Stone symbolication.Image.__init__(self, path, uuid) 225b9c1b51eSKate Stone self.add_section( 226b9c1b51eSKate Stone symbolication.Section( 227b9c1b51eSKate Stone text_addr_lo, 228b9c1b51eSKate Stone text_addr_hi, 229b9c1b51eSKate Stone "__TEXT")) 2303c2c4bb2SGreg Clayton self.identifier = identifier 231aa9d02b1SGreg Clayton self.version = version 23238be2c65SAdrian Prantl self.verbose = verbose 23338be2c65SAdrian Prantl 23438be2c65SAdrian Prantl def show_symbol_progress(self): 23538be2c65SAdrian Prantl """ 23638be2c65SAdrian Prantl Hide progress output and errors from system frameworks as they are plentiful. 23738be2c65SAdrian Prantl """ 23838be2c65SAdrian Prantl if self.verbose: 23938be2c65SAdrian Prantl return True 24038be2c65SAdrian Prantl return not (self.path.startswith("/System/Library/") or 24138be2c65SAdrian Prantl self.path.startswith("/usr/lib/")) 24238be2c65SAdrian Prantl 243aa9d02b1SGreg Clayton 244fb5aa932SAdrian Prantl def find_matching_slice(self): 245a658ab9fSDavide Italiano dwarfdump_cmd_output = subprocess.check_output( 246192dd7dfSDavide Italiano 'dwarfdump --uuid "%s"' % self.path, shell=True).decode("utf-8") 247fb5aa932SAdrian Prantl self_uuid = self.get_uuid() 248fb5aa932SAdrian Prantl for line in dwarfdump_cmd_output.splitlines(): 249fb5aa932SAdrian Prantl match = self.dwarfdump_uuid_regex.search(line) 250fb5aa932SAdrian Prantl if match: 251fb5aa932SAdrian Prantl dwarf_uuid_str = match.group(1) 252fb5aa932SAdrian Prantl dwarf_uuid = uuid.UUID(dwarf_uuid_str) 253fb5aa932SAdrian Prantl if self_uuid == dwarf_uuid: 254fb5aa932SAdrian Prantl self.resolved_path = self.path 255fb5aa932SAdrian Prantl self.arch = match.group(2) 256fb5aa932SAdrian Prantl return True 257fb5aa932SAdrian Prantl if not self.resolved_path: 258fb5aa932SAdrian Prantl self.unavailable = True 25938be2c65SAdrian Prantl if self.show_symbol_progress(): 260a658ab9fSDavide Italiano print(("error\n error: unable to locate '%s' with UUID %s" 261a658ab9fSDavide Italiano % (self.path, self.get_normalized_uuid_string()))) 262fb5aa932SAdrian Prantl return False 263fb5aa932SAdrian Prantl 2643c2c4bb2SGreg Clayton def locate_module_and_debug_symbols(self): 2653d8d3db3SGreg Clayton # Don't load a module twice... 266f51a23fbSGreg Clayton if self.resolved: 267f99295c3SGreg Clayton return True 268f51a23fbSGreg Clayton # Mark this as resolved so we don't keep trying 269f51a23fbSGreg Clayton self.resolved = True 27060bb58f6SGreg Clayton uuid_str = self.get_normalized_uuid_string() 27138be2c65SAdrian Prantl if self.show_symbol_progress(): 272a8abb695SJonas Devlieghere with print_lock: 273a8abb695SJonas Devlieghere print('Getting symbols for %s %s...' % (uuid_str, self.path)) 274a32bfbeeSGreg Clayton if os.path.exists(self.dsymForUUIDBinary): 275b9c1b51eSKate Stone dsym_for_uuid_command = '%s %s' % ( 276b9c1b51eSKate Stone self.dsymForUUIDBinary, uuid_str) 27768946d10SDavide Italiano s = subprocess.check_output(dsym_for_uuid_command, shell=True) 278aa9d02b1SGreg Clayton if s: 2799f44d460SJim Ingham try: 2808803124dSDavide Italiano plist_root = read_plist(s) 2819f44d460SJim Ingham except: 282a8abb695SJonas Devlieghere with print_lock: 2833577da76SSerge Guelton print(("Got exception: ", sys.exc_info()[1], " handling dsymForUUID output: \n", s)) 2849f44d460SJim Ingham raise 285aa9d02b1SGreg Clayton if plist_root: 28660bb58f6SGreg Clayton plist = plist_root[uuid_str] 2873d8d3db3SGreg Clayton if plist: 2883d8d3db3SGreg Clayton if 'DBGArchitecture' in plist: 2893d8d3db3SGreg Clayton self.arch = plist['DBGArchitecture'] 2903d8d3db3SGreg Clayton if 'DBGDSYMPath' in plist: 291b9c1b51eSKate Stone self.symfile = os.path.realpath( 292b9c1b51eSKate Stone plist['DBGDSYMPath']) 2933d8d3db3SGreg Clayton if 'DBGSymbolRichExecutable' in plist: 294b9c1b51eSKate Stone self.path = os.path.expanduser( 295b9c1b51eSKate Stone plist['DBGSymbolRichExecutable']) 29686e70cb3SGreg Clayton self.resolved_path = self.path 2973d8d3db3SGreg Clayton if not self.resolved_path and os.path.exists(self.path): 298fb5aa932SAdrian Prantl if not self.find_matching_slice(): 299f99295c3SGreg Clayton return False 300fb5aa932SAdrian Prantl if not self.resolved_path and not os.path.exists(self.path): 301fb5aa932SAdrian Prantl try: 3024da5a446SJonas Devlieghere mdfind_results = subprocess.check_output( 303fb5aa932SAdrian Prantl ["/usr/bin/mdfind", 3044da5a446SJonas Devlieghere "com_apple_xcode_dsym_uuids == %s" % uuid_str]).decode("utf-8").splitlines() 3054da5a446SJonas Devlieghere found_matching_slice = False 3064da5a446SJonas Devlieghere for dsym in mdfind_results: 307fb5aa932SAdrian Prantl dwarf_dir = os.path.join(dsym, 'Contents/Resources/DWARF') 3084da5a446SJonas Devlieghere if not os.path.exists(dwarf_dir): 3094da5a446SJonas Devlieghere # Not a dSYM bundle, probably an Xcode archive. 3104da5a446SJonas Devlieghere continue 311a8abb695SJonas Devlieghere with print_lock: 3124da5a446SJonas Devlieghere print('falling back to binary inside "%s"' % dsym) 3134da5a446SJonas Devlieghere self.symfile = dsym 314fb5aa932SAdrian Prantl for filename in os.listdir(dwarf_dir): 315fb5aa932SAdrian Prantl self.path = os.path.join(dwarf_dir, filename) 3164da5a446SJonas Devlieghere if self.find_matching_slice(): 3174da5a446SJonas Devlieghere found_matching_slice = True 3184da5a446SJonas Devlieghere break 3194da5a446SJonas Devlieghere if found_matching_slice: 320fb5aa932SAdrian Prantl break 321fb5aa932SAdrian Prantl except: 322fb5aa932SAdrian Prantl pass 323b9c1b51eSKate Stone if (self.resolved_path and os.path.exists(self.resolved_path)) or ( 324b9c1b51eSKate Stone self.path and os.path.exists(self.path)): 325a8abb695SJonas Devlieghere with print_lock: 326a8abb695SJonas Devlieghere print('Resolved symbols for %s %s...' % (uuid_str, self.path)) 327f99295c3SGreg Clayton return True 328f51a23fbSGreg Clayton else: 329f51a23fbSGreg Clayton self.unavailable = True 330f99295c3SGreg Clayton return False 331aa9d02b1SGreg Clayton 332c29c24beSJonas Devlieghere def __init__(self, debugger, path, verbose): 333aa9d02b1SGreg Clayton """CrashLog constructor that take a path to a darwin crash log file""" 334c29c24beSJonas Devlieghere symbolication.Symbolicator.__init__(self, debugger) 335b9c1b51eSKate Stone self.path = os.path.expanduser(path) 336aa9d02b1SGreg Clayton self.info_lines = list() 337aa9d02b1SGreg Clayton self.system_profile = list() 338aa9d02b1SGreg Clayton self.threads = list() 33948d157ddSGreg Clayton self.backtraces = list() # For application specific backtraces 340aa9d02b1SGreg Clayton self.idents = list() # A list of the required identifiers for doing all stack backtraces 341b225c5f7SJonas Devlieghere self.errors = list() 342aa9d02b1SGreg Clayton self.crashed_thread_idx = -1 343aa9d02b1SGreg Clayton self.version = -1 34448d157ddSGreg Clayton self.target = None 34538be2c65SAdrian Prantl self.verbose = verbose 346aa9d02b1SGreg Clayton 347aa9d02b1SGreg Clayton def dump(self): 348a658ab9fSDavide Italiano print("Crash Log File: %s" % (self.path)) 34948d157ddSGreg Clayton if self.backtraces: 350a658ab9fSDavide Italiano print("\nApplication Specific Backtraces:") 35148d157ddSGreg Clayton for thread in self.backtraces: 35248d157ddSGreg Clayton thread.dump(' ') 353a658ab9fSDavide Italiano print("\nThreads:") 354aa9d02b1SGreg Clayton for thread in self.threads: 355aa9d02b1SGreg Clayton thread.dump(' ') 356a658ab9fSDavide Italiano print("\nImages:") 357aa9d02b1SGreg Clayton for image in self.images: 358aa9d02b1SGreg Clayton image.dump(' ') 359aa9d02b1SGreg Clayton 3600f29319eSJonas Devlieghere def set_main_image(self, identifier): 3610f29319eSJonas Devlieghere for i, image in enumerate(self.images): 3620f29319eSJonas Devlieghere if image.identifier == identifier: 3630f29319eSJonas Devlieghere self.images.insert(0, self.images.pop(i)) 3640f29319eSJonas Devlieghere break 3650f29319eSJonas Devlieghere 3663c2c4bb2SGreg Clayton def find_image_with_identifier(self, identifier): 367aa9d02b1SGreg Clayton for image in self.images: 3683c2c4bb2SGreg Clayton if image.identifier == identifier: 369aa9d02b1SGreg Clayton return image 3706c42aa77SGreg Clayton regex_text = '^.*\.%s$' % (re.escape(identifier)) 37148d157ddSGreg Clayton regex = re.compile(regex_text) 37248d157ddSGreg Clayton for image in self.images: 37348d157ddSGreg Clayton if regex.match(image.identifier): 37448d157ddSGreg Clayton return image 375aa9d02b1SGreg Clayton return None 376aa9d02b1SGreg Clayton 37742a6eb71SGreg Clayton def create_target(self): 37848d157ddSGreg Clayton if self.target is None: 37948d157ddSGreg Clayton self.target = symbolication.Symbolicator.create_target(self) 38048d157ddSGreg Clayton if self.target: 38148d157ddSGreg Clayton return self.target 382b9c1b51eSKate Stone # We weren't able to open the main executable as, but we can still 383b9c1b51eSKate Stone # symbolicate 384a658ab9fSDavide Italiano print('crashlog.create_target()...2') 38542a6eb71SGreg Clayton if self.idents: 38688685f28SSean Callanan for ident in self.idents: 38742a6eb71SGreg Clayton image = self.find_image_with_identifier(ident) 38842a6eb71SGreg Clayton if image: 389c29c24beSJonas Devlieghere self.target = image.create_target(self.debugger) 39048d157ddSGreg Clayton if self.target: 39148d157ddSGreg Clayton return self.target # success 392a658ab9fSDavide Italiano print('crashlog.create_target()...3') 39342a6eb71SGreg Clayton for image in self.images: 394c29c24beSJonas Devlieghere self.target = image.create_target(self.debugger) 39548d157ddSGreg Clayton if self.target: 39648d157ddSGreg Clayton return self.target # success 397a658ab9fSDavide Italiano print('crashlog.create_target()...4') 39838be2c65SAdrian Prantl print('error: Unable to locate any executables from the crash log.') 39938be2c65SAdrian Prantl print(' Try loading the executable into lldb before running crashlog') 40038be2c65SAdrian Prantl print(' and/or make sure the .dSYM bundles can be found by Spotlight.') 40148d157ddSGreg Clayton return self.target 40242a6eb71SGreg Clayton 40348d157ddSGreg Clayton def get_target(self): 40448d157ddSGreg Clayton return self.target 405aa9d02b1SGreg Clayton 406b9c1b51eSKate Stone 407c7cbf32fSJonas Devlieghereclass CrashLogFormatException(Exception): 408c7cbf32fSJonas Devlieghere pass 409c7cbf32fSJonas Devlieghere 410c7cbf32fSJonas Devlieghere 4118639e2aaSJonas Devlieghereclass CrashLogParseException(Exception): 4128639e2aaSJonas Devlieghere pass 4138639e2aaSJonas Devlieghere 4148639e2aaSJonas Devlieghere 415c7cbf32fSJonas Devlieghereclass CrashLogParser: 416c7cbf32fSJonas Devlieghere def parse(self, debugger, path, verbose): 417c7cbf32fSJonas Devlieghere try: 418c7cbf32fSJonas Devlieghere return JSONCrashLogParser(debugger, path, verbose).parse() 419c7cbf32fSJonas Devlieghere except CrashLogFormatException: 420c7cbf32fSJonas Devlieghere return TextCrashLogParser(debugger, path, verbose).parse() 421c7cbf32fSJonas Devlieghere 422c7cbf32fSJonas Devlieghere 423c7cbf32fSJonas Devlieghereclass JSONCrashLogParser: 424c7cbf32fSJonas Devlieghere def __init__(self, debugger, path, verbose): 425c7cbf32fSJonas Devlieghere self.path = os.path.expanduser(path) 426c7cbf32fSJonas Devlieghere self.verbose = verbose 427c7cbf32fSJonas Devlieghere self.crashlog = CrashLog(debugger, self.path, self.verbose) 428c7cbf32fSJonas Devlieghere 429343662a0SJonas Devlieghere def parse_json(self, buffer): 430343662a0SJonas Devlieghere try: 431343662a0SJonas Devlieghere return json.loads(buffer) 432343662a0SJonas Devlieghere except: 433343662a0SJonas Devlieghere # The first line can contain meta data. Try stripping it and try 434343662a0SJonas Devlieghere # again. 435343662a0SJonas Devlieghere head, _, tail = buffer.partition('\n') 436343662a0SJonas Devlieghere return json.loads(tail) 437343662a0SJonas Devlieghere 438c7cbf32fSJonas Devlieghere def parse(self): 439c7cbf32fSJonas Devlieghere with open(self.path, 'r') as f: 440c7cbf32fSJonas Devlieghere buffer = f.read() 441c7cbf32fSJonas Devlieghere 442730fca46SJonas Devlieghere try: 443343662a0SJonas Devlieghere self.data = self.parse_json(buffer) 444343662a0SJonas Devlieghere except: 445c7cbf32fSJonas Devlieghere raise CrashLogFormatException() 446c7cbf32fSJonas Devlieghere 4478639e2aaSJonas Devlieghere try: 448c7cbf32fSJonas Devlieghere self.parse_process_info(self.data) 449c7cbf32fSJonas Devlieghere self.parse_images(self.data['usedImages']) 4500f29319eSJonas Devlieghere self.parse_main_image(self.data) 451c7cbf32fSJonas Devlieghere self.parse_threads(self.data['threads']) 452b225c5f7SJonas Devlieghere self.parse_errors(self.data) 453c7cbf32fSJonas Devlieghere thread = self.crashlog.threads[self.crashlog.crashed_thread_idx] 454a62cbd9aSJonas Devlieghere reason = self.parse_crash_reason(self.data['exception']) 455a62cbd9aSJonas Devlieghere if thread.reason: 456a62cbd9aSJonas Devlieghere thread.reason = '{} {}'.format(thread.reason, reason) 457a62cbd9aSJonas Devlieghere else: 458a62cbd9aSJonas Devlieghere thread.reason = reason 4598639e2aaSJonas Devlieghere except (KeyError, ValueError, TypeError) as e: 4608639e2aaSJonas Devlieghere raise CrashLogParseException( 4618639e2aaSJonas Devlieghere 'Failed to parse JSON crashlog: {}: {}'.format( 4628639e2aaSJonas Devlieghere type(e).__name__, e)) 463c7cbf32fSJonas Devlieghere 464c7cbf32fSJonas Devlieghere return self.crashlog 465c7cbf32fSJonas Devlieghere 466c7cbf32fSJonas Devlieghere def get_used_image(self, idx): 467c7cbf32fSJonas Devlieghere return self.data['usedImages'][idx] 468c7cbf32fSJonas Devlieghere 469c7cbf32fSJonas Devlieghere def parse_process_info(self, json_data): 470c7cbf32fSJonas Devlieghere self.crashlog.process_id = json_data['pid'] 471c7cbf32fSJonas Devlieghere self.crashlog.process_identifier = json_data['procName'] 472c7cbf32fSJonas Devlieghere self.crashlog.process_path = json_data['procPath'] 473c7cbf32fSJonas Devlieghere 474c7cbf32fSJonas Devlieghere def parse_crash_reason(self, json_exception): 475c7cbf32fSJonas Devlieghere exception_type = json_exception['type'] 47621658b77SMed Ismail Bennani exception_signal = " " 47721658b77SMed Ismail Bennani if 'signal' in json_exception: 47821658b77SMed Ismail Bennani exception_signal += "({})".format(json_exception['signal']) 47921658b77SMed Ismail Bennani 480c7cbf32fSJonas Devlieghere if 'codes' in json_exception: 481c7cbf32fSJonas Devlieghere exception_extra = " ({})".format(json_exception['codes']) 482c7cbf32fSJonas Devlieghere elif 'subtype' in json_exception: 483c7cbf32fSJonas Devlieghere exception_extra = " ({})".format(json_exception['subtype']) 484c7cbf32fSJonas Devlieghere else: 485c7cbf32fSJonas Devlieghere exception_extra = "" 48621658b77SMed Ismail Bennani return "{}{}{}".format(exception_type, exception_signal, 487c7cbf32fSJonas Devlieghere exception_extra) 488c7cbf32fSJonas Devlieghere 489c7cbf32fSJonas Devlieghere def parse_images(self, json_images): 490c7cbf32fSJonas Devlieghere idx = 0 491cc52ea30SJonas Devlieghere for json_image in json_images: 492cc52ea30SJonas Devlieghere img_uuid = uuid.UUID(json_image['uuid']) 493cc52ea30SJonas Devlieghere low = int(json_image['base']) 494cc52ea30SJonas Devlieghere high = int(0) 4952cbd3b04SJonas Devlieghere name = json_image['name'] if 'name' in json_image else '' 4962cbd3b04SJonas Devlieghere path = json_image['path'] if 'path' in json_image else '' 4972cbd3b04SJonas Devlieghere version = '' 498c7cbf32fSJonas Devlieghere darwin_image = self.crashlog.DarwinImage(low, high, name, version, 499c7cbf32fSJonas Devlieghere img_uuid, path, 500c7cbf32fSJonas Devlieghere self.verbose) 501c7cbf32fSJonas Devlieghere self.crashlog.images.append(darwin_image) 502c7cbf32fSJonas Devlieghere idx += 1 503c7cbf32fSJonas Devlieghere 5040f29319eSJonas Devlieghere def parse_main_image(self, json_data): 5050f29319eSJonas Devlieghere if 'procName' in json_data: 5060f29319eSJonas Devlieghere proc_name = json_data['procName'] 5070f29319eSJonas Devlieghere self.crashlog.set_main_image(proc_name) 5080f29319eSJonas Devlieghere 509c7cbf32fSJonas Devlieghere def parse_frames(self, thread, json_frames): 510c7cbf32fSJonas Devlieghere idx = 0 511c7cbf32fSJonas Devlieghere for json_frame in json_frames: 512cc52ea30SJonas Devlieghere image_id = int(json_frame['imageIndex']) 51305cdd294SJonas Devlieghere json_image = self.get_used_image(image_id) 51405cdd294SJonas Devlieghere ident = json_image['name'] if 'name' in json_image else '' 515c7cbf32fSJonas Devlieghere thread.add_ident(ident) 516c7cbf32fSJonas Devlieghere if ident not in self.crashlog.idents: 517c7cbf32fSJonas Devlieghere self.crashlog.idents.append(ident) 518c7cbf32fSJonas Devlieghere 519cc52ea30SJonas Devlieghere frame_offset = int(json_frame['imageOffset']) 520cc52ea30SJonas Devlieghere image_addr = self.get_used_image(image_id)['base'] 521c7cbf32fSJonas Devlieghere pc = image_addr + frame_offset 522c7cbf32fSJonas Devlieghere thread.frames.append(self.crashlog.Frame(idx, pc, frame_offset)) 5232be791e3SJason Molenda 5242be791e3SJason Molenda # on arm64 systems, if it jump through a null function pointer, 5252be791e3SJason Molenda # we end up at address 0 and the crash reporter unwinder 5262be791e3SJason Molenda # misses the frame that actually faulted. 5272be791e3SJason Molenda # But $lr can tell us where the last BL/BLR instruction used 5282be791e3SJason Molenda # was at, so insert that address as the caller stack frame. 5292be791e3SJason Molenda if idx == 0 and pc == 0 and "lr" in thread.registers: 5302be791e3SJason Molenda pc = thread.registers["lr"] 5312be791e3SJason Molenda for image in self.data['usedImages']: 5322be791e3SJason Molenda text_lo = image['base'] 5332be791e3SJason Molenda text_hi = text_lo + image['size'] 5342be791e3SJason Molenda if text_lo <= pc < text_hi: 5352be791e3SJason Molenda idx += 1 5362be791e3SJason Molenda frame_offset = pc - text_lo 5372be791e3SJason Molenda thread.frames.append(self.crashlog.Frame(idx, pc, frame_offset)) 5382be791e3SJason Molenda break 5392be791e3SJason Molenda 540c7cbf32fSJonas Devlieghere idx += 1 541c7cbf32fSJonas Devlieghere 542c7cbf32fSJonas Devlieghere def parse_threads(self, json_threads): 543c7cbf32fSJonas Devlieghere idx = 0 544c7cbf32fSJonas Devlieghere for json_thread in json_threads: 545c7cbf32fSJonas Devlieghere thread = self.crashlog.Thread(idx, False) 546a62cbd9aSJonas Devlieghere if 'name' in json_thread: 54712301d61SMed Ismail Bennani thread.name = json_thread['name'] 548a62cbd9aSJonas Devlieghere thread.reason = json_thread['name'] 54912301d61SMed Ismail Bennani if 'id' in json_thread: 55012301d61SMed Ismail Bennani thread.id = int(json_thread['id']) 551c7cbf32fSJonas Devlieghere if json_thread.get('triggered', False): 552c7cbf32fSJonas Devlieghere self.crashlog.crashed_thread_idx = idx 553b913065bSJonas Devlieghere thread.crashed = True 554b913065bSJonas Devlieghere if 'threadState' in json_thread: 55591d3f739SJonas Devlieghere thread.registers = self.parse_thread_registers( 556cc52ea30SJonas Devlieghere json_thread['threadState']) 55712301d61SMed Ismail Bennani if 'queue' in json_thread: 558c7cbf32fSJonas Devlieghere thread.queue = json_thread.get('queue') 559c7cbf32fSJonas Devlieghere self.parse_frames(thread, json_thread.get('frames', [])) 560c7cbf32fSJonas Devlieghere self.crashlog.threads.append(thread) 561c7cbf32fSJonas Devlieghere idx += 1 562c7cbf32fSJonas Devlieghere 5639a9bf12cSMed Ismail Bennani def parse_thread_registers(self, json_thread_state, prefix=None): 564c7cbf32fSJonas Devlieghere registers = dict() 56591d3f739SJonas Devlieghere for key, state in json_thread_state.items(): 566e1cad130SJonas Devlieghere if key == "rosetta": 567e1cad130SJonas Devlieghere registers.update(self.parse_thread_registers(state)) 568e1cad130SJonas Devlieghere continue 5699a9bf12cSMed Ismail Bennani if key == "x": 5709a9bf12cSMed Ismail Bennani gpr_dict = { str(idx) : reg for idx,reg in enumerate(state) } 5719a9bf12cSMed Ismail Bennani registers.update(self.parse_thread_registers(gpr_dict, key)) 5729a9bf12cSMed Ismail Bennani continue 57391d3f739SJonas Devlieghere try: 57491d3f739SJonas Devlieghere value = int(state['value']) 5752be791e3SJason Molenda registers["{}{}".format(prefix or '',key)] = value 576e1cad130SJonas Devlieghere except (KeyError, ValueError, TypeError): 57791d3f739SJonas Devlieghere pass 578c7cbf32fSJonas Devlieghere return registers 579c7cbf32fSJonas Devlieghere 580b225c5f7SJonas Devlieghere def parse_errors(self, json_data): 581b225c5f7SJonas Devlieghere if 'reportNotes' in json_data: 582b225c5f7SJonas Devlieghere self.crashlog.errors = json_data['reportNotes'] 583b225c5f7SJonas Devlieghere 584c7cbf32fSJonas Devlieghere 5854b846820SJonas Devlieghereclass CrashLogParseMode: 5864b846820SJonas Devlieghere NORMAL = 0 5874b846820SJonas Devlieghere THREAD = 1 5884b846820SJonas Devlieghere IMAGES = 2 5894b846820SJonas Devlieghere THREGS = 3 5904b846820SJonas Devlieghere SYSTEM = 4 5914b846820SJonas Devlieghere INSTRS = 5 5924b846820SJonas Devlieghere 5934b846820SJonas Devlieghere 594c7cbf32fSJonas Devlieghereclass TextCrashLogParser: 5954b846820SJonas Devlieghere parent_process_regex = re.compile('^Parent Process:\s*(.*)\[(\d+)\]') 5964b846820SJonas Devlieghere thread_state_regex = re.compile('^Thread ([0-9]+) crashed with') 5974b846820SJonas Devlieghere thread_instrs_regex = re.compile('^Thread ([0-9]+) instruction stream') 5984b846820SJonas Devlieghere thread_regex = re.compile('^Thread ([0-9]+)([^:]*):(.*)') 5994b846820SJonas Devlieghere app_backtrace_regex = re.compile('^Application Specific Backtrace ([0-9]+)([^:]*):(.*)') 6004b846820SJonas Devlieghere version = r'(\(.+\)|(arm|x86_)[0-9a-z]+)\s+' 6014b846820SJonas Devlieghere frame_regex = re.compile(r'^([0-9]+)' r'\s' # id 6024b846820SJonas Devlieghere r'+(.+?)' r'\s+' # img_name 6034b846820SJonas Devlieghere r'(' +version+ r')?' # img_version 6044b846820SJonas Devlieghere r'(0x[0-9a-fA-F]{7}[0-9a-fA-F]+)' # addr 6054b846820SJonas Devlieghere r' +(.*)' # offs 6064b846820SJonas Devlieghere ) 6074b846820SJonas Devlieghere null_frame_regex = re.compile(r'^([0-9]+)\s+\?\?\?\s+(0{7}0+) +(.*)') 6084b846820SJonas Devlieghere image_regex_uuid = re.compile(r'(0x[0-9a-fA-F]+)' # img_lo 6094b846820SJonas Devlieghere r'\s+' '-' r'\s+' # - 6104b846820SJonas Devlieghere r'(0x[0-9a-fA-F]+)' r'\s+' # img_hi 6114b846820SJonas Devlieghere r'[+]?(.+?)' r'\s+' # img_name 6124b846820SJonas Devlieghere r'(' +version+ ')?' # img_version 6134b846820SJonas Devlieghere r'(<([-0-9a-fA-F]+)>\s+)?' # img_uuid 6144b846820SJonas Devlieghere r'(/.*)' # img_path 6154b846820SJonas Devlieghere ) 6164b846820SJonas Devlieghere 6174b846820SJonas Devlieghere 618c29c24beSJonas Devlieghere def __init__(self, debugger, path, verbose): 6194b846820SJonas Devlieghere self.path = os.path.expanduser(path) 6204b846820SJonas Devlieghere self.verbose = verbose 6214b846820SJonas Devlieghere self.thread = None 6224b846820SJonas Devlieghere self.app_specific_backtrace = False 623c29c24beSJonas Devlieghere self.crashlog = CrashLog(debugger, self.path, self.verbose) 62416dd6934SJonas Devlieghere self.parse_mode = CrashLogParseMode.NORMAL 62516dd6934SJonas Devlieghere self.parsers = { 62616dd6934SJonas Devlieghere CrashLogParseMode.NORMAL : self.parse_normal, 62716dd6934SJonas Devlieghere CrashLogParseMode.THREAD : self.parse_thread, 62816dd6934SJonas Devlieghere CrashLogParseMode.IMAGES : self.parse_images, 62916dd6934SJonas Devlieghere CrashLogParseMode.THREGS : self.parse_thread_registers, 63016dd6934SJonas Devlieghere CrashLogParseMode.SYSTEM : self.parse_system, 63116dd6934SJonas Devlieghere CrashLogParseMode.INSTRS : self.parse_instructions, 63216dd6934SJonas Devlieghere } 6334b846820SJonas Devlieghere 6344b846820SJonas Devlieghere def parse(self): 6354b846820SJonas Devlieghere with open(self.path,'r') as f: 6364b846820SJonas Devlieghere lines = f.read().splitlines() 6374b846820SJonas Devlieghere 6384b846820SJonas Devlieghere for line in lines: 6394b846820SJonas Devlieghere line_len = len(line) 6404b846820SJonas Devlieghere if line_len == 0: 6414b846820SJonas Devlieghere if self.thread: 6424b846820SJonas Devlieghere if self.parse_mode == CrashLogParseMode.THREAD: 6434b846820SJonas Devlieghere if self.thread.index == self.crashlog.crashed_thread_idx: 6444b846820SJonas Devlieghere self.thread.reason = '' 6454b846820SJonas Devlieghere if self.crashlog.thread_exception: 6464b846820SJonas Devlieghere self.thread.reason += self.crashlog.thread_exception 6474b846820SJonas Devlieghere if self.crashlog.thread_exception_data: 6484b846820SJonas Devlieghere self.thread.reason += " (%s)" % self.crashlog.thread_exception_data 6494b846820SJonas Devlieghere if self.app_specific_backtrace: 6504b846820SJonas Devlieghere self.crashlog.backtraces.append(self.thread) 6514b846820SJonas Devlieghere else: 6524b846820SJonas Devlieghere self.crashlog.threads.append(self.thread) 6534b846820SJonas Devlieghere self.thread = None 6544b846820SJonas Devlieghere else: 6554b846820SJonas Devlieghere # only append an extra empty line if the previous line 6564b846820SJonas Devlieghere # in the info_lines wasn't empty 6574b846820SJonas Devlieghere if len(self.crashlog.info_lines) > 0 and len(self.crashlog.info_lines[-1]): 6584b846820SJonas Devlieghere self.crashlog.info_lines.append(line) 6594b846820SJonas Devlieghere self.parse_mode = CrashLogParseMode.NORMAL 66016dd6934SJonas Devlieghere else: 66116dd6934SJonas Devlieghere self.parsers[self.parse_mode](line) 66216dd6934SJonas Devlieghere 66316dd6934SJonas Devlieghere return self.crashlog 66416dd6934SJonas Devlieghere 66516dd6934SJonas Devlieghere 66616dd6934SJonas Devlieghere def parse_normal(self, line): 6674b846820SJonas Devlieghere if line.startswith('Process:'): 6684b846820SJonas Devlieghere (self.crashlog.process_name, pid_with_brackets) = line[ 6694b846820SJonas Devlieghere 8:].strip().split(' [') 6704b846820SJonas Devlieghere self.crashlog.process_id = pid_with_brackets.strip('[]') 6714b846820SJonas Devlieghere elif line.startswith('Path:'): 6724b846820SJonas Devlieghere self.crashlog.process_path = line[5:].strip() 6734b846820SJonas Devlieghere elif line.startswith('Identifier:'): 6744b846820SJonas Devlieghere self.crashlog.process_identifier = line[11:].strip() 6754b846820SJonas Devlieghere elif line.startswith('Version:'): 6764b846820SJonas Devlieghere version_string = line[8:].strip() 6774b846820SJonas Devlieghere matched_pair = re.search("(.+)\((.+)\)", version_string) 6784b846820SJonas Devlieghere if matched_pair: 6794b846820SJonas Devlieghere self.crashlog.process_version = matched_pair.group(1) 6804b846820SJonas Devlieghere self.crashlog.process_compatability_version = matched_pair.group( 6814b846820SJonas Devlieghere 2) 6824b846820SJonas Devlieghere else: 6834b846820SJonas Devlieghere self.crashlog.process = version_string 6844b846820SJonas Devlieghere self.crashlog.process_compatability_version = version_string 6854b846820SJonas Devlieghere elif self.parent_process_regex.search(line): 6864b846820SJonas Devlieghere parent_process_match = self.parent_process_regex.search( 6874b846820SJonas Devlieghere line) 6884b846820SJonas Devlieghere self.crashlog.parent_process_name = parent_process_match.group(1) 6894b846820SJonas Devlieghere self.crashlog.parent_process_id = parent_process_match.group(2) 6904b846820SJonas Devlieghere elif line.startswith('Exception Type:'): 6914b846820SJonas Devlieghere self.crashlog.thread_exception = line[15:].strip() 69216dd6934SJonas Devlieghere return 6934b846820SJonas Devlieghere elif line.startswith('Exception Codes:'): 6944b846820SJonas Devlieghere self.crashlog.thread_exception_data = line[16:].strip() 69516dd6934SJonas Devlieghere return 6964b846820SJonas Devlieghere elif line.startswith('Exception Subtype:'): # iOS 6974b846820SJonas Devlieghere self.crashlog.thread_exception_data = line[18:].strip() 69816dd6934SJonas Devlieghere return 6994b846820SJonas Devlieghere elif line.startswith('Crashed Thread:'): 7004b846820SJonas Devlieghere self.crashlog.crashed_thread_idx = int(line[15:].strip().split()[0]) 70116dd6934SJonas Devlieghere return 7024b846820SJonas Devlieghere elif line.startswith('Triggered by Thread:'): # iOS 7034b846820SJonas Devlieghere self.crashlog.crashed_thread_idx = int(line[20:].strip().split()[0]) 70416dd6934SJonas Devlieghere return 7054b846820SJonas Devlieghere elif line.startswith('Report Version:'): 7064b846820SJonas Devlieghere self.crashlog.version = int(line[15:].strip()) 70716dd6934SJonas Devlieghere return 7084b846820SJonas Devlieghere elif line.startswith('System Profile:'): 7094b846820SJonas Devlieghere self.parse_mode = CrashLogParseMode.SYSTEM 71016dd6934SJonas Devlieghere return 7114b846820SJonas Devlieghere elif (line.startswith('Interval Since Last Report:') or 7124b846820SJonas Devlieghere line.startswith('Crashes Since Last Report:') or 7134b846820SJonas Devlieghere line.startswith('Per-App Interval Since Last Report:') or 7144b846820SJonas Devlieghere line.startswith('Per-App Crashes Since Last Report:') or 7154b846820SJonas Devlieghere line.startswith('Sleep/Wake UUID:') or 7164b846820SJonas Devlieghere line.startswith('Anonymous UUID:')): 7174b846820SJonas Devlieghere # ignore these 71816dd6934SJonas Devlieghere return 7194b846820SJonas Devlieghere elif line.startswith('Thread'): 7204b846820SJonas Devlieghere thread_state_match = self.thread_state_regex.search(line) 7214b846820SJonas Devlieghere if thread_state_match: 7224b846820SJonas Devlieghere self.app_specific_backtrace = False 7234b846820SJonas Devlieghere thread_state_match = self.thread_regex.search(line) 7244b846820SJonas Devlieghere thread_idx = int(thread_state_match.group(1)) 7254b846820SJonas Devlieghere self.parse_mode = CrashLogParseMode.THREGS 7264b846820SJonas Devlieghere self.thread = self.crashlog.threads[thread_idx] 72716dd6934SJonas Devlieghere return 7284b846820SJonas Devlieghere thread_insts_match = self.thread_instrs_regex.search(line) 7294b846820SJonas Devlieghere if thread_insts_match: 7304b846820SJonas Devlieghere self.parse_mode = CrashLogParseMode.INSTRS 73116dd6934SJonas Devlieghere return 7324b846820SJonas Devlieghere thread_match = self.thread_regex.search(line) 7334b846820SJonas Devlieghere if thread_match: 7344b846820SJonas Devlieghere self.app_specific_backtrace = False 7354b846820SJonas Devlieghere self.parse_mode = CrashLogParseMode.THREAD 7364b846820SJonas Devlieghere thread_idx = int(thread_match.group(1)) 7374b846820SJonas Devlieghere self.thread = self.crashlog.Thread(thread_idx, False) 73816dd6934SJonas Devlieghere return 73916dd6934SJonas Devlieghere return 7404b846820SJonas Devlieghere elif line.startswith('Binary Images:'): 7414b846820SJonas Devlieghere self.parse_mode = CrashLogParseMode.IMAGES 74216dd6934SJonas Devlieghere return 7434b846820SJonas Devlieghere elif line.startswith('Application Specific Backtrace'): 7444b846820SJonas Devlieghere app_backtrace_match = self.app_backtrace_regex.search(line) 7454b846820SJonas Devlieghere if app_backtrace_match: 7464b846820SJonas Devlieghere self.parse_mode = CrashLogParseMode.THREAD 7474b846820SJonas Devlieghere self.app_specific_backtrace = True 7484b846820SJonas Devlieghere idx = int(app_backtrace_match.group(1)) 7494b846820SJonas Devlieghere self.thread = self.crashlog.Thread(idx, True) 7504b846820SJonas Devlieghere elif line.startswith('Last Exception Backtrace:'): # iOS 7514b846820SJonas Devlieghere self.parse_mode = CrashLogParseMode.THREAD 7524b846820SJonas Devlieghere self.app_specific_backtrace = True 7534b846820SJonas Devlieghere idx = 1 7544b846820SJonas Devlieghere self.thread = self.crashlog.Thread(idx, True) 7554b846820SJonas Devlieghere self.crashlog.info_lines.append(line.strip()) 75616dd6934SJonas Devlieghere 75716dd6934SJonas Devlieghere def parse_thread(self, line): 7584b846820SJonas Devlieghere if line.startswith('Thread'): 75916dd6934SJonas Devlieghere return 7604b846820SJonas Devlieghere if self.null_frame_regex.search(line): 7614b846820SJonas Devlieghere print('warning: thread parser ignored null-frame: "%s"' % line) 76216dd6934SJonas Devlieghere return 7634b846820SJonas Devlieghere frame_match = self.frame_regex.search(line) 7644b846820SJonas Devlieghere if frame_match: 7654b846820SJonas Devlieghere (frame_id, frame_img_name, _, frame_img_version, _, 7664b846820SJonas Devlieghere frame_addr, frame_ofs) = frame_match.groups() 7674b846820SJonas Devlieghere ident = frame_img_name 7684b846820SJonas Devlieghere self.thread.add_ident(ident) 7694b846820SJonas Devlieghere if ident not in self.crashlog.idents: 7704b846820SJonas Devlieghere self.crashlog.idents.append(ident) 7714b846820SJonas Devlieghere self.thread.frames.append(self.crashlog.Frame(int(frame_id), int( 7724b846820SJonas Devlieghere frame_addr, 0), frame_ofs)) 7734b846820SJonas Devlieghere else: 7744b846820SJonas Devlieghere print('error: frame regex failed for line: "%s"' % line) 77516dd6934SJonas Devlieghere 77616dd6934SJonas Devlieghere def parse_images(self, line): 7774b846820SJonas Devlieghere image_match = self.image_regex_uuid.search(line) 7784b846820SJonas Devlieghere if image_match: 7794b846820SJonas Devlieghere (img_lo, img_hi, img_name, _, img_version, _, 7804b846820SJonas Devlieghere _, img_uuid, img_path) = image_match.groups() 7814b846820SJonas Devlieghere image = self.crashlog.DarwinImage(int(img_lo, 0), int(img_hi, 0), 7824b846820SJonas Devlieghere img_name.strip(), 7834b846820SJonas Devlieghere img_version.strip() 7844b846820SJonas Devlieghere if img_version else "", 7854b846820SJonas Devlieghere uuid.UUID(img_uuid), img_path, 7864b846820SJonas Devlieghere self.verbose) 7874b846820SJonas Devlieghere self.crashlog.images.append(image) 7884b846820SJonas Devlieghere else: 7894b846820SJonas Devlieghere print("error: image regex failed for: %s" % line) 7904b846820SJonas Devlieghere 79116dd6934SJonas Devlieghere 79216dd6934SJonas Devlieghere def parse_thread_registers(self, line): 7934b846820SJonas Devlieghere stripped_line = line.strip() 7944b846820SJonas Devlieghere # "r12: 0x00007fff6b5939c8 r13: 0x0000000007000006 r14: 0x0000000000002a03 r15: 0x0000000000000c00" 7954b846820SJonas Devlieghere reg_values = re.findall( 7964b846820SJonas Devlieghere '([a-zA-Z0-9]+: 0[Xx][0-9a-fA-F]+) *', stripped_line) 7974b846820SJonas Devlieghere for reg_value in reg_values: 7984b846820SJonas Devlieghere (reg, value) = reg_value.split(': ') 7994b846820SJonas Devlieghere self.thread.registers[reg.strip()] = int(value, 0) 80016dd6934SJonas Devlieghere 80116dd6934SJonas Devlieghere def parse_system(self, line): 8024b846820SJonas Devlieghere self.crashlog.system_profile.append(line) 80316dd6934SJonas Devlieghere 80416dd6934SJonas Devlieghere def parse_instructions(self, line): 8054b846820SJonas Devlieghere pass 8064b846820SJonas Devlieghere 8074b846820SJonas Devlieghere 808aa9d02b1SGreg Claytondef usage(): 809a658ab9fSDavide Italiano print("Usage: lldb-symbolicate.py [-n name] executable-image") 810aa9d02b1SGreg Clayton sys.exit(0) 811aa9d02b1SGreg Clayton 812b9c1b51eSKate Stone 8138bebdff4SJim Inghamdef save_crashlog(debugger, command, exe_ctx, result, dict): 814f47b2c26SGreg Clayton usage = "usage: %prog [options] <output-path>" 815f47b2c26SGreg Clayton description = '''Export the state of current target into a crashlog file''' 816b9c1b51eSKate Stone parser = optparse.OptionParser( 817b9c1b51eSKate Stone description=description, 818b9c1b51eSKate Stone prog='save_crashlog', 819b9c1b51eSKate Stone usage=usage) 820b9c1b51eSKate Stone parser.add_option( 821b9c1b51eSKate Stone '-v', 822b9c1b51eSKate Stone '--verbose', 823b9c1b51eSKate Stone action='store_true', 824b9c1b51eSKate Stone dest='verbose', 825b9c1b51eSKate Stone help='display verbose debug info', 826b9c1b51eSKate Stone default=False) 827f47b2c26SGreg Clayton try: 828f47b2c26SGreg Clayton (options, args) = parser.parse_args(shlex.split(command)) 829f47b2c26SGreg Clayton except: 830b9c1b51eSKate Stone result.PutCString("error: invalid options") 831f47b2c26SGreg Clayton return 832f47b2c26SGreg Clayton if len(args) != 1: 833b9c1b51eSKate Stone result.PutCString( 834b9c1b51eSKate Stone "error: invalid arguments, a single output file is the only valid argument") 835f47b2c26SGreg Clayton return 836f47b2c26SGreg Clayton out_file = open(args[0], 'w') 837f47b2c26SGreg Clayton if not out_file: 838b9c1b51eSKate Stone result.PutCString( 839b9c1b51eSKate Stone "error: failed to open file '%s' for writing...", 840b9c1b51eSKate Stone args[0]) 841f47b2c26SGreg Clayton return 8428bebdff4SJim Ingham target = exe_ctx.target 843c917a381SJim Ingham if target: 844c917a381SJim Ingham identifier = target.executable.basename 8458bebdff4SJim Ingham process = exe_ctx.process 8468bebdff4SJim Ingham if process: 8478bebdff4SJim Ingham pid = process.id 848f47b2c26SGreg Clayton if pid != lldb.LLDB_INVALID_PROCESS_ID: 849b9c1b51eSKate Stone out_file.write( 850b9c1b51eSKate Stone 'Process: %s [%u]\n' % 851b9c1b51eSKate Stone (identifier, pid)) 852c917a381SJim Ingham out_file.write('Path: %s\n' % (target.executable.fullpath)) 853f47b2c26SGreg Clayton out_file.write('Identifier: %s\n' % (identifier)) 854b9c1b51eSKate Stone out_file.write('\nDate/Time: %s\n' % 855b9c1b51eSKate Stone (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) 856b9c1b51eSKate Stone out_file.write( 857b9c1b51eSKate Stone 'OS Version: Mac OS X %s (%s)\n' % 858573ffd88SAdrian Prantl (platform.mac_ver()[0], subprocess.check_output('sysctl -n kern.osversion', shell=True).decode("utf-8"))) 859f47b2c26SGreg Clayton out_file.write('Report Version: 9\n') 8608bebdff4SJim Ingham for thread_idx in range(process.num_threads): 8618bebdff4SJim Ingham thread = process.thread[thread_idx] 862f47b2c26SGreg Clayton out_file.write('\nThread %u:\n' % (thread_idx)) 863f47b2c26SGreg Clayton for (frame_idx, frame) in enumerate(thread.frames): 864f47b2c26SGreg Clayton frame_pc = frame.pc 865f47b2c26SGreg Clayton frame_offset = 0 866f47b2c26SGreg Clayton if frame.function: 867f47b2c26SGreg Clayton block = frame.GetFrameBlock() 868f47b2c26SGreg Clayton block_range = block.range[frame.addr] 869f47b2c26SGreg Clayton if block_range: 870f47b2c26SGreg Clayton block_start_addr = block_range[0] 871723a1caaSJim Ingham frame_offset = frame_pc - block_start_addr.GetLoadAddress(target) 872f47b2c26SGreg Clayton else: 873723a1caaSJim Ingham frame_offset = frame_pc - frame.function.addr.GetLoadAddress(target) 874f47b2c26SGreg Clayton elif frame.symbol: 875723a1caaSJim Ingham frame_offset = frame_pc - frame.symbol.addr.GetLoadAddress(target) 876b9c1b51eSKate Stone out_file.write( 877b9c1b51eSKate Stone '%-3u %-32s 0x%16.16x %s' % 878b9c1b51eSKate Stone (frame_idx, frame.module.file.basename, frame_pc, frame.name)) 879f47b2c26SGreg Clayton if frame_offset > 0: 880f47b2c26SGreg Clayton out_file.write(' + %u' % (frame_offset)) 881f47b2c26SGreg Clayton line_entry = frame.line_entry 882f47b2c26SGreg Clayton if line_entry: 883f47b2c26SGreg Clayton if options.verbose: 884f47b2c26SGreg Clayton # This will output the fullpath + line + column 885f47b2c26SGreg Clayton out_file.write(' %s' % (line_entry)) 886f47b2c26SGreg Clayton else: 887b9c1b51eSKate Stone out_file.write( 888b9c1b51eSKate Stone ' %s:%u' % 889b9c1b51eSKate Stone (line_entry.file.basename, line_entry.line)) 890f47b2c26SGreg Clayton column = line_entry.column 891f47b2c26SGreg Clayton if column: 892f47b2c26SGreg Clayton out_file.write(':%u' % (column)) 893f47b2c26SGreg Clayton out_file.write('\n') 894f47b2c26SGreg Clayton 895f47b2c26SGreg Clayton out_file.write('\nBinary Images:\n') 896c917a381SJim Ingham for module in target.modules: 897f47b2c26SGreg Clayton text_segment = module.section['__TEXT'] 898f47b2c26SGreg Clayton if text_segment: 899c917a381SJim Ingham text_segment_load_addr = text_segment.GetLoadAddress(target) 900f47b2c26SGreg Clayton if text_segment_load_addr != lldb.LLDB_INVALID_ADDRESS: 901f47b2c26SGreg Clayton text_segment_end_load_addr = text_segment_load_addr + text_segment.size 902f47b2c26SGreg Clayton identifier = module.file.basename 903f47b2c26SGreg Clayton module_version = '???' 904f47b2c26SGreg Clayton module_version_array = module.GetVersion() 905f47b2c26SGreg Clayton if module_version_array: 906b9c1b51eSKate Stone module_version = '.'.join( 907b9c1b51eSKate Stone map(str, module_version_array)) 908b9c1b51eSKate Stone out_file.write( 909b9c1b51eSKate Stone ' 0x%16.16x - 0x%16.16x %s (%s - ???) <%s> %s\n' % 910b9c1b51eSKate Stone (text_segment_load_addr, 911b9c1b51eSKate Stone text_segment_end_load_addr, 912b9c1b51eSKate Stone identifier, 913b9c1b51eSKate Stone module_version, 914b9c1b51eSKate Stone module.GetUUIDString(), 915b9c1b51eSKate Stone module.file.fullpath)) 916f47b2c26SGreg Clayton out_file.close() 917f47b2c26SGreg Clayton else: 918b9c1b51eSKate Stone result.PutCString("error: invalid target") 919f47b2c26SGreg Clayton 920f47b2c26SGreg Clayton 921cb5ea132SDave Leeclass Symbolicate: 922cb5ea132SDave Lee def __init__(self, debugger, internal_dict): 923cb5ea132SDave Lee pass 924cb5ea132SDave Lee 925cb5ea132SDave Lee def __call__(self, debugger, command, exe_ctx, result): 926c29c24beSJonas Devlieghere SymbolicateCrashLogs(debugger, shlex.split(command)) 927aa9d02b1SGreg Clayton 928cb5ea132SDave Lee def get_short_help(self): 929cb5ea132SDave Lee return "Symbolicate one or more darwin crash log files." 930cb5ea132SDave Lee 931cb5ea132SDave Lee def get_long_help(self): 932cb5ea132SDave Lee option_parser = CrashLogOptionParser() 933cb5ea132SDave Lee return option_parser.format_help() 934cb5ea132SDave Lee 935b9c1b51eSKate Stone 936eb749096SGreg Claytondef SymbolicateCrashLog(crash_log, options): 937a7fb1dcdSGreg Clayton if options.debug: 938aa9d02b1SGreg Clayton crash_log.dump() 9395c0f483eSGreg Clayton if not crash_log.images: 940a658ab9fSDavide Italiano print('error: no images in crash log') 9415c0f483eSGreg Clayton return 9425c0f483eSGreg Clayton 9431f746071SGreg Clayton if options.dump_image_list: 944a658ab9fSDavide Italiano print("Binary Images:") 9451f746071SGreg Clayton for image in crash_log.images: 9461f746071SGreg Clayton if options.verbose: 947a658ab9fSDavide Italiano print(image.debug_dump()) 9481f746071SGreg Clayton else: 949a658ab9fSDavide Italiano print(image) 9501f746071SGreg Clayton 9513c2c4bb2SGreg Clayton target = crash_log.create_target() 9523c2c4bb2SGreg Clayton if not target: 9535c0f483eSGreg Clayton return 9543c2c4bb2SGreg Clayton exe_module = target.GetModuleAtIndex(0) 9555c0f483eSGreg Clayton images_to_load = list() 9563c2c4bb2SGreg Clayton loaded_images = list() 9575c0f483eSGreg Clayton if options.load_all_images: 9585c0f483eSGreg Clayton # --load-all option was specified, load everything up 9595c0f483eSGreg Clayton for image in crash_log.images: 9605c0f483eSGreg Clayton images_to_load.append(image) 9615c0f483eSGreg Clayton else: 9625c0f483eSGreg Clayton # Only load the images found in stack frames for the crashed threads 963563d0393SGreg Clayton if options.crashed_only: 964563d0393SGreg Clayton for thread in crash_log.threads: 965563d0393SGreg Clayton if thread.did_crash(): 966563d0393SGreg Clayton for ident in thread.idents: 967563d0393SGreg Clayton images = crash_log.find_images_with_identifier(ident) 968563d0393SGreg Clayton if images: 969563d0393SGreg Clayton for image in images: 970563d0393SGreg Clayton images_to_load.append(image) 971563d0393SGreg Clayton else: 972a658ab9fSDavide Italiano print('error: can\'t find image for identifier "%s"' % ident) 973563d0393SGreg Clayton else: 974aa9d02b1SGreg Clayton for ident in crash_log.idents: 9753c2c4bb2SGreg Clayton images = crash_log.find_images_with_identifier(ident) 9763c2c4bb2SGreg Clayton if images: 9773c2c4bb2SGreg Clayton for image in images: 9785c0f483eSGreg Clayton images_to_load.append(image) 9795c0f483eSGreg Clayton else: 980a658ab9fSDavide Italiano print('error: can\'t find image for identifier "%s"' % ident) 9815c0f483eSGreg Clayton 982a8abb695SJonas Devlieghere futures = [] 983a8abb695SJonas Devlieghere with concurrent.futures.ThreadPoolExecutor() as executor: 984a8abb695SJonas Devlieghere def add_module(image, target): 985a8abb695SJonas Devlieghere return image, image.add_module(target) 986a8abb695SJonas Devlieghere 9875c0f483eSGreg Clayton for image in images_to_load: 988a8abb695SJonas Devlieghere futures.append(executor.submit(add_module, image=image, target=target)) 989a8abb695SJonas Devlieghere 990a8abb695SJonas Devlieghere for future in concurrent.futures.as_completed(futures): 991a8abb695SJonas Devlieghere image, err = future.result() 992aa9d02b1SGreg Clayton if err: 993a658ab9fSDavide Italiano print(err) 994aa9d02b1SGreg Clayton else: 9953c2c4bb2SGreg Clayton loaded_images.append(image) 996aa9d02b1SGreg Clayton 99748d157ddSGreg Clayton if crash_log.backtraces: 99848d157ddSGreg Clayton for thread in crash_log.backtraces: 99948d157ddSGreg Clayton thread.dump_symbolicated(crash_log, options) 1000a658ab9fSDavide Italiano print() 1001f67002ddSGreg Clayton 100248d157ddSGreg Clayton for thread in crash_log.threads: 100348d157ddSGreg Clayton thread.dump_symbolicated(crash_log, options) 1004a658ab9fSDavide Italiano print() 100548d157ddSGreg Clayton 1006b225c5f7SJonas Devlieghere if crash_log.errors: 1007b225c5f7SJonas Devlieghere print("Errors:") 1008b225c5f7SJonas Devlieghere for error in crash_log.errors: 1009b225c5f7SJonas Devlieghere print(error) 1010b225c5f7SJonas Devlieghere 10110a65112cSMed Ismail Bennanidef load_crashlog_in_scripted_process(debugger, crash_log_file, options): 10127c54ffdcSMed Ismail Bennani result = lldb.SBCommandReturnObject() 10137c54ffdcSMed Ismail Bennani 10147c54ffdcSMed Ismail Bennani crashlog_path = os.path.expanduser(crash_log_file) 10157c54ffdcSMed Ismail Bennani if not os.path.exists(crashlog_path): 10167c54ffdcSMed Ismail Bennani result.PutCString("error: crashlog file %s does not exist" % crashlog_path) 10177c54ffdcSMed Ismail Bennani 10187c54ffdcSMed Ismail Bennani crashlog = CrashLogParser().parse(debugger, crashlog_path, False) 10197c54ffdcSMed Ismail Bennani 1020badb6e27SMed Ismail Bennani if debugger.GetNumTargets() > 0: 1021badb6e27SMed Ismail Bennani target = debugger.GetTargetAtIndex(0) 1022badb6e27SMed Ismail Bennani else: 10237c54ffdcSMed Ismail Bennani target = crashlog.create_target() 10247c54ffdcSMed Ismail Bennani if not target: 10257c54ffdcSMed Ismail Bennani result.PutCString("error: couldn't create target") 10267c54ffdcSMed Ismail Bennani return 10277c54ffdcSMed Ismail Bennani 10287c54ffdcSMed Ismail Bennani ci = debugger.GetCommandInterpreter() 10297c54ffdcSMed Ismail Bennani if not ci: 10307c54ffdcSMed Ismail Bennani result.PutCString("error: couldn't get command interpreter") 10317c54ffdcSMed Ismail Bennani return 10327c54ffdcSMed Ismail Bennani 10337c54ffdcSMed Ismail Bennani res = lldb.SBCommandReturnObject() 10347c54ffdcSMed Ismail Bennani ci.HandleCommand('script from lldb.macosx import crashlog_scripted_process', res) 10357c54ffdcSMed Ismail Bennani if not res.Succeeded(): 10367c54ffdcSMed Ismail Bennani result.PutCString("error: couldn't import crashlog scripted process module") 10377c54ffdcSMed Ismail Bennani return 10387c54ffdcSMed Ismail Bennani 10397c54ffdcSMed Ismail Bennani structured_data = lldb.SBStructuredData() 10400a65112cSMed Ismail Bennani structured_data.SetFromJSON(json.dumps({ "crashlog_path" : crashlog_path, 10410a65112cSMed Ismail Bennani "load_all_images": options.load_all_images })) 10427c54ffdcSMed Ismail Bennani launch_info = lldb.SBLaunchInfo(None) 10437c54ffdcSMed Ismail Bennani launch_info.SetProcessPluginName("ScriptedProcess") 10447c54ffdcSMed Ismail Bennani launch_info.SetScriptedProcessClassName("crashlog_scripted_process.CrashLogScriptedProcess") 10457c54ffdcSMed Ismail Bennani launch_info.SetScriptedProcessDictionary(structured_data) 10467c54ffdcSMed Ismail Bennani error = lldb.SBError() 10477c54ffdcSMed Ismail Bennani process = target.Launch(launch_info, error) 1048aa9d02b1SGreg Clayton 10498d097a6bSMed Ismail Bennani if not process or error.Fail(): 10508d097a6bSMed Ismail Bennani return 10518d097a6bSMed Ismail Bennani 10528d097a6bSMed Ismail Bennani @contextlib.contextmanager 10538d097a6bSMed Ismail Bennani def synchronous(debugger): 10548d097a6bSMed Ismail Bennani async_state = debugger.GetAsync() 10558d097a6bSMed Ismail Bennani debugger.SetAsync(False) 10568d097a6bSMed Ismail Bennani try: 10578d097a6bSMed Ismail Bennani yield 10588d097a6bSMed Ismail Bennani finally: 10598d097a6bSMed Ismail Bennani debugger.SetAsync(async_state) 10608d097a6bSMed Ismail Bennani 10618d097a6bSMed Ismail Bennani with synchronous(debugger): 10628d097a6bSMed Ismail Bennani run_options = lldb.SBCommandInterpreterRunOptions() 10638d097a6bSMed Ismail Bennani run_options.SetStopOnError(True) 10648d097a6bSMed Ismail Bennani run_options.SetStopOnCrash(True) 10658d097a6bSMed Ismail Bennani run_options.SetEchoCommands(True) 10668d097a6bSMed Ismail Bennani 10678d097a6bSMed Ismail Bennani commands_stream = lldb.SBStream() 10688d097a6bSMed Ismail Bennani commands_stream.Print("process status\n") 10698d097a6bSMed Ismail Bennani commands_stream.Print("thread backtrace\n") 10708d097a6bSMed Ismail Bennani error = debugger.SetInputString(commands_stream.GetData()) 10718d097a6bSMed Ismail Bennani if error.Success(): 10728d097a6bSMed Ismail Bennani debugger.RunCommandInterpreter(True, False, run_options, 0, False, True) 10738d097a6bSMed Ismail Bennani 1074b9c1b51eSKate Stonedef CreateSymbolicateCrashLogOptions( 1075b9c1b51eSKate Stone command_name, 1076b9c1b51eSKate Stone description, 1077b9c1b51eSKate Stone add_interactive_options): 1078eb749096SGreg Clayton usage = "usage: %prog [options] <FILE> [FILE ...]" 1079b9c1b51eSKate Stone option_parser = optparse.OptionParser( 1080b9c1b51eSKate Stone description=description, prog='crashlog', usage=usage) 1081b9c1b51eSKate Stone option_parser.add_option( 1082b9c1b51eSKate Stone '--verbose', 1083b9c1b51eSKate Stone '-v', 1084b9c1b51eSKate Stone action='store_true', 1085b9c1b51eSKate Stone dest='verbose', 1086b9c1b51eSKate Stone help='display verbose debug info', 1087b9c1b51eSKate Stone default=False) 1088b9c1b51eSKate Stone option_parser.add_option( 1089b9c1b51eSKate Stone '--debug', 1090b9c1b51eSKate Stone '-g', 1091b9c1b51eSKate Stone action='store_true', 1092b9c1b51eSKate Stone dest='debug', 1093b9c1b51eSKate Stone help='display verbose debug logging', 1094b9c1b51eSKate Stone default=False) 1095b9c1b51eSKate Stone option_parser.add_option( 1096b9c1b51eSKate Stone '--load-all', 1097b9c1b51eSKate Stone '-a', 1098b9c1b51eSKate Stone action='store_true', 1099b9c1b51eSKate Stone dest='load_all_images', 11000a65112cSMed Ismail Bennani help='load all executable images, not just the images found in the ' 11010a65112cSMed Ismail Bennani 'crashed stack frames, loads stackframes for all the threads in ' 11020a65112cSMed Ismail Bennani 'interactive mode.', 1103b9c1b51eSKate Stone default=False) 1104b9c1b51eSKate Stone option_parser.add_option( 1105b9c1b51eSKate Stone '--images', 1106b9c1b51eSKate Stone action='store_true', 1107b9c1b51eSKate Stone dest='dump_image_list', 1108b9c1b51eSKate Stone help='show image list', 1109b9c1b51eSKate Stone default=False) 1110b9c1b51eSKate Stone option_parser.add_option( 1111b9c1b51eSKate Stone '--debug-delay', 1112b9c1b51eSKate Stone type='int', 1113b9c1b51eSKate Stone dest='debug_delay', 1114b9c1b51eSKate Stone metavar='NSEC', 1115b9c1b51eSKate Stone help='pause for NSEC seconds for debugger', 1116b9c1b51eSKate Stone default=0) 1117b9c1b51eSKate Stone option_parser.add_option( 1118b9c1b51eSKate Stone '--crashed-only', 1119b9c1b51eSKate Stone '-c', 1120b9c1b51eSKate Stone action='store_true', 1121b9c1b51eSKate Stone dest='crashed_only', 1122b9c1b51eSKate Stone help='only symbolicate the crashed thread', 1123b9c1b51eSKate Stone default=False) 1124b9c1b51eSKate Stone option_parser.add_option( 1125b9c1b51eSKate Stone '--disasm-depth', 1126b9c1b51eSKate Stone '-d', 1127b9c1b51eSKate Stone type='int', 1128b9c1b51eSKate Stone dest='disassemble_depth', 1129b9c1b51eSKate Stone help='set the depth in stack frames that should be disassembled (default is 1)', 1130b9c1b51eSKate Stone default=1) 1131b9c1b51eSKate Stone option_parser.add_option( 1132b9c1b51eSKate Stone '--disasm-all', 1133b9c1b51eSKate Stone '-D', 1134b9c1b51eSKate Stone action='store_true', 1135b9c1b51eSKate Stone dest='disassemble_all_threads', 1136b9c1b51eSKate Stone help='enabled disassembly of frames on all threads (not just the crashed thread)', 1137b9c1b51eSKate Stone default=False) 1138b9c1b51eSKate Stone option_parser.add_option( 1139b9c1b51eSKate Stone '--disasm-before', 1140b9c1b51eSKate Stone '-B', 1141b9c1b51eSKate Stone type='int', 1142b9c1b51eSKate Stone dest='disassemble_before', 1143b9c1b51eSKate Stone help='the number of instructions to disassemble before the frame PC', 1144b9c1b51eSKate Stone default=4) 1145b9c1b51eSKate Stone option_parser.add_option( 1146b9c1b51eSKate Stone '--disasm-after', 1147b9c1b51eSKate Stone '-A', 1148b9c1b51eSKate Stone type='int', 1149b9c1b51eSKate Stone dest='disassemble_after', 1150b9c1b51eSKate Stone help='the number of instructions to disassemble after the frame PC', 1151b9c1b51eSKate Stone default=4) 1152b9c1b51eSKate Stone option_parser.add_option( 1153b9c1b51eSKate Stone '--source-context', 1154b9c1b51eSKate Stone '-C', 1155b9c1b51eSKate Stone type='int', 1156b9c1b51eSKate Stone metavar='NLINES', 1157b9c1b51eSKate Stone dest='source_context', 1158b9c1b51eSKate Stone help='show NLINES source lines of source context (default = 4)', 1159b9c1b51eSKate Stone default=4) 1160b9c1b51eSKate Stone option_parser.add_option( 1161b9c1b51eSKate Stone '--source-frames', 1162b9c1b51eSKate Stone type='int', 1163b9c1b51eSKate Stone metavar='NFRAMES', 1164b9c1b51eSKate Stone dest='source_frames', 1165b9c1b51eSKate Stone help='show source for NFRAMES (default = 4)', 1166b9c1b51eSKate Stone default=4) 1167b9c1b51eSKate Stone option_parser.add_option( 1168b9c1b51eSKate Stone '--source-all', 1169b9c1b51eSKate Stone action='store_true', 1170b9c1b51eSKate Stone dest='source_all', 1171b9c1b51eSKate Stone help='show source for all threads, not just the crashed thread', 1172b9c1b51eSKate Stone default=False) 1173eb749096SGreg Clayton if add_interactive_options: 1174b9c1b51eSKate Stone option_parser.add_option( 1175b9c1b51eSKate Stone '-i', 1176b9c1b51eSKate Stone '--interactive', 1177b9c1b51eSKate Stone action='store_true', 11787c54ffdcSMed Ismail Bennani help='parse a crash log and load it in a ScriptedProcess', 1179b9c1b51eSKate Stone default=False) 11807c54ffdcSMed Ismail Bennani option_parser.add_option( 11817c54ffdcSMed Ismail Bennani '-b', 11827c54ffdcSMed Ismail Bennani '--batch', 11837c54ffdcSMed Ismail Bennani action='store_true', 11847c54ffdcSMed Ismail Bennani help='dump symbolicated stackframes without creating a debug session', 11857c54ffdcSMed Ismail Bennani default=True) 1186eb749096SGreg Clayton return option_parser 1187eb749096SGreg Clayton 1188b9c1b51eSKate Stone 1189cb5ea132SDave Leedef CrashLogOptionParser(): 1190eb749096SGreg Clayton description = '''Symbolicate one or more darwin crash log files to provide source file and line information, 1191eb749096SGreg Claytoninlined stack frames back to the concrete functions, and disassemble the location of the crash 1192eb749096SGreg Claytonfor the first frame of the crashed thread. 1193eb749096SGreg ClaytonIf this script is imported into the LLDB command interpreter, a "crashlog" command will be added to the interpreter 1194eb749096SGreg Claytonfor use at the LLDB command line. After a crash log has been parsed and symbolicated, a target will have been 1195eb749096SGreg Claytoncreated that has all of the shared libraries loaded at the load addresses found in the crash log file. This allows 1196eb749096SGreg Claytonyou to explore the program as if it were stopped at the locations described in the crash log and functions can 1197eb749096SGreg Claytonbe disassembled and lookups can be performed using the addresses found in the crash log.''' 1198cb5ea132SDave Lee return CreateSymbolicateCrashLogOptions('crashlog', description, True) 1199cb5ea132SDave Lee 1200cb5ea132SDave Leedef SymbolicateCrashLogs(debugger, command_args): 1201cb5ea132SDave Lee option_parser = CrashLogOptionParser() 1202*5a9fa21cSMed Ismail Bennani 1203*5a9fa21cSMed Ismail Bennani if not len(command_args): 1204*5a9fa21cSMed Ismail Bennani option_parser.print_help() 1205*5a9fa21cSMed Ismail Bennani return 1206*5a9fa21cSMed Ismail Bennani 1207eb749096SGreg Clayton try: 1208eb749096SGreg Clayton (options, args) = option_parser.parse_args(command_args) 1209eb749096SGreg Clayton except: 1210eb749096SGreg Clayton return 1211eb749096SGreg Clayton 1212a7fb1dcdSGreg Clayton if options.debug: 1213a658ab9fSDavide Italiano print('command_args = %s' % command_args) 1214a658ab9fSDavide Italiano print('options', options) 1215a658ab9fSDavide Italiano print('args', args) 1216eb749096SGreg Clayton 1217eb749096SGreg Clayton if options.debug_delay > 0: 1218a658ab9fSDavide Italiano print("Waiting %u seconds for debugger to attach..." % options.debug_delay) 1219eb749096SGreg Clayton time.sleep(options.debug_delay) 1220eb749096SGreg Clayton error = lldb.SBError() 1221eb749096SGreg Clayton 12227c54ffdcSMed Ismail Bennani def should_run_in_interactive_mode(options, ci): 1223eb749096SGreg Clayton if options.interactive: 12247c54ffdcSMed Ismail Bennani return True 12257c54ffdcSMed Ismail Bennani elif options.batch: 12267c54ffdcSMed Ismail Bennani return False 12277c54ffdcSMed Ismail Bennani # elif ci and ci.IsInteractive(): 12287c54ffdcSMed Ismail Bennani # return True 1229eb749096SGreg Clayton else: 12307c54ffdcSMed Ismail Bennani return False 12317c54ffdcSMed Ismail Bennani 12327c54ffdcSMed Ismail Bennani ci = debugger.GetCommandInterpreter() 12337c54ffdcSMed Ismail Bennani 12347c54ffdcSMed Ismail Bennani if args: 1235eb749096SGreg Clayton for crash_log_file in args: 12367c54ffdcSMed Ismail Bennani if should_run_in_interactive_mode(options, ci): 12370a65112cSMed Ismail Bennani load_crashlog_in_scripted_process(debugger, crash_log_file, 12380a65112cSMed Ismail Bennani options) 12397c54ffdcSMed Ismail Bennani else: 1240c7cbf32fSJonas Devlieghere crash_log = CrashLogParser().parse(debugger, crash_log_file, options.verbose) 1241eb749096SGreg Clayton SymbolicateCrashLog(crash_log, options) 1242cb5ea132SDave Lee 1243aa9d02b1SGreg Claytonif __name__ == '__main__': 124442a6eb71SGreg Clayton # Create a new debugger instance 1245c29c24beSJonas Devlieghere debugger = lldb.SBDebugger.Create() 1246c29c24beSJonas Devlieghere SymbolicateCrashLogs(debugger, sys.argv[1:]) 1247c29c24beSJonas Devlieghere lldb.SBDebugger.Destroy(debugger) 1248cb5ea132SDave Lee 1249cb5ea132SDave Leedef __lldb_init_module(debugger, internal_dict): 1250cb5ea132SDave Lee debugger.HandleCommand( 1251cb5ea132SDave Lee 'command script add -c lldb.macosx.crashlog.Symbolicate crashlog') 1252cb5ea132SDave Lee debugger.HandleCommand( 1253b9c1b51eSKate Stone 'command script add -f lldb.macosx.crashlog.save_crashlog save_crashlog') 1254f8d889a7SDave Lee print('"crashlog" and "save_crashlog" commands have been installed, use ' 1255f8d889a7SDave Lee 'the "--help" options on these commands for detailed help.') 1256