JDevlieghere created this revision.
JDevlieghere added reviewers: aprantl, jasonmolenda, clayborg.
JDevlieghere requested review of this revision.
Add a parser for JSON crashlogs. The `CrashLogParser` now defers to either the
`JSONCrashLogParser` or the `TextCrashLogParser`. It first tries to interpret
the input as JSON, and if that fails falling back to the textual parser.
https://reviews.llvm.org/D91130
Files:
lldb/examples/python/crashlog.py
lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/Assertion.check
lldb/test/Shell/ScriptInterpreter/Python/Crashlog/crashlog.test
lldb/test/Shell/ScriptInterpreter/Python/Crashlog/json_parser.test
Index: lldb/test/Shell/ScriptInterpreter/Python/Crashlog/json_parser.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Python/Crashlog/json_parser.test
@@ -0,0 +1,45 @@
+# -*- python -*-
+# RUN: cd %S/../../../../../examples/python && cat %s | %lldb 2>&1 > %t.out
+# RUN: cat %t.out | FileCheck %S/Inputs/Assertion.check
+script
+import crashlog
+import json
+
+parser = crashlog.JSONCrashLogParser("", "", False)
+
+process_info_json = json.loads('{"pid" : 287, "procName" : "mediaserverd", "procPath" : "\/usr\/sbin\/mediaserverd"}')
+parser.parse_process_info(process_info_json)
+
+assert parser.crashlog.process_id == 287
+assert parser.crashlog.process_identifier == "mediaserverd"
+assert parser.crashlog.process_path == "/usr/sbin/mediaserverd"
+
+crash_reason_json = json.loads('{"type" : "EXC_BAD_ACCESS", "signal" : "SIGSEGV", "subtype" : "KERN_INVALID_ADDRESS"}')
+assert parser.parse_crash_reason(crash_reason_json) == "EXC_BAD_ACCESS (SIGSEGV) (KERN_INVALID_ADDRESS)"
+
+crash_reason_json = json.loads('{"type" : "EXC_BAD_ACCESS", "signal" : "SIGSEGV"}')
+assert parser.parse_crash_reason(crash_reason_json) == "EXC_BAD_ACCESS (SIGSEGV)"
+
+crash_reason_json = json.loads('{"type" : "EXC_BAD_ACCESS", "signal" : "SIGSEGV", "codes" : "0x0000000000000000, 0x0000000000000000"}')
+assert parser.parse_crash_reason(crash_reason_json) == "EXC_BAD_ACCESS (SIGSEGV) (0x0000000000000000, 0x0000000000000000)"
+
+thread_state_json = json.loads('{"x":[268451845,117442566],"lr":7309751904,"cpsr":1073741824,"fp":6093236784,"sp":6093236704,"esr":1442840704,"pc":7309755088}')
+registers = parser.parse_thread_registers(thread_state_json)
+assert registers['x0'] == 268451845
+assert registers['x1'] == 117442566
+assert registers['lr'] == 7309751904
+assert registers['cpsr'] ==1073741824
+assert registers['fp'] == 6093236784
+assert registers['sp'] == 6093236704
+assert registers['esr'] == 1442840704
+assert registers['pc'] == 7309755088
+
+parser.data = json.loads('{"usedImages":[["f4d85377-f215-3da3-921e-3fe870e622e9",7309737984,"P"]],"legacyInfo":{"imageExtraInfo":[{"size":204800,"arch":"arm64e","path":"/usr/lib/system/libsystem_kernel.dylib","name":"libsystem_kernel.dylib"}]}}')
+thread_json = json.loads('[{"triggered":true,"id":3835,"queue":"com.apple.bwgraph.devicevendor","frames":[[0,101472],[0,408892]]}]')
+parser.parse_threads(thread_json)
+assert len(parser.crashlog.threads) == 1
+assert parser.crashlog.threads[0].queue == "com.apple.bwgraph.devicevendor"
+assert len(parser.crashlog.threads[0].frames) == 2
+assert parser.crashlog.threads[0].frames[0].pc == 7309839456
+assert parser.crashlog.threads[0].frames[0].description == 101472
+exit()
Index: lldb/test/Shell/ScriptInterpreter/Python/Crashlog/crashlog.test
===================================================================
--- lldb/test/Shell/ScriptInterpreter/Python/Crashlog/crashlog.test
+++ lldb/test/Shell/ScriptInterpreter/Python/Crashlog/crashlog.test
@@ -4,7 +4,7 @@
# CHECK-LABEL: {{S}}KIP BEYOND CHECKS
script
import crashlog
-crash_log_parser = crashlog.CrashLogParser
+crash_log_parser = crashlog.TextCrashLogParser
crash_log = crashlog.CrashLog
images = [
"0x10b60b000 - 0x10f707fff com.apple.LLDB.framework (1.1000.11.38.2 - 1000.11.38.2) <96E36F5C-1A83-39A1-8713-5FDD9701C3F1> /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/LLDB",
Index: lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/Assertion.check
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/Assertion.check
@@ -0,0 +1 @@
+# CHECK-NOT: AssertionError
Index: lldb/examples/python/crashlog.py
===================================================================
--- lldb/examples/python/crashlog.py
+++ lldb/examples/python/crashlog.py
@@ -41,6 +41,7 @@
import sys
import time
import uuid
+import json
try:
# First try for LLDB in case PYTHONPATH is already correctly setup.
@@ -378,6 +379,129 @@
return self.target
+class CrashLogFormatException(Exception):
+ pass
+
+
+class CrashLogParser:
+ def parse(self, debugger, path, verbose):
+ try:
+ return JSONCrashLogParser(debugger, path, verbose).parse()
+ except CrashLogFormatException:
+ return TextCrashLogParser(debugger, path, verbose).parse()
+
+
+class JSONCrashLogParser:
+ def __init__(self, debugger, path, verbose):
+ self.path = os.path.expanduser(path)
+ self.verbose = verbose
+ self.crashlog = CrashLog(debugger, self.path, self.verbose)
+
+ def parse(self):
+ with open(self.path, 'r') as f:
+ buffer = f.read()
+
+ # First line is meta-data.
+ buffer = buffer[buffer.index('\n') + 1:]
+
+ try:
+ self.data = json.loads(buffer)
+ except ValueError:
+ raise CrashLogFormatException()
+
+ self.parse_process_info(self.data)
+ self.parse_images(self.data['usedImages'])
+ self.parse_threads(self.data['threads'])
+
+ thread = self.crashlog.threads[self.crashlog.crashed_thread_idx]
+ thread.reason = self.parse_crash_reason(self.data['exception'])
+ thread.registers = self.parse_thread_registers(self.data['threadState'])
+
+ return self.crashlog
+
+ def get_image_extra_info(self, idx):
+ return self.data['legacyInfo']['imageExtraInfo'][idx]
+
+ def get_used_image(self, idx):
+ return self.data['usedImages'][idx]
+
+ def parse_process_info(self, json_data):
+ self.crashlog.process_id = json_data['pid']
+ self.crashlog.process_identifier = json_data['procName']
+ self.crashlog.process_path = json_data['procPath']
+
+ def parse_crash_reason(self, json_exception):
+ exception_type = json_exception['type']
+ exception_signal = json_exception['signal']
+ if 'codes' in json_exception:
+ exception_extra = " ({})".format(json_exception['codes'])
+ elif 'subtype' in json_exception:
+ exception_extra = " ({})".format(json_exception['subtype'])
+ else:
+ exception_extra = ""
+ return "{} ({}){}".format(exception_type, exception_signal,
+ exception_extra)
+
+ def parse_images(self, json_images):
+ idx = 0
+ for json_images in json_images:
+ img_uuid = uuid.UUID(json_images[0])
+ low = int(json_images[1])
+ high = 0
+ extra_info = self.get_image_extra_info(idx)
+ name = extra_info['name']
+ path = extra_info['path']
+ version = ""
+ darwin_image = self.crashlog.DarwinImage(low, high, name, version,
+ img_uuid, path,
+ self.verbose)
+ self.crashlog.images.append(darwin_image)
+ idx += 1
+
+ def parse_frames(self, thread, json_frames):
+ idx = 0
+ for json_frame in json_frames:
+ image_id = int(json_frame[0])
+
+ ident = self.get_image_extra_info(image_id)['name']
+ thread.add_ident(ident)
+ if ident not in self.crashlog.idents:
+ self.crashlog.idents.append(ident)
+
+ frame_offset = int(json_frame[1])
+ image = self.get_used_image(image_id)
+ image_addr = int(image[1])
+ pc = image_addr + frame_offset
+ thread.frames.append(self.crashlog.Frame(idx, pc, frame_offset))
+ idx += 1
+
+ def parse_threads(self, json_threads):
+ idx = 0
+ for json_thread in json_threads:
+ thread = self.crashlog.Thread(idx, False)
+ if json_thread.get('triggered', False):
+ self.crashlog.crashed_thread_idx = idx
+ thread.queue = json_thread.get('queue')
+ self.parse_frames(thread, json_thread.get('frames', []))
+ self.crashlog.threads.append(thread)
+ idx += 1
+
+ def parse_thread_registers(self, json_thread_state):
+ idx = 0
+ registers = dict()
+ for reg in json_thread_state.get('x', []):
+ key = str('x{}'.format(idx))
+ value = int(reg)
+ registers[key] = value
+ idx += 1
+
+ for register in ['lr', 'cpsr', 'fp', 'sp', 'esr', 'pc']:
+ if register in json_thread_state:
+ registers[register] = int(json_thread_state[register])
+
+ return registers
+
+
class CrashLogParseMode:
NORMAL = 0
THREAD = 1
@@ -387,7 +511,7 @@
INSTRS = 5
-class CrashLogParser:
+class TextCrashLogParser:
parent_process_regex = re.compile('^Parent Process:\s*(.*)\[(\d+)\]')
thread_state_regex = re.compile('^Thread ([0-9]+) crashed with')
thread_instrs_regex = re.compile('^Thread ([0-9]+) instruction stream')
@@ -720,7 +844,7 @@
crash_logs = list()
for crash_log_file in crash_log_files:
try:
- crash_log = CrashLogParser(debugger, crash_log_file, options.verbose).parse()
+ crash_log = CrashLogParser().parse(debugger, crash_log_file, options.verbose)
except Exception as e:
print(e)
continue
@@ -1055,8 +1179,7 @@
interactive_crashlogs(debugger, options, args)
else:
for crash_log_file in args:
- crash_log_parser = CrashLogParser(debugger, crash_log_file, options.verbose)
- crash_log = crash_log_parser.parse()
+ crash_log = CrashLogParser().parse(debugger, crash_log_file, options.verbose)
SymbolicateCrashLog(crash_log, options)
if __name__ == '__main__':
# Create a new debugger instance
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits