Skip to content

[lldb/crashlog] Load inlined symbol into crashlog scripted process (+ code reformatting) #7012

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 116 additions & 55 deletions lldb/examples/python/crashlog.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,22 +681,46 @@ def parse_asi_backtrace(self, thread, bt):
print("error: can't parse application specific backtrace.")
return False

frame_id = frame_img_name = frame_addr = frame_symbol = frame_offset = frame_file = frame_line = frame_column = None
frame_id = (
frame_img_name
) = (
frame_addr
) = (
frame_symbol
) = frame_offset = frame_file = frame_line = frame_column = None

if len(frame_match.groups()) == 3:
# Get the image UUID from the frame image name.
(frame_id, frame_img_name, frame_addr) = frame_match.groups()
elif len(frame_match.groups()) == 5:
(frame_id, frame_img_name, frame_addr,
frame_symbol, frame_offset) = frame_match.groups()
(
frame_id,
frame_img_name,
frame_addr,
frame_symbol,
frame_offset,
) = frame_match.groups()
elif len(frame_match.groups()) == 7:
(frame_id, frame_img_name, frame_addr,
frame_symbol, frame_offset,
frame_file, frame_line) = frame_match.groups()
(
frame_id,
frame_img_name,
frame_addr,
frame_symbol,
frame_offset,
frame_file,
frame_line,
) = frame_match.groups()
elif len(frame_match.groups()) == 8:
(frame_id, frame_img_name, frame_addr,
frame_symbol, frame_offset,
frame_file, frame_line, frame_column) = frame_match.groups()
(
frame_id,
frame_img_name,
frame_addr,
frame_symbol,
frame_offset,
frame_file,
frame_line,
frame_column,
) = frame_match.groups()

thread.add_ident(frame_img_name)
if frame_img_name not in self.crashlog.idents:
Expand Down Expand Up @@ -769,24 +793,24 @@ class CrashLogParseMode:


class TextCrashLogParser(CrashLogParser):
parent_process_regex = re.compile(r'^Parent Process:\s*(.*)\[(\d+)\]')
thread_state_regex = re.compile(r'^Thread \d+ crashed with')
thread_instrs_regex = re.compile(r'^Thread \d+ instruction stream')
thread_regex = re.compile(r'^Thread (\d+).*:')
app_backtrace_regex = re.compile(r'^Application Specific Backtrace (\d+).*:')
parent_process_regex = re.compile(r"^Parent Process:\s*(.*)\[(\d+)\]")
thread_state_regex = re.compile(r"^Thread \d+ crashed with")
thread_instrs_regex = re.compile(r"^Thread \d+ instruction stream")
thread_regex = re.compile(r"^Thread (\d+).*:")
app_backtrace_regex = re.compile(r"^Application Specific Backtrace (\d+).*:")

class VersionRegex:
version = r'\(.+\)|(?:arm|x86_)[0-9a-z]+'
version = r"\(.+\)|(?:arm|x86_)[0-9a-z]+"

class FrameRegex(VersionRegex):
@classmethod
def get(cls):
index = r'^(\d+)\s+'
img_name = r'(.+?)\s+'
version = r'(?:' + super().version + r'\s+)?'
address = r'(0x[0-9a-fA-F]{4,})' # 4 digits or more
index = r"^(\d+)\s+"
img_name = r"(.+?)\s+"
version = r"(?:" + super().version + r"\s+)?"
address = r"(0x[0-9a-fA-F]{4,})" # 4 digits or more

