Skip to content

Commit 8f75c4d

Browse files
committed
[lldb/crashlog] Make TextCrashLogParser more resilient to new lines
This patch changes the parsing logic for the legacy crash report format to avoid interrupting the parsing if there are new lines in the middle of a section. To do, the parser starts to skip all consecutive empty lines. If the number of lines skipped is greater than 1, the parser considers that it reached a new setion of the report and should reset the parsing mode to back to normal. Otherwise, it tries to parse the next line in the current parsing mode. If it succeeds, the parser will also skip that line since it has already been parsed and continue the parsing. rdar://107022595 Differential Revision: https://reviews.llvm.org/D157043 Signed-off-by: Med Ismail Bennani <[email protected]>
1 parent f043e66 commit 8f75c4d

File tree

1 file changed

+60
-31
lines changed

1 file changed

+60
-31
lines changed

lldb/examples/python/crashlog.py

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -537,21 +537,21 @@ class InteractiveCrashLogException(Exception):
537537

538538
class CrashLogParser:
539539
@staticmethod
540-
def create(debugger, path, verbose):
540+
def create(debugger, path, options):
541541
data = JSONCrashLogParser.is_valid_json(path)
542542
if data:
543-
parser = JSONCrashLogParser(debugger, path, verbose)
543+
parser = JSONCrashLogParser(debugger, path, options)
544544
parser.data = data
545545
return parser
546546
else:
547-
return TextCrashLogParser(debugger, path, verbose)
547+
return TextCrashLogParser(debugger, path, options)
548548

549-
def __init__(self, debugger, path, verbose):
549+
def __init__(self, debugger, path, options):
550550
self.path = os.path.expanduser(path)
551-
self.verbose = verbose
551+
self.options = options
552552
# List of DarwinImages sorted by their index.
553553
self.images = list()
554-
self.crashlog = CrashLog(debugger, self.path, self.verbose)
554+
self.crashlog = CrashLog(debugger, self.path, self.options.verbose)
555555

556556
@abc.abstractmethod
557557
def parse(self):
@@ -577,8 +577,8 @@ def parse_json(buffer):
577577
except:
578578
return None
579579

580-
def __init__(self, debugger, path, verbose):
581-
super().__init__(debugger, path, verbose)
580+
def __init__(self, debugger, path, options):
581+
super().__init__(debugger, path, options)
582582

583583
def parse(self):
584584
try:
@@ -639,7 +639,7 @@ def parse_images(self, json_images):
639639
path = json_image["path"] if "path" in json_image else ""
640640
version = ""
641641
darwin_image = self.crashlog.DarwinImage(
642-
low, high, name, version, img_uuid, path, self.verbose
642+
low, high, name, version, img_uuid, path, self.options.verbose
643643
)
644644
if "arch" in json_image:
645645
darwin_image.arch = json_image["arch"]
@@ -898,8 +898,8 @@ def get(cls):
898898
)
899899
exception_extra_regex = re.compile(r"^Exception\s+.*:\s+(.*)")
900900

901-
def __init__(self, debugger, path, verbose):
902-
super().__init__(debugger, path, verbose)
901+
def __init__(self, debugger, path, options):
902+
super().__init__(debugger, path, options)
903903
self.thread = None
904904
self.app_specific_backtrace = False
905905
self.parse_mode = CrashLogParseMode.NORMAL
@@ -917,8 +917,15 @@ def parse(self):
917917
with open(self.path, "r", encoding="utf-8") as f:
918918
lines = f.read().splitlines()
919919

920-
for line in lines:
920+
idx = 0
921+
lines_count = len(lines)
922+
while True:
923+
if idx >= lines_count:
924+
break
925+
926+
line = lines[idx]
921927
line_len = len(line)
928+
922929
if line_len == 0:
923930
if self.thread:
924931
if self.parse_mode == CrashLogParseMode.THREAD:
@@ -935,22 +942,36 @@ def parse(self):
935942
else:
936943
self.crashlog.threads.append(self.thread)
937944
self.thread = None
938-
else:
939-
# only append an extra empty line if the previous line
940-
# in the info_lines wasn't empty
941-
if len(self.crashlog.info_lines) > 0 and len(
942-
self.crashlog.info_lines[-1]
943-
):
944-
self.crashlog.info_lines.append(line)
945+
946+
empty_lines = 1
947+
while (
948+
idx + empty_lines < lines_count
949+
and len(lines[idx + empty_lines]) == 0
950+
):
951+
empty_lines = empty_lines + 1
952+
953+
if (
954+
empty_lines == 1
955+
and idx + empty_lines < lines_count - 1
956+
and self.parse_mode != CrashLogParseMode.NORMAL
957+
):
958+
# check if next line can be parsed with the current parse mode
959+
next_line_idx = idx + empty_lines
960+
if self.parsers[self.parse_mode](lines[next_line_idx]):
961+
# If that suceeded, skip the empty line and the next line.
962+
idx = next_line_idx + 1
963+
continue
945964
self.parse_mode = CrashLogParseMode.NORMAL
946-
else:
947-
self.parsers[self.parse_mode](line)
965+
966+
self.parsers[self.parse_mode](line)
967+
968+
idx = idx + 1
948969

