brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.0 KiB · 6c6eec8 Raw
217 lines · python
1import os, json, struct, signal, uuid, tempfile2 3from typing import Any, Dict4 5import lldb6from lldb.plugins.scripted_process import ScriptedProcess7from lldb.plugins.scripted_process import ScriptedThread8 9from lldb.macosx.crashlog import CrashLog, CrashLogParser10 11 12class CrashLogScriptedProcess(ScriptedProcess):13    def parse_crashlog(self):14        if self.crashlog.process_id:15            if type(self.crashlog.process_id) is int:16                self.pid = self.crashlog.process_id17            elif type(self.crashlog.process_id) is str:18                self.pid = int(self.crashlog.process_id, 0)19            else:20                self.pid = super().get_process_id()21        self.addr_mask = self.crashlog.addr_mask22        self.crashed_thread_idx = self.crashlog.crashed_thread_idx23        self.loaded_images = []24        self.exception = self.crashlog.exception25        self.app_specific_thread = None26        if hasattr(self.crashlog, "asi"):27            self.metadata["asi"] = self.crashlog.asi28        if hasattr(self.crashlog, "asb"):29            self.extended_thread_info = self.crashlog.asb30 31        for thread in self.crashlog.threads:32            if (33                hasattr(thread, "app_specific_backtrace")34                and thread.app_specific_backtrace35            ):36                # We don't want to include the Application Specific Backtrace37                # Thread into the Scripted Process' Thread list.38                # Instead, we will try to extract the stackframe pcs from the39                # backtrace and inject that as the extended thread info.40                self.app_specific_thread = thread41                continue42 43            self.threads[thread.index] = CrashLogScriptedThread(self, None, thread)44 45        if self.app_specific_thread:46            self.extended_thread_info = CrashLogScriptedThread.resolve_stackframes(47                self.app_specific_thread, self.addr_mask, self.target48            )49 50    class CrashLogOptions:51        load_all_images = False52        crashed_only = True53        no_parallel_image_loading = False54 55    def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData):56        super().__init__(exe_ctx, args)57 58        if not self.target or not self.target.IsValid():59            # Return error60            return61 62        self.crashlog_path = None63 64        crashlog_path = args.GetValueForKey("file_path")65        if crashlog_path and crashlog_path.IsValid():66            if crashlog_path.GetType() == lldb.eStructuredDataTypeString:67                self.crashlog_path = crashlog_path.GetStringValue(4096)68 69        if not self.crashlog_path:70            # Return error71            return72 73        self.options = self.CrashLogOptions()74 75        load_all_images = args.GetValueForKey("load_all_images")76        if load_all_images and load_all_images.IsValid():77            if load_all_images.GetType() == lldb.eStructuredDataTypeBoolean:78                self.options.load_all_images = load_all_images.GetBooleanValue()79 80        crashed_only = args.GetValueForKey("crashed_only")81        if crashed_only and crashed_only.IsValid():82            if crashed_only.GetType() == lldb.eStructuredDataTypeBoolean:83                self.options.crashed_only = crashed_only.GetBooleanValue()84 85        no_parallel_image_loading = args.GetValueForKey("no_parallel_image_loading")86        if no_parallel_image_loading and no_parallel_image_loading.IsValid():87            if no_parallel_image_loading.GetType() == lldb.eStructuredDataTypeBoolean:88                self.options.no_parallel_image_loading = (89                    no_parallel_image_loading.GetBooleanValue()90                )91 92        self.crashlog = None93        crashlog = args.GetValueForKey("crashlog")94        if crashlog and crashlog.IsValid():95            if crashlog.GetType() == lldb.eStructuredDataTypeGeneric:96                self.crashlog = crashlog.GetGenericValue()97 98        if not self.crashlog:99            # Return error100            return101 102        self.pid = super().get_process_id()103        self.crashed_thread_idx = 0104        self.exception = None105        self.extended_thread_info = None106        self.parse_crashlog()107 108    def read_memory_at_address(109        self, addr: int, size: int, error: lldb.SBError110    ) -> lldb.SBData:111        # NOTE: CrashLogs don't contain any memory.112        return lldb.SBData()113 114    def get_loaded_images(self):115        if len(self.loaded_images) == 0:116            self.crashlog.load_images(self.options, self.loaded_images)117        return self.loaded_images118 119    def should_stop(self) -> bool:120        return True121 122    def is_alive(self) -> bool:123        return True124 125    def get_scripted_thread_plugin(self):126        return CrashLogScriptedThread.__module__ + "." + CrashLogScriptedThread.__name__127 128    def get_process_metadata(self):129        return self.metadata130 131 132class CrashLogScriptedThread(ScriptedThread):133    def create_register_ctx(self):134        if not self.backing_thread or not len(self.backing_thread.registers):135            return dict.fromkeys(136                [*map(lambda reg: reg["name"], self.register_info["registers"])], 0137            )138 139        for reg in self.register_info["registers"]:140            reg_name = reg["name"]141            reg_alt_name = None142            if "alt-name" in reg:143                reg_alt_name = reg["alt-name"]144            if reg_name in self.backing_thread.registers:145                self.register_ctx[reg_name] = self.backing_thread.registers[reg_name]146            elif reg_alt_name and reg_alt_name in self.backing_thread.registers:147                self.register_ctx[reg_name] = self.backing_thread.registers[148                    reg_alt_name149                ]150            else:151                self.register_ctx[reg_name] = 0152 153        return self.register_ctx154 155    def resolve_stackframes(thread, addr_mask, target):156        frames = []157        for frame in thread.frames:158            frame_pc = frame.pc & addr_mask159            pc = frame_pc if frame.index == 0 or frame_pc == 0 else frame_pc - 1160            sym_addr = lldb.SBAddress()161            sym_addr.SetLoadAddress(pc, target)162            if not sym_addr.IsValid():163                continue164            frames.append({"idx": frame.index, "pc": pc})165        return frames166 167    def create_stackframes(self):168        if not (self.originating_process.options.load_all_images or self.has_crashed):169            return None170 171        if not self.backing_thread or not len(self.backing_thread.frames):172            return None173 174        self.frames = CrashLogScriptedThread.resolve_stackframes(175            self.backing_thread, self.originating_process.addr_mask, self.target176        )177 178        return self.frames179 180    def __init__(self, process, args, crashlog_thread):181        super().__init__(process, args)182 183        self.backing_thread = crashlog_thread184        self.idx = self.backing_thread.index185        self.tid = self.backing_thread.id186        self.name = self.backing_thread.name187        self.queue = self.backing_thread.queue188        self.has_crashed = self.originating_process.crashed_thread_idx == self.idx189        self.create_stackframes()190 191    def get_state(self):192        if not self.has_crashed:193            return lldb.eStateStopped194        return lldb.eStateCrashed195 196    def get_stop_reason(self) -> Dict[str, Any]:197        if not self.has_crashed:198            return {"type": lldb.eStopReasonNone}199        # TODO: Investigate what stop reason should be reported when crashed200        stop_reason = {"type": lldb.eStopReasonException, "data": {}}201        if self.originating_process.exception:202            stop_reason["data"]["mach_exception"] = self.originating_process.exception203        return stop_reason204 205    def get_register_context(self) -> str:206        if not self.register_ctx:207            self.register_ctx = self.create_register_ctx()208 209        return struct.pack(210            "{}Q".format(len(self.register_ctx)), *self.register_ctx.values()211        )212 213    def get_extended_info(self):214        if self.has_crashed:215            self.extended_info = self.originating_process.extended_thread_info216        return self.extended_info217