symbol = """
symbol = """
(?:
[ ]+
(?P<symbol>.+)
Expand All @@ -804,24 +828,28 @@ def get(cls):
)?
"""

return re.compile(index + img_name + version + address + symbol,
flags=re.VERBOSE)
return re.compile(
index + img_name + version + address + symbol, flags=re.VERBOSE
)

frame_regex = FrameRegex.get()
null_frame_regex = re.compile(r'^\d+\s+\?\?\?\s+0{4,} +')
image_regex_uuid = re.compile(r'(0x[0-9a-fA-F]+)' # img_lo
r'\s+-\s+' # -
r'(0x[0-9a-fA-F]+)\s+' # img_hi
r'[+]?(.+?)\s+' # img_name
r'(?:(' +
VersionRegex.version + # img_version
r')\s+)?'
r'(?:<([-0-9a-fA-F]+)>\s+)?' # img_uuid
r'(\?+|/.*)' # img_path
)
exception_type_regex = re.compile(r'^Exception Type:\s+(EXC_[A-Z_]+)(?:\s+\((.*)\))?')
exception_codes_regex = re.compile(r'^Exception Codes:\s+(0x[0-9a-fA-F]+),\s*(0x[0-9a-fA-F]+)')
exception_extra_regex = re.compile(r'^Exception\s+.*:\s+(.*)')
null_frame_regex = re.compile(r"^\d+\s+\?\?\?\s+0{4,} +")
image_regex_uuid = re.compile(
r"(0x[0-9a-fA-F]+)" # img_lo
r"\s+-\s+" # -
r"(0x[0-9a-fA-F]+)\s+" # img_hi
r"[+]?(.+?)\s+" # img_name
r"(?:(" + VersionRegex.version + r")\s+)?" # img_version
r"(?:<([-0-9a-fA-F]+)>\s+)?" # img_uuid
r"(\?+|/.*)" # img_path
)
exception_type_regex = re.compile(
r"^Exception Type:\s+(EXC_[A-Z_]+)(?:\s+\((.*)\))?"
)
exception_codes_regex = re.compile(
r"^Exception Codes:\s+(0x[0-9a-fA-F]+),\s*(0x[0-9a-fA-F]+)"
)
exception_extra_regex = re.compile(r"^Exception\s+.*:\s+(.*)")

def __init__(self, debugger, path, verbose):
super().__init__(debugger, path, verbose)
Expand Down Expand Up @@ -1011,22 +1039,44 @@ def parse_thread(self, line):
print('error: frame regex failed for line: "%s"' % line)
return

frame_id = frame_img_name = frame_addr = frame_symbol = frame_offset = frame_file = frame_line = frame_column = None
frame_id = (
frame_img_name
) = (
frame_addr
) = frame_symbol = frame_offset = frame_file = frame_line = frame_column = None

if len(frame_match.groups()) == 3:
# Get the image UUID from the frame image name.
(frame_id, frame_img_name, frame_addr) = frame_match.groups()
elif len(frame_match.groups()) == 5:
(frame_id, frame_img_name, frame_addr,
frame_symbol, frame_offset) = frame_match.groups()
(
frame_id,
frame_img_name,
frame_addr,
frame_symbol,
frame_offset,
) = frame_match.groups()
elif len(frame_match.groups()) == 7:
(frame_id, frame_img_name, frame_addr,
frame_symbol, frame_offset,
frame_file, frame_line) = frame_match.groups()
(
frame_id,
frame_img_name,
frame_addr,
frame_symbol,
frame_offset,
frame_file,
frame_line,
) = frame_match.groups()
elif len(frame_match.groups()) == 8:
(frame_id, frame_img_name, frame_addr,
frame_symbol, frame_offset,
frame_file, frame_line, frame_column) = frame_match.groups()
(
frame_id,
frame_img_name,
frame_addr,
frame_symbol,
frame_offset,
frame_file,
frame_line,
frame_column,
) = frame_match.groups()

self.thread.add_ident(frame_img_name)
if frame_img_name not in self.crashlog.idents:
Expand Down Expand Up @@ -1057,15 +1107,24 @@ def parse_thread(self, line):
def parse_images(self, line):
image_match = self.image_regex_uuid.search(line)
if image_match:
(img_lo, img_hi, img_name, img_version,
img_uuid, img_path) = image_match.groups()

image = self.crashlog.DarwinImage(int(img_lo, 0), int(img_hi, 0),
img_name.strip(),
img_version.strip()
if img_version else "",
uuid.UUID(img_uuid), img_path,
self.verbose)
(
img_lo,
img_hi,
img_name,
img_version,
img_uuid,
img_path,
) = image_match.groups()

image = self.crashlog.DarwinImage(
int(img_lo, 0),
int(img_hi, 0),
img_name.strip(),
img_version.strip() if img_version else "",
uuid.UUID(img_uuid),
img_path,
self.verbose,
)
unqualified_img_name = os.path.basename(img_path)
if unqualified_img_name in self.symbols:
for symbol in self.symbols[unqualified_img_name]:
Expand Down Expand Up @@ -1326,7 +1385,9 @@ def load_crashlog_in_scripted_process(debugger, crash_log_file, options, result)
if target is None or not target.IsValid():
arch = crashlog.process_arch
if not arch:
raise InteractiveCrashLogException("couldn't create find the architecture to create the target")
raise InteractiveCrashLogException(
"couldn't create find the architecture to create the target"
)
target = debugger.CreateTargetWithFileAndArch(None, arch)
# 4. Fail
if target is None or not target.IsValid():
Expand Down
65 changes: 37 additions & 28 deletions lldb/examples/python/scripted_process/crashlog_scripted_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from lldb.plugins.scripted_process import ScriptedProcess
from lldb.plugins.scripted_process import ScriptedThread

from lldb.macosx.crashlog import CrashLog,CrashLogParser
from lldb.macosx.crashlog import CrashLog, CrashLogParser


class CrashLogScriptedProcess(ScriptedProcess):
def set_crashlog(self, crashlog):
Expand All @@ -23,9 +24,9 @@ def set_crashlog(self, crashlog):
self.loaded_images = []
self.exception = self.crashlog.exception
self.app_specific_thread = None
if hasattr(self.crashlog, 'asi'):
self.metadata['asi'] = self.crashlog.asi
if hasattr(self.crashlog, 'asb'):
if hasattr(self.crashlog, "asi"):
self.metadata["asi"] = self.crashlog.asi
if hasattr(self.crashlog, "asb"):
self.extended_thread_info = self.crashlog.asb

if self.load_all_images:
Expand All @@ -51,7 +52,10 @@ def set_crashlog(self, crashlog):
self.loaded_images.append(image)

for thread in self.crashlog.threads:
if hasattr(thread, 'app_specific_backtrace') and thread.app_specific_backtrace:
if (
hasattr(thread, "app_specific_backtrace")
and thread.app_specific_backtrace
):
# We don't want to include the Application Specific Backtrace
# Thread into the Scripted Process' Thread list.
# Instead, we will try to extract the stackframe pcs from the
Expand All @@ -61,14 +65,12 @@ def set_crashlog(self, crashlog):

self.threads[thread.index] = CrashLogScriptedThread(self, None, thread)


if self.app_specific_thread:
self.extended_thread_info = \
CrashLogScriptedThread.resolve_stackframes(self.app_specific_thread,
self.addr_mask,
self.target)
self.extended_thread_info = CrashLogScriptedThread.resolve_stackframes(
self.app_specific_thread, self.addr_mask, self.target
)

def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData):
def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData):
super().__init__(exe_ctx, args)

if not self.target or not self.target.IsValid():
Expand Down Expand Up @@ -99,7 +101,9 @@ def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredDat
self.exception = None
self.extended_thread_info = None

def read_memory_at_address(self, addr: int, size: int, error: lldb.SBError) -> lldb.SBData:
def read_memory_at_address(
self, addr: int, size: int, error: lldb.SBError
) -> lldb.SBData:
# NOTE: CrashLogs don't contain any memory.
return lldb.SBData()

Expand All @@ -120,16 +124,21 @@ def get_scripted_thread_plugin(self):
def get_process_metadata(self):
return self.metadata


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)
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)
return dict.fromkeys(
[*map(lambda reg: reg["name"], self.register_info["registers"])], 0
)

for reg in self.register_info['registers']:
reg_name = reg['name']
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:
Expand All @@ -141,25 +150,24 @@ def resolve_stackframes(thread, addr_mask, target):
frames = []
for frame in thread.frames:
frame_pc = frame.pc & addr_mask
pc = frame_pc if frame.index == 0 or frame_pc == 0 else frame_pc - 1
pc = frame_pc if frame.index == 0 or frame_pc == 0 else frame_pc - 1
sym_addr = lldb.SBAddress()
sym_addr.SetLoadAddress(pc, target)
if not sym_addr.IsValid():
continue
frames.append({"idx": frame.index, "pc": pc})
return frames


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

self.frames = CrashLogScriptedThread.resolve_stackframes(self.backing_thread,
self.scripted_process.addr_mask,
self.target)
self.frames = CrashLogScriptedThread.resolve_stackframes(
self.backing_thread, self.scripted_process.addr_mask, self.target
)

return self.frames

Expand All @@ -174,7 +182,7 @@ def __init__(self, process, args, crashlog_thread):
else:
self.name = self.backing_thread.name
self.queue = self.backing_thread.queue
self.has_crashed = (self.scripted_process.crashed_thread_idx == self.idx)
self.has_crashed = self.scripted_process.crashed_thread_idx == self.idx
self.create_stackframes()

def get_state(self):
Expand All @@ -184,21 +192,22 @@ def get_state(self):

def get_stop_reason(self) -> Dict[str, Any]:
if not self.has_crashed:
return { "type": lldb.eStopReasonNone }
return {"type": lldb.eStopReasonNone}
# TODO: Investigate what stop reason should be reported when crashed
stop_reason = { "type": lldb.eStopReasonException, "data": { }}
stop_reason = {"type": lldb.eStopReasonException, "data": {}}
if self.scripted_process.exception:
stop_reason['data']['mach_exception'] = self.scripted_process.exception
stop_reason["data"]["mach_exception"] = self.scripted_process.exception
return stop_reason

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())
return struct.pack(
"{}Q".format(len(self.register_ctx)), *self.register_ctx.values()
)

def get_extended_info(self):
if (self.has_crashed):
if self.has_crashed:
self.extended_info = self.scripted_process.extended_thread_info
return self.extended_info

Loading