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