17c54ffdcSMed Ismail Bennaniimport os,json,struct,signal 27c54ffdcSMed Ismail Bennani 37c54ffdcSMed Ismail Bennanifrom typing import Any, Dict 47c54ffdcSMed Ismail Bennani 57c54ffdcSMed Ismail Bennaniimport lldb 67c54ffdcSMed Ismail Bennanifrom lldb.plugins.scripted_process import ScriptedProcess 77c54ffdcSMed Ismail Bennanifrom lldb.plugins.scripted_process import ScriptedThread 87c54ffdcSMed Ismail Bennani 97c54ffdcSMed Ismail Bennanifrom lldb.macosx.crashlog import CrashLog,CrashLogParser 107c54ffdcSMed Ismail Bennani 117c54ffdcSMed Ismail Bennaniclass CrashLogScriptedProcess(ScriptedProcess): 127c54ffdcSMed Ismail Bennani def parse_crashlog(self): 137c54ffdcSMed Ismail Bennani try: 147c54ffdcSMed Ismail Bennani crash_log = CrashLogParser().parse(self.dbg, self.crashlog_path, False) 157c54ffdcSMed Ismail Bennani except Exception as e: 167c54ffdcSMed Ismail Bennani return 177c54ffdcSMed Ismail Bennani 187c54ffdcSMed Ismail Bennani self.pid = crash_log.process_id 197c54ffdcSMed Ismail Bennani self.crashed_thread_idx = crash_log.crashed_thread_idx 207c54ffdcSMed Ismail Bennani self.loaded_images = [] 217c54ffdcSMed Ismail Bennani 22*0a65112cSMed Ismail Bennani def load_images(self, images): 23*0a65112cSMed Ismail Bennani #TODO: Add to self.loaded_images and load images in lldb 247c54ffdcSMed Ismail Bennani if images: 257c54ffdcSMed Ismail Bennani for image in images: 26*0a65112cSMed Ismail Bennani if image not in self.loaded_images: 277c54ffdcSMed Ismail Bennani err = image.add_module(self.target) 287c54ffdcSMed Ismail Bennani if err: 297c54ffdcSMed Ismail Bennani print(err) 307c54ffdcSMed Ismail Bennani else: 317c54ffdcSMed Ismail Bennani self.loaded_images.append(image) 32*0a65112cSMed Ismail Bennani 33*0a65112cSMed Ismail Bennani for thread in crash_log.threads: 34*0a65112cSMed Ismail Bennani if self.load_all_images: 35*0a65112cSMed Ismail Bennani load_images(self, crash_log.images) 36*0a65112cSMed Ismail Bennani elif thread.did_crash(): 37*0a65112cSMed Ismail Bennani for ident in thread.idents: 38*0a65112cSMed Ismail Bennani load_images(self, crash_log.find_images_with_identifier(ident)) 39*0a65112cSMed Ismail Bennani 407c54ffdcSMed Ismail Bennani self.threads[thread.index] = CrashLogScriptedThread(self, None, thread) 417c54ffdcSMed Ismail Bennani 427c54ffdcSMed Ismail Bennani def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData): 437c54ffdcSMed Ismail Bennani super().__init__(target, args) 447c54ffdcSMed Ismail Bennani 457c54ffdcSMed Ismail Bennani if not self.target or not self.target.IsValid(): 467c54ffdcSMed Ismail Bennani return 477c54ffdcSMed Ismail Bennani 487c54ffdcSMed Ismail Bennani self.crashlog_path = None 497c54ffdcSMed Ismail Bennani 507c54ffdcSMed Ismail Bennani crashlog_path = args.GetValueForKey("crashlog_path") 517c54ffdcSMed Ismail Bennani if crashlog_path and crashlog_path.IsValid(): 527c54ffdcSMed Ismail Bennani if crashlog_path.GetType() == lldb.eStructuredDataTypeString: 537c54ffdcSMed Ismail Bennani self.crashlog_path = crashlog_path.GetStringValue(4096) 547c54ffdcSMed Ismail Bennani 557c54ffdcSMed Ismail Bennani if not self.crashlog_path: 567c54ffdcSMed Ismail Bennani return 577c54ffdcSMed Ismail Bennani 58*0a65112cSMed Ismail Bennani load_all_images = args.GetValueForKey("load_all_images") 59*0a65112cSMed Ismail Bennani if load_all_images and load_all_images.IsValid(): 60*0a65112cSMed Ismail Bennani if load_all_images.GetType() == lldb.eStructuredDataTypeBoolean: 61*0a65112cSMed Ismail Bennani self.load_all_images = load_all_images.GetBooleanValue() 62*0a65112cSMed Ismail Bennani 63*0a65112cSMed Ismail Bennani if not self.load_all_images: 64*0a65112cSMed Ismail Bennani self.load_all_images = False 65*0a65112cSMed Ismail Bennani 667c54ffdcSMed Ismail Bennani self.pid = super().get_process_id() 677c54ffdcSMed Ismail Bennani self.crashed_thread_idx = 0 687c54ffdcSMed Ismail Bennani self.parse_crashlog() 697c54ffdcSMed Ismail Bennani 707c54ffdcSMed Ismail Bennani def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo: 717c54ffdcSMed Ismail Bennani return None 727c54ffdcSMed Ismail Bennani 737c54ffdcSMed Ismail Bennani def get_thread_with_id(self, tid: int): 747c54ffdcSMed Ismail Bennani return {} 757c54ffdcSMed Ismail Bennani 767c54ffdcSMed Ismail Bennani def get_registers_for_thread(self, tid: int): 777c54ffdcSMed Ismail Bennani return {} 787c54ffdcSMed Ismail Bennani 797c54ffdcSMed Ismail Bennani def read_memory_at_address(self, addr: int, size: int) -> lldb.SBData: 807c54ffdcSMed Ismail Bennani # NOTE: CrashLogs don't contain any memory. 817c54ffdcSMed Ismail Bennani return lldb.SBData() 827c54ffdcSMed Ismail Bennani 837c54ffdcSMed Ismail Bennani def get_loaded_images(self): 847c54ffdcSMed Ismail Bennani # TODO: Iterate over corefile_target modules and build a data structure 857c54ffdcSMed Ismail Bennani # from it. 867c54ffdcSMed Ismail Bennani return self.loaded_images 877c54ffdcSMed Ismail Bennani 887c54ffdcSMed Ismail Bennani def get_process_id(self) -> int: 897c54ffdcSMed Ismail Bennani return self.pid 907c54ffdcSMed Ismail Bennani 917c54ffdcSMed Ismail Bennani def should_stop(self) -> bool: 927c54ffdcSMed Ismail Bennani return True 937c54ffdcSMed Ismail Bennani 947c54ffdcSMed Ismail Bennani def is_alive(self) -> bool: 957c54ffdcSMed Ismail Bennani return True 967c54ffdcSMed Ismail Bennani 977c54ffdcSMed Ismail Bennani def get_scripted_thread_plugin(self): 987c54ffdcSMed Ismail Bennani return CrashLogScriptedThread.__module__ + "." + CrashLogScriptedThread.__name__ 997c54ffdcSMed Ismail Bennani 1007c54ffdcSMed Ismail Bennaniclass CrashLogScriptedThread(ScriptedThread): 1017c54ffdcSMed Ismail Bennani def create_register_ctx(self): 1027c54ffdcSMed Ismail Bennani if not self.has_crashed: 1037c54ffdcSMed Ismail Bennani return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0) 1047c54ffdcSMed Ismail Bennani 1057c54ffdcSMed Ismail Bennani if not self.backing_thread or not len(self.backing_thread.registers): 1067c54ffdcSMed Ismail Bennani return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0) 1077c54ffdcSMed Ismail Bennani 1087c54ffdcSMed Ismail Bennani for reg in self.register_info['registers']: 1097c54ffdcSMed Ismail Bennani reg_name = reg['name'] 1107c54ffdcSMed Ismail Bennani if reg_name in self.backing_thread.registers: 1117c54ffdcSMed Ismail Bennani self.register_ctx[reg_name] = self.backing_thread.registers[reg_name] 1127c54ffdcSMed Ismail Bennani else: 1137c54ffdcSMed Ismail Bennani self.register_ctx[reg_name] = 0 1147c54ffdcSMed Ismail Bennani 1157c54ffdcSMed Ismail Bennani return self.register_ctx 1167c54ffdcSMed Ismail Bennani 1177c54ffdcSMed Ismail Bennani def create_stackframes(self): 118*0a65112cSMed Ismail Bennani if not (self.scripted_process.load_all_images or self.has_crashed): 1197c54ffdcSMed Ismail Bennani return None 1207c54ffdcSMed Ismail Bennani 1217c54ffdcSMed Ismail Bennani if not self.backing_thread or not len(self.backing_thread.frames): 1227c54ffdcSMed Ismail Bennani return None 1237c54ffdcSMed Ismail Bennani 1247c54ffdcSMed Ismail Bennani for frame in self.backing_thread.frames: 1257c54ffdcSMed Ismail Bennani sym_addr = lldb.SBAddress() 1267c54ffdcSMed Ismail Bennani sym_addr.SetLoadAddress(frame.pc, self.target) 1277c54ffdcSMed Ismail Bennani if not sym_addr.IsValid(): 1287c54ffdcSMed Ismail Bennani continue 1297c54ffdcSMed Ismail Bennani self.frames.append({"idx": frame.index, "pc": frame.pc}) 1307c54ffdcSMed Ismail Bennani 1317c54ffdcSMed Ismail Bennani return self.frames 1327c54ffdcSMed Ismail Bennani 1337c54ffdcSMed Ismail Bennani def __init__(self, process, args, crashlog_thread): 1347c54ffdcSMed Ismail Bennani super().__init__(process, args) 1357c54ffdcSMed Ismail Bennani 1367c54ffdcSMed Ismail Bennani self.backing_thread = crashlog_thread 1377c54ffdcSMed Ismail Bennani self.idx = self.backing_thread.index 1387c54ffdcSMed Ismail Bennani self.has_crashed = (self.scripted_process.crashed_thread_idx == self.idx) 1397c54ffdcSMed Ismail Bennani self.create_stackframes() 1407c54ffdcSMed Ismail Bennani 1417c54ffdcSMed Ismail Bennani def get_thread_id(self) -> int: 1427c54ffdcSMed Ismail Bennani return self.idx 1437c54ffdcSMed Ismail Bennani 1447c54ffdcSMed Ismail Bennani def get_name(self) -> str: 1457c54ffdcSMed Ismail Bennani return CrashLogScriptedThread.__name__ + ".thread-" + str(self.idx) 1467c54ffdcSMed Ismail Bennani 1477c54ffdcSMed Ismail Bennani def get_state(self): 1487c54ffdcSMed Ismail Bennani if not self.has_crashed: 1497c54ffdcSMed Ismail Bennani return lldb.eStateStopped 1507c54ffdcSMed Ismail Bennani return lldb.eStateCrashed 1517c54ffdcSMed Ismail Bennani 1527c54ffdcSMed Ismail Bennani def get_stop_reason(self) -> Dict[str, Any]: 1537c54ffdcSMed Ismail Bennani if not self.has_crashed: 1547c54ffdcSMed Ismail Bennani return { "type": lldb.eStopReasonNone, "data": { }} 1557c54ffdcSMed Ismail Bennani # TODO: Investigate what stop reason should be reported when crashed 1567c54ffdcSMed Ismail Bennani return { "type": lldb.eStopReasonException, "data": { "desc": "EXC_BAD_ACCESS" }} 1577c54ffdcSMed Ismail Bennani 1587c54ffdcSMed Ismail Bennani def get_register_context(self) -> str: 1597c54ffdcSMed Ismail Bennani if not self.register_ctx: 1607c54ffdcSMed Ismail Bennani self.register_ctx = self.create_register_ctx() 1617c54ffdcSMed Ismail Bennani 1627c54ffdcSMed Ismail Bennani return struct.pack("{}Q".format(len(self.register_ctx)), *self.register_ctx.values()) 163