1*f6aab3d8Srobertimport os,json,struct,signal,uuid 2*f6aab3d8Srobert 3*f6aab3d8Srobertfrom typing import Any, Dict 4*f6aab3d8Srobert 5*f6aab3d8Srobertimport lldb 6*f6aab3d8Srobertfrom lldb.plugins.scripted_process import ScriptedProcess 7*f6aab3d8Srobertfrom lldb.plugins.scripted_process import ScriptedThread 8*f6aab3d8Srobert 9*f6aab3d8Srobertfrom lldb.macosx.crashlog import CrashLog,CrashLogParser 10*f6aab3d8Srobert 11*f6aab3d8Srobertclass CrashLogScriptedProcess(ScriptedProcess): 12*f6aab3d8Srobert def parse_crashlog(self): 13*f6aab3d8Srobert crashlog_parser = CrashLogParser.create(self.dbg, self.crashlog_path, False) 14*f6aab3d8Srobert crash_log = crashlog_parser.parse() 15*f6aab3d8Srobert 16*f6aab3d8Srobert self.pid = crash_log.process_id 17*f6aab3d8Srobert self.addr_mask = crash_log.addr_mask 18*f6aab3d8Srobert self.crashed_thread_idx = crash_log.crashed_thread_idx 19*f6aab3d8Srobert self.loaded_images = [] 20*f6aab3d8Srobert self.exception = crash_log.exception 21*f6aab3d8Srobert self.app_specific_thread = None 22*f6aab3d8Srobert if hasattr(crash_log, 'asi'): 23*f6aab3d8Srobert self.metadata['asi'] = crash_log.asi 24*f6aab3d8Srobert if hasattr(crash_log, 'asb'): 25*f6aab3d8Srobert self.extended_thread_info = crash_log.asb 26*f6aab3d8Srobert 27*f6aab3d8Srobert def load_images(self, images): 28*f6aab3d8Srobert #TODO: Add to self.loaded_images and load images in lldb 29*f6aab3d8Srobert if images: 30*f6aab3d8Srobert for image in images: 31*f6aab3d8Srobert if image not in self.loaded_images: 32*f6aab3d8Srobert if image.uuid == uuid.UUID(int=0): 33*f6aab3d8Srobert continue 34*f6aab3d8Srobert err = image.add_module(self.target) 35*f6aab3d8Srobert if err: 36*f6aab3d8Srobert # Append to SBCommandReturnObject 37*f6aab3d8Srobert print(err) 38*f6aab3d8Srobert else: 39*f6aab3d8Srobert self.loaded_images.append(image) 40*f6aab3d8Srobert 41*f6aab3d8Srobert for thread in crash_log.threads: 42*f6aab3d8Srobert if self.load_all_images: 43*f6aab3d8Srobert load_images(self, crash_log.images) 44*f6aab3d8Srobert elif thread.did_crash(): 45*f6aab3d8Srobert for ident in thread.idents: 46*f6aab3d8Srobert load_images(self, crash_log.find_images_with_identifier(ident)) 47*f6aab3d8Srobert 48*f6aab3d8Srobert if hasattr(thread, 'app_specific_backtrace') and thread.app_specific_backtrace: 49*f6aab3d8Srobert # We don't want to include the Application Specific Backtrace 50*f6aab3d8Srobert # Thread into the Scripted Process' Thread list. 51*f6aab3d8Srobert # Instead, we will try to extract the stackframe pcs from the 52*f6aab3d8Srobert # backtrace and inject that as the extended thread info. 53*f6aab3d8Srobert self.app_specific_thread = thread 54*f6aab3d8Srobert continue 55*f6aab3d8Srobert 56*f6aab3d8Srobert self.threads[thread.index] = CrashLogScriptedThread(self, None, thread) 57*f6aab3d8Srobert 58*f6aab3d8Srobert 59*f6aab3d8Srobert if self.app_specific_thread: 60*f6aab3d8Srobert self.extended_thread_info = \ 61*f6aab3d8Srobert CrashLogScriptedThread.resolve_stackframes(self.app_specific_thread, 62*f6aab3d8Srobert self.addr_mask, 63*f6aab3d8Srobert self.target) 64*f6aab3d8Srobert 65*f6aab3d8Srobert def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData): 66*f6aab3d8Srobert super().__init__(exe_ctx, args) 67*f6aab3d8Srobert 68*f6aab3d8Srobert if not self.target or not self.target.IsValid(): 69*f6aab3d8Srobert # Return error 70*f6aab3d8Srobert return 71*f6aab3d8Srobert 72*f6aab3d8Srobert self.crashlog_path = None 73*f6aab3d8Srobert 74*f6aab3d8Srobert crashlog_path = args.GetValueForKey("file_path") 75*f6aab3d8Srobert if crashlog_path and crashlog_path.IsValid(): 76*f6aab3d8Srobert if crashlog_path.GetType() == lldb.eStructuredDataTypeString: 77*f6aab3d8Srobert self.crashlog_path = crashlog_path.GetStringValue(4096) 78*f6aab3d8Srobert 79*f6aab3d8Srobert if not self.crashlog_path: 80*f6aab3d8Srobert # Return error 81*f6aab3d8Srobert return 82*f6aab3d8Srobert 83*f6aab3d8Srobert load_all_images = args.GetValueForKey("load_all_images") 84*f6aab3d8Srobert if load_all_images and load_all_images.IsValid(): 85*f6aab3d8Srobert if load_all_images.GetType() == lldb.eStructuredDataTypeBoolean: 86*f6aab3d8Srobert self.load_all_images = load_all_images.GetBooleanValue() 87*f6aab3d8Srobert 88*f6aab3d8Srobert if not self.load_all_images: 89*f6aab3d8Srobert self.load_all_images = False 90*f6aab3d8Srobert 91*f6aab3d8Srobert self.pid = super().get_process_id() 92*f6aab3d8Srobert self.crashed_thread_idx = 0 93*f6aab3d8Srobert self.exception = None 94*f6aab3d8Srobert self.extended_thread_info = None 95*f6aab3d8Srobert self.parse_crashlog() 96*f6aab3d8Srobert 97*f6aab3d8Srobert def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo: 98*f6aab3d8Srobert return None 99*f6aab3d8Srobert 100*f6aab3d8Srobert def get_thread_with_id(self, tid: int): 101*f6aab3d8Srobert return {} 102*f6aab3d8Srobert 103*f6aab3d8Srobert def get_registers_for_thread(self, tid: int): 104*f6aab3d8Srobert return {} 105*f6aab3d8Srobert 106*f6aab3d8Srobert def read_memory_at_address(self, addr: int, size: int, error: lldb.SBError) -> lldb.SBData: 107*f6aab3d8Srobert # NOTE: CrashLogs don't contain any memory. 108*f6aab3d8Srobert return lldb.SBData() 109*f6aab3d8Srobert 110*f6aab3d8Srobert def get_loaded_images(self): 111*f6aab3d8Srobert # TODO: Iterate over corefile_target modules and build a data structure 112*f6aab3d8Srobert # from it. 113*f6aab3d8Srobert return self.loaded_images 114*f6aab3d8Srobert 115*f6aab3d8Srobert def get_process_id(self) -> int: 116*f6aab3d8Srobert return self.pid 117*f6aab3d8Srobert 118*f6aab3d8Srobert def should_stop(self) -> bool: 119*f6aab3d8Srobert return True 120*f6aab3d8Srobert 121*f6aab3d8Srobert def is_alive(self) -> bool: 122*f6aab3d8Srobert return True 123*f6aab3d8Srobert 124*f6aab3d8Srobert def get_scripted_thread_plugin(self): 125*f6aab3d8Srobert return CrashLogScriptedThread.__module__ + "." + CrashLogScriptedThread.__name__ 126*f6aab3d8Srobert 127*f6aab3d8Srobert def get_process_metadata(self): 128*f6aab3d8Srobert return self.metadata 129*f6aab3d8Srobert 130*f6aab3d8Srobertclass CrashLogScriptedThread(ScriptedThread): 131*f6aab3d8Srobert def create_register_ctx(self): 132*f6aab3d8Srobert if not self.has_crashed: 133*f6aab3d8Srobert return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0) 134*f6aab3d8Srobert 135*f6aab3d8Srobert if not self.backing_thread or not len(self.backing_thread.registers): 136*f6aab3d8Srobert return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0) 137*f6aab3d8Srobert 138*f6aab3d8Srobert for reg in self.register_info['registers']: 139*f6aab3d8Srobert reg_name = reg['name'] 140*f6aab3d8Srobert if reg_name in self.backing_thread.registers: 141*f6aab3d8Srobert self.register_ctx[reg_name] = self.backing_thread.registers[reg_name] 142*f6aab3d8Srobert else: 143*f6aab3d8Srobert self.register_ctx[reg_name] = 0 144*f6aab3d8Srobert 145*f6aab3d8Srobert return self.register_ctx 146*f6aab3d8Srobert 147*f6aab3d8Srobert def resolve_stackframes(thread, addr_mask, target): 148*f6aab3d8Srobert frames = [] 149*f6aab3d8Srobert for frame in thread.frames: 150*f6aab3d8Srobert frame_pc = frame.pc & addr_mask 151*f6aab3d8Srobert pc = frame_pc if frame.index == 0 or frame_pc == 0 else frame_pc - 1 152*f6aab3d8Srobert sym_addr = lldb.SBAddress() 153*f6aab3d8Srobert sym_addr.SetLoadAddress(pc, target) 154*f6aab3d8Srobert if not sym_addr.IsValid(): 155*f6aab3d8Srobert continue 156*f6aab3d8Srobert frames.append({"idx": frame.index, "pc": pc}) 157*f6aab3d8Srobert return frames 158*f6aab3d8Srobert 159*f6aab3d8Srobert 160*f6aab3d8Srobert def create_stackframes(self): 161*f6aab3d8Srobert if not (self.scripted_process.load_all_images or self.has_crashed): 162*f6aab3d8Srobert return None 163*f6aab3d8Srobert 164*f6aab3d8Srobert if not self.backing_thread or not len(self.backing_thread.frames): 165*f6aab3d8Srobert return None 166*f6aab3d8Srobert 167*f6aab3d8Srobert self.frames = CrashLogScriptedThread.resolve_stackframes(self.backing_thread, 168*f6aab3d8Srobert self.scripted_process.addr_mask, 169*f6aab3d8Srobert self.target) 170*f6aab3d8Srobert 171*f6aab3d8Srobert return self.frames 172*f6aab3d8Srobert 173*f6aab3d8Srobert def __init__(self, process, args, crashlog_thread): 174*f6aab3d8Srobert super().__init__(process, args) 175*f6aab3d8Srobert 176*f6aab3d8Srobert self.backing_thread = crashlog_thread 177*f6aab3d8Srobert self.idx = self.backing_thread.index 178*f6aab3d8Srobert self.tid = self.backing_thread.id 179*f6aab3d8Srobert if self.backing_thread.app_specific_backtrace: 180*f6aab3d8Srobert self.name = "Application Specific Backtrace - " + str(self.idx) 181*f6aab3d8Srobert else: 182*f6aab3d8Srobert self.name = self.backing_thread.name 183*f6aab3d8Srobert self.queue = self.backing_thread.queue 184*f6aab3d8Srobert self.has_crashed = (self.scripted_process.crashed_thread_idx == self.idx) 185*f6aab3d8Srobert self.create_stackframes() 186*f6aab3d8Srobert 187*f6aab3d8Srobert def get_state(self): 188*f6aab3d8Srobert if not self.has_crashed: 189*f6aab3d8Srobert return lldb.eStateStopped 190*f6aab3d8Srobert return lldb.eStateCrashed 191*f6aab3d8Srobert 192*f6aab3d8Srobert def get_stop_reason(self) -> Dict[str, Any]: 193*f6aab3d8Srobert if not self.has_crashed: 194*f6aab3d8Srobert return { "type": lldb.eStopReasonNone } 195*f6aab3d8Srobert # TODO: Investigate what stop reason should be reported when crashed 196*f6aab3d8Srobert stop_reason = { "type": lldb.eStopReasonException, "data": { }} 197*f6aab3d8Srobert if self.scripted_process.exception: 198*f6aab3d8Srobert stop_reason['data']['mach_exception'] = self.scripted_process.exception 199*f6aab3d8Srobert return stop_reason 200*f6aab3d8Srobert 201*f6aab3d8Srobert def get_register_context(self) -> str: 202*f6aab3d8Srobert if not self.register_ctx: 203*f6aab3d8Srobert self.register_ctx = self.create_register_ctx() 204*f6aab3d8Srobert 205*f6aab3d8Srobert return struct.pack("{}Q".format(len(self.register_ctx)), *self.register_ctx.values()) 206*f6aab3d8Srobert 207*f6aab3d8Srobert def get_extended_info(self): 208*f6aab3d8Srobert if (self.has_crashed): 209*f6aab3d8Srobert self.extended_info = self.scripted_process.extended_thread_info 210*f6aab3d8Srobert return self.extended_info 211*f6aab3d8Srobert 212