949970
return self.crashlog
950971

951972
def parse_exception(self, line):
952973
if not line.startswith("Exception"):
953-
return
974+
return False
954975
if line.startswith("Exception Type:"):
955976
self.crashlog.thread_exception = line[15:].strip()
956977
exception_type_match = self.exception_type_regex.search(line)
@@ -968,7 +989,7 @@ def parse_exception(self, line):
968989
elif line.startswith("Exception Codes:"):
969990
self.crashlog.thread_exception_data = line[16:].strip()
970991
if "type" not in self.crashlog.exception:
971-
return
992+
return False
972993
exception_codes_match = self.exception_codes_regex.search(line)
973994
if exception_codes_match:
974995
self.crashlog.exception["codes"] = self.crashlog.thread_exception_data
@@ -979,10 +1000,11 @@ def parse_exception(self, line):
9791000
]
9801001
else:
9811002
if "type" not in self.crashlog.exception:
982-
return
1003+
return False
9831004
exception_extra_match = self.exception_extra_regex.search(line)
9841005
if exception_extra_match:
9851006
self.crashlog.exception["message"] = exception_extra_match.group(1)
1007+
return True
9861008

9871009
def parse_normal(self, line):
9881010
if line.startswith("Process:"):
@@ -1081,14 +1103,14 @@ def parse_normal(self, line):
10811103

10821104
def parse_thread(self, line):
10831105
if line.startswith("Thread"):
1084-
return
1106+
return False
10851107
if self.null_frame_regex.search(line):
10861108
print('warning: thread parser ignored null-frame: "%s"' % line)
1087-
return
1109+
return False
10881110
frame_match = self.frame_regex.search(line)
10891111
if not frame_match:
10901112
print('error: frame regex failed for line: "%s"' % line)
1091-
return
1113+
return False
10921114

10931115
frame_id = (
10941116
frame_img_name
@@ -1155,6 +1177,8 @@ def parse_thread(self, line):
11551177
self.crashlog.Frame(int(frame_id), int(frame_addr, 0), description)
11561178
)
11571179

1180+
return True
1181+
11581182
def parse_images(self, line):
11591183
image_match = self.image_regex_uuid.search(line)
11601184
if image_match:
@@ -1174,7 +1198,7 @@ def parse_images(self, line):
11741198
img_version.strip() if img_version else "",
11751199
uuid.UUID(img_uuid),
11761200
img_path,
1177-
self.verbose,
1201+
self.options.verbose,
11781202
)
11791203
unqualified_img_name = os.path.basename(img_path)
11801204
if unqualified_img_name in self.symbols:
@@ -1188,17 +1212,22 @@ def parse_images(self, line):
11881212

11891213
self.images.append(image)
11901214
self.crashlog.images.append(image)
1215+
return True
11911216
else:
1192-
print("error: image regex failed for: %s" % line)
1217+
if self.options.debug:
1218+
print("error: image regex failed for: %s" % line)
1219+
return False
11931220

11941221
def parse_thread_registers(self, line):
11951222
# "r12: 0x00007fff6b5939c8 r13: 0x0000000007000006 r14: 0x0000000000002a03 r15: 0x0000000000000c00"
11961223
reg_values = re.findall("([a-z0-9]+): (0x[0-9a-f]+)", line, re.I)
11971224
for reg, value in reg_values:
11981225
self.thread.registers[reg] = int(value, 16)
1226+
return len(reg_values) != 0
11991227

12001228
def parse_system(self, line):
12011229
self.crashlog.system_profile.append(line)
1230+
return True
12021231

12031232
def parse_instructions(self, line):
12041233
pass
@@ -1412,7 +1441,7 @@ def add_module(image, target, obj_dir):
14121441

14131442

14141443
def load_crashlog_in_scripted_process(debugger, crashlog_path, options, result):
1415-
crashlog = CrashLogParser.create(debugger, crashlog_path, False).parse()
1444+
crashlog = CrashLogParser.create(debugger, crashlog_path, options).parse()
14161445

14171446
target = lldb.SBTarget()
14181447
# 1. Try to use the user-provided target
@@ -1735,7 +1764,7 @@ def should_run_in_interactive_mode(options, ci):
17351764
result.SetError(str(e))
17361765
else:
17371766
crash_log = CrashLogParser.create(
1738-
debugger, crashlog_path, options.verbose
1767+
debugger, crashlog_path, options
17391768
).parse()
17401769
SymbolicateCrashLog(crash_log, options)
17411770

0 commit comments

Comments
 (0)