1*7c54ffdcSMed Ismail Bennaniimport os,json,struct,signal 2*7c54ffdcSMed Ismail Bennani 3*7c54ffdcSMed Ismail Bennanifrom typing import Any, Dict 4*7c54ffdcSMed Ismail Bennani 5*7c54ffdcSMed Ismail Bennaniimport lldb 6*7c54ffdcSMed Ismail Bennanifrom lldb.plugins.scripted_process import ScriptedProcess 7*7c54ffdcSMed Ismail Bennanifrom lldb.plugins.scripted_process import ScriptedThread 8*7c54ffdcSMed Ismail Bennani 9*7c54ffdcSMed Ismail Bennanifrom lldb.macosx.crashlog import CrashLog,CrashLogParser 10*7c54ffdcSMed Ismail Bennani 11*7c54ffdcSMed Ismail Bennaniclass CrashLogScriptedProcess(ScriptedProcess): 12*7c54ffdcSMed Ismail Bennani def parse_crashlog(self): 13*7c54ffdcSMed Ismail Bennani try: 14*7c54ffdcSMed Ismail Bennani crash_log = CrashLogParser().parse(self.dbg, self.crashlog_path, False) 15*7c54ffdcSMed Ismail Bennani except Exception as e: 16*7c54ffdcSMed Ismail Bennani return 17*7c54ffdcSMed Ismail Bennani 18*7c54ffdcSMed Ismail Bennani self.pid = crash_log.process_id 19*7c54ffdcSMed Ismail Bennani self.crashed_thread_idx = crash_log.crashed_thread_idx 20*7c54ffdcSMed Ismail Bennani self.loaded_images = [] 21*7c54ffdcSMed Ismail Bennani 22*7c54ffdcSMed Ismail Bennani for thread in crash_log.threads: 23*7c54ffdcSMed Ismail Bennani if thread.did_crash(): 24*7c54ffdcSMed Ismail Bennani for ident in thread.idents: 25*7c54ffdcSMed Ismail Bennani images = crash_log.find_images_with_identifier(ident) 26*7c54ffdcSMed Ismail Bennani if images: 27*7c54ffdcSMed Ismail Bennani for image in images: 28*7c54ffdcSMed Ismail Bennani #TODO: Add to self.loaded_images and load images in lldb 29*7c54ffdcSMed Ismail Bennani err = image.add_module(self.target) 30*7c54ffdcSMed Ismail Bennani if err: 31*7c54ffdcSMed Ismail Bennani print(err) 32*7c54ffdcSMed Ismail Bennani else: 33*7c54ffdcSMed Ismail Bennani self.loaded_images.append(image) 34*7c54ffdcSMed Ismail Bennani self.threads[thread.index] = CrashLogScriptedThread(self, None, thread) 35*7c54ffdcSMed Ismail Bennani 36*7c54ffdcSMed Ismail Bennani def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData): 37*7c54ffdcSMed Ismail Bennani super().__init__(target, args) 38*7c54ffdcSMed Ismail Bennani 39*7c54ffdcSMed Ismail Bennani if not self.target or not self.target.IsValid(): 40*7c54ffdcSMed Ismail Bennani return 41*7c54ffdcSMed Ismail Bennani 42*7c54ffdcSMed Ismail Bennani self.crashlog_path = None 43*7c54ffdcSMed Ismail Bennani 44*7c54ffdcSMed Ismail Bennani crashlog_path = args.GetValueForKey("crashlog_path") 45*7c54ffdcSMed Ismail Bennani if crashlog_path and crashlog_path.IsValid(): 46*7c54ffdcSMed Ismail Bennani if crashlog_path.GetType() == lldb.eStructuredDataTypeString: 47*7c54ffdcSMed Ismail Bennani self.crashlog_path = crashlog_path.GetStringValue(4096) 48*7c54ffdcSMed Ismail Bennani 49*7c54ffdcSMed Ismail Bennani if not self.crashlog_path: 50*7c54ffdcSMed Ismail Bennani return 51*7c54ffdcSMed Ismail Bennani 52*7c54ffdcSMed Ismail Bennani self.pid = super().get_process_id() 53*7c54ffdcSMed Ismail Bennani self.crashed_thread_idx = 0 54*7c54ffdcSMed Ismail Bennani self.parse_crashlog() 55*7c54ffdcSMed Ismail Bennani 56*7c54ffdcSMed Ismail Bennani def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo: 57*7c54ffdcSMed Ismail Bennani return None 58*7c54ffdcSMed Ismail Bennani 59*7c54ffdcSMed Ismail Bennani def get_thread_with_id(self, tid: int): 60*7c54ffdcSMed Ismail Bennani return {} 61*7c54ffdcSMed Ismail Bennani 62*7c54ffdcSMed Ismail Bennani def get_registers_for_thread(self, tid: int): 63*7c54ffdcSMed Ismail Bennani return {} 64*7c54ffdcSMed Ismail Bennani 65*7c54ffdcSMed Ismail Bennani def read_memory_at_address(self, addr: int, size: int) -> lldb.SBData: 66*7c54ffdcSMed Ismail Bennani # NOTE: CrashLogs don't contain any memory. 67*7c54ffdcSMed Ismail Bennani return lldb.SBData() 68*7c54ffdcSMed Ismail Bennani 69*7c54ffdcSMed Ismail Bennani def get_loaded_images(self): 70*7c54ffdcSMed Ismail Bennani # TODO: Iterate over corefile_target modules and build a data structure 71*7c54ffdcSMed Ismail Bennani # from it. 72*7c54ffdcSMed Ismail Bennani return self.loaded_images 73*7c54ffdcSMed Ismail Bennani 74*7c54ffdcSMed Ismail Bennani def get_process_id(self) -> int: 75*7c54ffdcSMed Ismail Bennani return self.pid 76*7c54ffdcSMed Ismail Bennani 77*7c54ffdcSMed Ismail Bennani def should_stop(self) -> bool: 78*7c54ffdcSMed Ismail Bennani return True 79*7c54ffdcSMed Ismail Bennani 80*7c54ffdcSMed Ismail Bennani def is_alive(self) -> bool: 81*7c54ffdcSMed Ismail Bennani return True 82*7c54ffdcSMed Ismail Bennani 83*7c54ffdcSMed Ismail Bennani def get_scripted_thread_plugin(self): 84*7c54ffdcSMed Ismail Bennani return CrashLogScriptedThread.__module__ + "." + CrashLogScriptedThread.__name__ 85*7c54ffdcSMed Ismail Bennani 86*7c54ffdcSMed Ismail Bennaniclass CrashLogScriptedThread(ScriptedThread): 87*7c54ffdcSMed Ismail Bennani def create_register_ctx(self): 88*7c54ffdcSMed Ismail Bennani if not self.has_crashed: 89*7c54ffdcSMed Ismail Bennani return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0) 90*7c54ffdcSMed Ismail Bennani 91*7c54ffdcSMed Ismail Bennani if not self.backing_thread or not len(self.backing_thread.registers): 92*7c54ffdcSMed Ismail Bennani return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0) 93*7c54ffdcSMed Ismail Bennani 94*7c54ffdcSMed Ismail Bennani for reg in self.register_info['registers']: 95*7c54ffdcSMed Ismail Bennani reg_name = reg['name'] 96*7c54ffdcSMed Ismail Bennani if reg_name in self.backing_thread.registers: 97*7c54ffdcSMed Ismail Bennani self.register_ctx[reg_name] = self.backing_thread.registers[reg_name] 98*7c54ffdcSMed Ismail Bennani else: 99*7c54ffdcSMed Ismail Bennani self.register_ctx[reg_name] = 0 100*7c54ffdcSMed Ismail Bennani 101*7c54ffdcSMed Ismail Bennani return self.register_ctx 102*7c54ffdcSMed Ismail Bennani 103*7c54ffdcSMed Ismail Bennani def create_stackframes(self): 104*7c54ffdcSMed Ismail Bennani if not self.has_crashed: 105*7c54ffdcSMed Ismail Bennani return None 106*7c54ffdcSMed Ismail Bennani 107*7c54ffdcSMed Ismail Bennani if not self.backing_thread or not len(self.backing_thread.frames): 108*7c54ffdcSMed Ismail Bennani return None 109*7c54ffdcSMed Ismail Bennani 110*7c54ffdcSMed Ismail Bennani for frame in self.backing_thread.frames: 111*7c54ffdcSMed Ismail Bennani sym_addr = lldb.SBAddress() 112*7c54ffdcSMed Ismail Bennani sym_addr.SetLoadAddress(frame.pc, self.target) 113*7c54ffdcSMed Ismail Bennani if not sym_addr.IsValid(): 114*7c54ffdcSMed Ismail Bennani continue 115*7c54ffdcSMed Ismail Bennani self.frames.append({"idx": frame.index, "pc": frame.pc}) 116*7c54ffdcSMed Ismail Bennani 117*7c54ffdcSMed Ismail Bennani return self.frames 118*7c54ffdcSMed Ismail Bennani 119*7c54ffdcSMed Ismail Bennani def __init__(self, process, args, crashlog_thread): 120*7c54ffdcSMed Ismail Bennani super().__init__(process, args) 121*7c54ffdcSMed Ismail Bennani 122*7c54ffdcSMed Ismail Bennani self.backing_thread = crashlog_thread 123*7c54ffdcSMed Ismail Bennani self.idx = self.backing_thread.index 124*7c54ffdcSMed Ismail Bennani self.has_crashed = (self.scripted_process.crashed_thread_idx == self.idx) 125*7c54ffdcSMed Ismail Bennani self.create_stackframes() 126*7c54ffdcSMed Ismail Bennani 127*7c54ffdcSMed Ismail Bennani def get_thread_id(self) -> int: 128*7c54ffdcSMed Ismail Bennani return self.idx 129*7c54ffdcSMed Ismail Bennani 130*7c54ffdcSMed Ismail Bennani def get_name(self) -> str: 131*7c54ffdcSMed Ismail Bennani return CrashLogScriptedThread.__name__ + ".thread-" + str(self.idx) 132*7c54ffdcSMed Ismail Bennani 133*7c54ffdcSMed Ismail Bennani def get_state(self): 134*7c54ffdcSMed Ismail Bennani if not self.has_crashed: 135*7c54ffdcSMed Ismail Bennani return lldb.eStateStopped 136*7c54ffdcSMed Ismail Bennani return lldb.eStateCrashed 137*7c54ffdcSMed Ismail Bennani 138*7c54ffdcSMed Ismail Bennani def get_stop_reason(self) -> Dict[str, Any]: 139*7c54ffdcSMed Ismail Bennani if not self.has_crashed: 140*7c54ffdcSMed Ismail Bennani return { "type": lldb.eStopReasonNone, "data": { }} 141*7c54ffdcSMed Ismail Bennani # TODO: Investigate what stop reason should be reported when crashed 142*7c54ffdcSMed Ismail Bennani return { "type": lldb.eStopReasonException, "data": { "desc": "EXC_BAD_ACCESS" }} 143*7c54ffdcSMed Ismail Bennani 144*7c54ffdcSMed Ismail Bennani def get_register_context(self) -> str: 145*7c54ffdcSMed Ismail Bennani if not self.register_ctx: 146*7c54ffdcSMed Ismail Bennani self.register_ctx = self.create_register_ctx() 147*7c54ffdcSMed Ismail Bennani 148*7c54ffdcSMed Ismail Bennani return struct.pack("{}Q".format(len(self.register_ctx)), *self.register_ctx.values()) 149