Files
clang-p2996/lldb/examples/python/scripted_process/crashlog_scripted_process.py
Med Ismail Bennani 12301d616f [lldb/crashlog] Parse thread fields and pass it to crashlog scripted process
Previously, the ScriptedThread used the thread index as the thread id.

This patch parses the crashlog json to extract the actual thread "id" value,
and passes this information to the Crashlog ScriptedProcess blueprint,
to create a higher fidelity ScriptedThreaad.

It also updates the blueprint to show the thread name and thread queue.

Finally, this patch updates the interactive crashlog test to reflect
these changes.

rdar://90327854

Differential Revision: https://reviews.llvm.org/D122422

Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2022-03-25 14:59:50 -07:00

160 lines
5.6 KiB
Python

import os,json,struct,signal
from typing import Any, Dict
import lldb
from lldb.plugins.scripted_process import ScriptedProcess
from lldb.plugins.scripted_process import ScriptedThread
from lldb.macosx.crashlog import CrashLog,CrashLogParser
class CrashLogScriptedProcess(ScriptedProcess):
def parse_crashlog(self):
try:
crash_log = CrashLogParser().parse(self.dbg, self.crashlog_path, False)
except Exception as e:
return
self.pid = crash_log.process_id
self.crashed_thread_idx = crash_log.crashed_thread_idx
self.loaded_images = []
def load_images(self, images):
#TODO: Add to self.loaded_images and load images in lldb
if images:
for image in images:
if image not in self.loaded_images:
err = image.add_module(self.target)
if err:
print(err)
else:
self.loaded_images.append(image)
for thread in crash_log.threads:
if self.load_all_images:
load_images(self, crash_log.images)
elif thread.did_crash():
for ident in thread.idents:
load_images(self, crash_log.find_images_with_identifier(ident))
self.threads[thread.index] = CrashLogScriptedThread(self, None, thread)
def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
super().__init__(target, args)
if not self.target or not self.target.IsValid():
return
self.crashlog_path = None
crashlog_path = args.GetValueForKey("crashlog_path")
if crashlog_path and crashlog_path.IsValid():
if crashlog_path.GetType() == lldb.eStructuredDataTypeString:
self.crashlog_path = crashlog_path.GetStringValue(4096)
if not self.crashlog_path:
return
load_all_images = args.GetValueForKey("load_all_images")
if load_all_images and load_all_images.IsValid():
if load_all_images.GetType() == lldb.eStructuredDataTypeBoolean:
self.load_all_images = load_all_images.GetBooleanValue()
if not self.load_all_images:
self.load_all_images = False
self.pid = super().get_process_id()
self.crashed_thread_idx = 0
self.parse_crashlog()
def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo:
return None
def get_thread_with_id(self, tid: int):
return {}
def get_registers_for_thread(self, tid: int):
return {}
def read_memory_at_address(self, addr: int, size: int) -> lldb.SBData:
# NOTE: CrashLogs don't contain any memory.
return lldb.SBData()
def get_loaded_images(self):
# TODO: Iterate over corefile_target modules and build a data structure
# from it.
return self.loaded_images
def get_process_id(self) -> int:
return self.pid
def should_stop(self) -> bool:
return True
def is_alive(self) -> bool:
return True
def get_scripted_thread_plugin(self):
return CrashLogScriptedThread.__module__ + "." + CrashLogScriptedThread.__name__
class CrashLogScriptedThread(ScriptedThread):
def create_register_ctx(self):
if not self.has_crashed:
return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0)
if not self.backing_thread or not len(self.backing_thread.registers):
return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0)
for reg in self.register_info['registers']:
reg_name = reg['name']
if reg_name in self.backing_thread.registers:
self.register_ctx[reg_name] = self.backing_thread.registers[reg_name]
else:
self.register_ctx[reg_name] = 0
return self.register_ctx
def create_stackframes(self):
if not (self.scripted_process.load_all_images or self.has_crashed):
return None
if not self.backing_thread or not len(self.backing_thread.frames):
return None
for frame in self.backing_thread.frames:
sym_addr = lldb.SBAddress()
sym_addr.SetLoadAddress(frame.pc, self.target)
if not sym_addr.IsValid():
continue
self.frames.append({"idx": frame.index, "pc": frame.pc})
return self.frames
def __init__(self, process, args, crashlog_thread):
super().__init__(process, args)
self.backing_thread = crashlog_thread
self.idx = self.backing_thread.index
self.tid = self.backing_thread.id
self.name = self.backing_thread.name
self.queue = self.backing_thread.queue
self.has_crashed = (self.scripted_process.crashed_thread_idx == self.idx)
self.create_stackframes()
def get_state(self):
if not self.has_crashed:
return lldb.eStateStopped
return lldb.eStateCrashed
def get_stop_reason(self) -> Dict[str, Any]:
if not self.has_crashed:
return { "type": lldb.eStopReasonNone, "data": { }}
# TODO: Investigate what stop reason should be reported when crashed
return { "type": lldb.eStopReasonException, "data": { "desc": "EXC_BAD_ACCESS" }}
def get_register_context(self) -> str:
if not self.register_ctx:
self.register_ctx = self.create_register_ctx()
return struct.pack("{}Q".format(len(self.register_ctx)), *self.register_ctx.values())