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