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
19*3e54ea0cSMed Ismail Bennani        self.addr_mask = crash_log.addr_mask
207c54ffdcSMed Ismail Bennani        self.crashed_thread_idx = crash_log.crashed_thread_idx
217c54ffdcSMed Ismail Bennani        self.loaded_images = []
227c54ffdcSMed Ismail Bennani
230a65112cSMed Ismail Bennani        def load_images(self, images):
240a65112cSMed Ismail Bennani            #TODO: Add to self.loaded_images and load images in lldb
257c54ffdcSMed Ismail Bennani            if images:
267c54ffdcSMed Ismail Bennani                for image in images:
270a65112cSMed Ismail Bennani                    if image not in self.loaded_images:
287c54ffdcSMed Ismail Bennani                        err = image.add_module(self.target)
297c54ffdcSMed Ismail Bennani                        if err:
307c54ffdcSMed Ismail Bennani                            print(err)
317c54ffdcSMed Ismail Bennani                        else:
327c54ffdcSMed Ismail Bennani                            self.loaded_images.append(image)
330a65112cSMed Ismail Bennani
340a65112cSMed Ismail Bennani        for thread in crash_log.threads:
350a65112cSMed Ismail Bennani            if self.load_all_images:
360a65112cSMed Ismail Bennani                load_images(self, crash_log.images)
370a65112cSMed Ismail Bennani            elif thread.did_crash():
380a65112cSMed Ismail Bennani                for ident in thread.idents:
390a65112cSMed Ismail Bennani                    load_images(self, crash_log.find_images_with_identifier(ident))
400a65112cSMed Ismail Bennani
417c54ffdcSMed Ismail Bennani            self.threads[thread.index] = CrashLogScriptedThread(self, None, thread)
427c54ffdcSMed Ismail Bennani
437c54ffdcSMed Ismail Bennani    def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
447c54ffdcSMed Ismail Bennani        super().__init__(target, args)
457c54ffdcSMed Ismail Bennani
467c54ffdcSMed Ismail Bennani        if not self.target or not self.target.IsValid():
477c54ffdcSMed Ismail Bennani            return
487c54ffdcSMed Ismail Bennani
497c54ffdcSMed Ismail Bennani        self.crashlog_path = None
507c54ffdcSMed Ismail Bennani
517c54ffdcSMed Ismail Bennani        crashlog_path = args.GetValueForKey("crashlog_path")
527c54ffdcSMed Ismail Bennani        if crashlog_path and crashlog_path.IsValid():
537c54ffdcSMed Ismail Bennani            if crashlog_path.GetType() == lldb.eStructuredDataTypeString:
547c54ffdcSMed Ismail Bennani                self.crashlog_path = crashlog_path.GetStringValue(4096)
557c54ffdcSMed Ismail Bennani
567c54ffdcSMed Ismail Bennani        if not self.crashlog_path:
577c54ffdcSMed Ismail Bennani            return
587c54ffdcSMed Ismail Bennani
590a65112cSMed Ismail Bennani        load_all_images = args.GetValueForKey("load_all_images")
600a65112cSMed Ismail Bennani        if load_all_images and load_all_images.IsValid():
610a65112cSMed Ismail Bennani            if load_all_images.GetType() == lldb.eStructuredDataTypeBoolean:
620a65112cSMed Ismail Bennani                self.load_all_images = load_all_images.GetBooleanValue()
630a65112cSMed Ismail Bennani
640a65112cSMed Ismail Bennani        if not self.load_all_images:
650a65112cSMed Ismail Bennani            self.load_all_images = False
660a65112cSMed Ismail Bennani
677c54ffdcSMed Ismail Bennani        self.pid = super().get_process_id()
687c54ffdcSMed Ismail Bennani        self.crashed_thread_idx = 0
697c54ffdcSMed Ismail Bennani        self.parse_crashlog()
707c54ffdcSMed Ismail Bennani
717c54ffdcSMed Ismail Bennani    def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo:
727c54ffdcSMed Ismail Bennani        return None
737c54ffdcSMed Ismail Bennani
747c54ffdcSMed Ismail Bennani    def get_thread_with_id(self, tid: int):
757c54ffdcSMed Ismail Bennani        return {}
767c54ffdcSMed Ismail Bennani
777c54ffdcSMed Ismail Bennani    def get_registers_for_thread(self, tid: int):
787c54ffdcSMed Ismail Bennani        return {}
797c54ffdcSMed Ismail Bennani
807c54ffdcSMed Ismail Bennani    def read_memory_at_address(self, addr: int, size: int) -> lldb.SBData:
817c54ffdcSMed Ismail Bennani        # NOTE: CrashLogs don't contain any memory.
827c54ffdcSMed Ismail Bennani        return lldb.SBData()
837c54ffdcSMed Ismail Bennani
847c54ffdcSMed Ismail Bennani    def get_loaded_images(self):
857c54ffdcSMed Ismail Bennani        # TODO: Iterate over corefile_target modules and build a data structure
867c54ffdcSMed Ismail Bennani        # from it.
877c54ffdcSMed Ismail Bennani        return self.loaded_images
887c54ffdcSMed Ismail Bennani
897c54ffdcSMed Ismail Bennani    def get_process_id(self) -> int:
907c54ffdcSMed Ismail Bennani        return self.pid
917c54ffdcSMed Ismail Bennani
927c54ffdcSMed Ismail Bennani    def should_stop(self) -> bool:
937c54ffdcSMed Ismail Bennani        return True
947c54ffdcSMed Ismail Bennani
957c54ffdcSMed Ismail Bennani    def is_alive(self) -> bool:
967c54ffdcSMed Ismail Bennani        return True
977c54ffdcSMed Ismail Bennani
987c54ffdcSMed Ismail Bennani    def get_scripted_thread_plugin(self):
997c54ffdcSMed Ismail Bennani        return CrashLogScriptedThread.__module__ + "." + CrashLogScriptedThread.__name__
1007c54ffdcSMed Ismail Bennani
1017c54ffdcSMed Ismail Bennaniclass CrashLogScriptedThread(ScriptedThread):
1027c54ffdcSMed Ismail Bennani    def create_register_ctx(self):
1037c54ffdcSMed Ismail Bennani        if not self.has_crashed:
1047c54ffdcSMed Ismail Bennani            return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0)
1057c54ffdcSMed Ismail Bennani
1067c54ffdcSMed Ismail Bennani        if not self.backing_thread or not len(self.backing_thread.registers):
1077c54ffdcSMed Ismail Bennani            return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0)
1087c54ffdcSMed Ismail Bennani
1097c54ffdcSMed Ismail Bennani        for reg in self.register_info['registers']:
1107c54ffdcSMed Ismail Bennani            reg_name = reg['name']
1117c54ffdcSMed Ismail Bennani            if reg_name in self.backing_thread.registers:
1127c54ffdcSMed Ismail Bennani                self.register_ctx[reg_name] = self.backing_thread.registers[reg_name]
1137c54ffdcSMed Ismail Bennani            else:
1147c54ffdcSMed Ismail Bennani                self.register_ctx[reg_name] = 0
1157c54ffdcSMed Ismail Bennani
1167c54ffdcSMed Ismail Bennani        return self.register_ctx
1177c54ffdcSMed Ismail Bennani
1187c54ffdcSMed Ismail Bennani    def create_stackframes(self):
1190a65112cSMed Ismail Bennani        if not (self.scripted_process.load_all_images or self.has_crashed):
1207c54ffdcSMed Ismail Bennani            return None
1217c54ffdcSMed Ismail Bennani
1227c54ffdcSMed Ismail Bennani        if not self.backing_thread or not len(self.backing_thread.frames):
1237c54ffdcSMed Ismail Bennani            return None
1247c54ffdcSMed Ismail Bennani
1257c54ffdcSMed Ismail Bennani        for frame in self.backing_thread.frames:
126*3e54ea0cSMed Ismail Bennani            frame_pc = frame.pc & self.scripted_process.addr_mask
127*3e54ea0cSMed Ismail Bennani            pc = frame_pc if frame.index == 0  or frame_pc == 0 else frame_pc - 1
1287c54ffdcSMed Ismail Bennani            sym_addr = lldb.SBAddress()
129*3e54ea0cSMed Ismail Bennani            sym_addr.SetLoadAddress(pc, self.target)
1307c54ffdcSMed Ismail Bennani            if not sym_addr.IsValid():
1317c54ffdcSMed Ismail Bennani                continue
132*3e54ea0cSMed Ismail Bennani            self.frames.append({"idx": frame.index, "pc": pc})
1337c54ffdcSMed Ismail Bennani
1347c54ffdcSMed Ismail Bennani        return self.frames
1357c54ffdcSMed Ismail Bennani
1367c54ffdcSMed Ismail Bennani    def __init__(self, process, args, crashlog_thread):
1377c54ffdcSMed Ismail Bennani        super().__init__(process, args)
1387c54ffdcSMed Ismail Bennani
1397c54ffdcSMed Ismail Bennani        self.backing_thread = crashlog_thread
1407c54ffdcSMed Ismail Bennani        self.idx = self.backing_thread.index
14112301d61SMed Ismail Bennani        self.tid = self.backing_thread.id
14212301d61SMed Ismail Bennani        self.name = self.backing_thread.name
14312301d61SMed Ismail Bennani        self.queue = self.backing_thread.queue
1447c54ffdcSMed Ismail Bennani        self.has_crashed = (self.scripted_process.crashed_thread_idx == self.idx)
1457c54ffdcSMed Ismail Bennani        self.create_stackframes()
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