Skip to content

Commit 16dd693

Browse files
committed
[crashlog] Modularize parser
Instead of parsing the crashlog in one big loop, use methods that correspond to the different parsing modes. Differential revision: https://reviews.llvm.org/D90665
1 parent 6dabc38 commit 16dd693

File tree

1 file changed

+152
-136
lines changed

1 file changed

+152
-136
lines changed

lldb/examples/python/crashlog.py

Lines changed: 152 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -414,10 +414,18 @@ class CrashLogParser:
414414
def __init__(self, path, verbose):
415415
self.path = os.path.expanduser(path)
416416
self.verbose = verbose
417-
self.parse_mode = CrashLogParseMode.NORMAL
418417
self.thread = None
419418
self.app_specific_backtrace = False
420419
self.crashlog = CrashLog(self.path, self.verbose)
420+
self.parse_mode = CrashLogParseMode.NORMAL
421+
self.parsers = {
422+
CrashLogParseMode.NORMAL : self.parse_normal,
423+
CrashLogParseMode.THREAD : self.parse_thread,
424+
CrashLogParseMode.IMAGES : self.parse_images,
425+
CrashLogParseMode.THREGS : self.parse_thread_registers,
426+
CrashLogParseMode.SYSTEM : self.parse_system,
427+
CrashLogParseMode.INSTRS : self.parse_instructions,
428+
}
421429

422430
def parse(self):
423431
with open(self.path,'r') as f:
@@ -445,145 +453,153 @@ def parse(self):
445453
if len(self.crashlog.info_lines) > 0 and len(self.crashlog.info_lines[-1]):
446454
self.crashlog.info_lines.append(line)
447455
self.parse_mode = CrashLogParseMode.NORMAL
448-
elif self.parse_mode == CrashLogParseMode.NORMAL:
449-
if line.startswith('Process:'):
450-
(self.crashlog.process_name, pid_with_brackets) = line[
451-
8:].strip().split(' [')
452-
self.crashlog.process_id = pid_with_brackets.strip('[]')
453-
elif line.startswith('Path:'):
454-
self.crashlog.process_path = line[5:].strip()
455-
elif line.startswith('Identifier:'):
456-
self.crashlog.process_identifier = line[11:].strip()
457-
elif line.startswith('Version:'):
458-
version_string = line[8:].strip()
459-
matched_pair = re.search("(.+)\((.+)\)", version_string)
460-
if matched_pair:
461-
self.crashlog.process_version = matched_pair.group(1)
462-
self.crashlog.process_compatability_version = matched_pair.group(
463-
2)
464-
else:
465-
self.crashlog.process = version_string
466-
self.crashlog.process_compatability_version = version_string
467-
elif self.parent_process_regex.search(line):
468-
parent_process_match = self.parent_process_regex.search(
469-
line)
470-
self.crashlog.parent_process_name = parent_process_match.group(1)
471-
self.crashlog.parent_process_id = parent_process_match.group(2)
472-
elif line.startswith('Exception Type:'):
473-
self.crashlog.thread_exception = line[15:].strip()
474-
continue
475-
elif line.startswith('Exception Codes:'):
476-
self.crashlog.thread_exception_data = line[16:].strip()
477-
continue
478-
elif line.startswith('Exception Subtype:'): # iOS
479-
self.crashlog.thread_exception_data = line[18:].strip()
480-
continue
481-
elif line.startswith('Crashed Thread:'):
482-
self.crashlog.crashed_thread_idx = int(line[15:].strip().split()[0])
483-
continue
484-
elif line.startswith('Triggered by Thread:'): # iOS
485-
self.crashlog.crashed_thread_idx = int(line[20:].strip().split()[0])
486-
continue
487-
elif line.startswith('Report Version:'):
488-
self.crashlog.version = int(line[15:].strip())
489-
continue
490-
elif line.startswith('System Profile:'):
491-
self.parse_mode = CrashLogParseMode.SYSTEM
492-
continue
493-
elif (line.startswith('Interval Since Last Report:') or
494-
line.startswith('Crashes Since Last Report:') or
495-
line.startswith('Per-App Interval Since Last Report:') or
496-
line.startswith('Per-App Crashes Since Last Report:') or
497-
line.startswith('Sleep/Wake UUID:') or
498-
line.startswith('Anonymous UUID:')):
499-
# ignore these
500-
continue
501-
elif line.startswith('Thread'):
502-
thread_state_match = self.thread_state_regex.search(line)
503-
if thread_state_match:
504-
self.app_specific_backtrace = False
505-
thread_state_match = self.thread_regex.search(line)
506-
thread_idx = int(thread_state_match.group(1))
507-
self.parse_mode = CrashLogParseMode.THREGS
508-
self.thread = self.crashlog.threads[thread_idx]
509-
continue
510-
thread_insts_match = self.thread_instrs_regex.search(line)
511-
if thread_insts_match:
512-
self.parse_mode = CrashLogParseMode.INSTRS
513-
continue
514-
thread_match = self.thread_regex.search(line)
515-
if thread_match:
516-
self.app_specific_backtrace = False
517-
self.parse_mode = CrashLogParseMode.THREAD
518-
thread_idx = int(thread_match.group(1))
519-
self.thread = self.crashlog.Thread(thread_idx, False)
520-
continue
521-
continue
522-
elif line.startswith('Binary Images:'):
523-
self.parse_mode = CrashLogParseMode.IMAGES
524-
continue
525-
elif line.startswith('Application Specific Backtrace'):
526-
app_backtrace_match = self.app_backtrace_regex.search(line)
527-
if app_backtrace_match:
528-
self.parse_mode = CrashLogParseMode.THREAD
529-
self.app_specific_backtrace = True
530-
idx = int(app_backtrace_match.group(1))
531-
self.thread = self.crashlog.Thread(idx, True)
532-
elif line.startswith('Last Exception Backtrace:'): # iOS
533-
self.parse_mode = CrashLogParseMode.THREAD
534-
self.app_specific_backtrace = True
535-
idx = 1
536-
self.thread = self.crashlog.Thread(idx, True)
537-
self.crashlog.info_lines.append(line.strip())
538-
elif self.parse_mode == CrashLogParseMode.THREAD:
539-
if line.startswith('Thread'):
540-
continue
541-
if self.null_frame_regex.search(line):
542-
print('warning: thread parser ignored null-frame: "%s"' % line)
543-
continue
544-
frame_match = self.frame_regex.search(line)
545-
if frame_match:
546-
(frame_id, frame_img_name, _, frame_img_version, _,
547-
frame_addr, frame_ofs) = frame_match.groups()
548-
ident = frame_img_name
549-
self.thread.add_ident(ident)
550-
if ident not in self.crashlog.idents:
551-
self.crashlog.idents.append(ident)
552-
self.thread.frames.append(self.crashlog.Frame(int(frame_id), int(
553-
frame_addr, 0), frame_ofs))
554-
else:
555-
print('error: frame regex failed for line: "%s"' % line)
556-
elif self.parse_mode == CrashLogParseMode.IMAGES:
557-
image_match = self.image_regex_uuid.search(line)
558-
if image_match:
559-
(img_lo, img_hi, img_name, _, img_version, _,
560-
_, img_uuid, img_path) = image_match.groups()
561-
image = self.crashlog.DarwinImage(int(img_lo, 0), int(img_hi, 0),
562-
img_name.strip(),
563-
img_version.strip()
564-
if img_version else "",
565-
uuid.UUID(img_uuid), img_path,
566-
self.verbose)
567-
self.crashlog.images.append(image)
568-
else:
569-
print("error: image regex failed for: %s" % line)
570-
571-
elif self.parse_mode == CrashLogParseMode.THREGS:
572-
stripped_line = line.strip()
573-
# "r12: 0x00007fff6b5939c8 r13: 0x0000000007000006 r14: 0x0000000000002a03 r15: 0x0000000000000c00"
574-
reg_values = re.findall(
575-
'([a-zA-Z0-9]+: 0[Xx][0-9a-fA-F]+) *', stripped_line)
576-
for reg_value in reg_values:
577-
(reg, value) = reg_value.split(': ')
578-
self.thread.registers[reg.strip()] = int(value, 0)
579-
elif self.parse_mode == CrashLogParseMode.SYSTEM:
580-
self.crashlog.system_profile.append(line)
581-
elif self.parse_mode == CrashLogParseMode.INSTRS:
582-
pass
456+
else:
457+
self.parsers[self.parse_mode](line)
583458

584459
return self.crashlog
585460

586461

462+
def parse_normal(self, line):
463+
if line.startswith('Process:'):
464+
(self.crashlog.process_name, pid_with_brackets) = line[
465+
8:].strip().split(' [')
466+
self.crashlog.process_id = pid_with_brackets.strip('[]')
467+
elif line.startswith('Path:'):
468+
self.crashlog.process_path = line[5:].strip()
469+
elif line.startswith('Identifier:'):
470+
self.crashlog.process_identifier = line[11:].strip()
471+
elif line.startswith('Version:'):
472+
version_string = line[8:].strip()
473+
matched_pair = re.search("(.+)\((.+)\)", version_string)
474+
if matched_pair:
475+
self.crashlog.process_version = matched_pair.group(1)
476+
self.crashlog.process_compatability_version = matched_pair.group(
477+
2)
478+
else:
479+
self.crashlog.process = version_string
480+
self.crashlog.process_compatability_version = version_string
481+
elif self.parent_process_regex.search(line):
482+
parent_process_match = self.parent_process_regex.search(
483+
line)
484+
self.crashlog.parent_process_name = parent_process_match.group(1)
485+
self.crashlog.parent_process_id = parent_process_match.group(2)
486+
elif line.startswith('Exception Type:'):
487+
self.crashlog.thread_exception = line[15:].strip()
488+
return
489+
elif line.startswith('Exception Codes:'):
490+
self.crashlog.thread_exception_data = line[16:].strip()
491+
return
492+
elif line.startswith('Exception Subtype:'): # iOS
493+
self.crashlog.thread_exception_data = line[18:].strip()
494+
return
495+
elif line.startswith('Crashed Thread:'):
496+
self.crashlog.crashed_thread_idx = int(line[15:].strip().split()[0])
497+
return
498+
elif line.startswith('Triggered by Thread:'): # iOS
499+
self.crashlog.crashed_thread_idx = int(line[20:].strip().split()[0])
500+
return
501+
elif line.startswith('Report Version:'):
502+
self.crashlog.version = int(line[15:].strip())
503+
return
504+
elif line.startswith('System Profile:'):
505+
self.parse_mode = CrashLogParseMode.SYSTEM
506+
return
507+
elif (line.startswith('Interval Since Last Report:') or
508+
line.startswith('Crashes Since Last Report:') or
509+
line.startswith('Per-App Interval Since Last Report:') or
510+
line.startswith('Per-App Crashes Since Last Report:') or
511+
line.startswith('Sleep/Wake UUID:') or
512+
line.startswith('Anonymous UUID:')):
513+
# ignore these
514+
return
515+
elif line.startswith('Thread'):
516+
thread_state_match = self.thread_state_regex.search(line)
517+
if thread_state_match:
518+
self.app_specific_backtrace = False
519+
thread_state_match = self.thread_regex.search(line)
520+
thread_idx = int(thread_state_match.group(1))
521+
self.parse_mode = CrashLogParseMode.THREGS
522+
self.thread = self.crashlog.threads[thread_idx]
523+
return
524+
thread_insts_match = self.thread_instrs_regex.search(line)
525+
if thread_insts_match:
526+
self.parse_mode = CrashLogParseMode.INSTRS
527+
return
528+
thread_match = self.thread_regex.search(line)
529+
if thread_match:
530+
self.app_specific_backtrace = False
531+
self.parse_mode = CrashLogParseMode.THREAD
532+
thread_idx = int(thread_match.group(1))
533+
self.thread = self.crashlog.Thread(thread_idx, False)
534+
return
535+
return
536+
elif line.startswith('Binary Images:'):
537+
self.parse_mode = CrashLogParseMode.IMAGES
538+
return
539+
elif line.startswith('Application Specific Backtrace'):
540+
app_backtrace_match = self.app_backtrace_regex.search(line)
541+
if app_backtrace_match:
542+
self.parse_mode = CrashLogParseMode.THREAD
543+
self.app_specific_backtrace = True
544+
idx = int(app_backtrace_match.group(1))
545+
self.thread = self.crashlog.Thread(idx, True)
546+
elif line.startswith('Last Exception Backtrace:'): # iOS
547+
self.parse_mode = CrashLogParseMode.THREAD
548+
self.app_specific_backtrace = True
549+
idx = 1
550+
self.thread = self.crashlog.Thread(idx, True)
551+
self.crashlog.info_lines.append(line.strip())
552+
553+
def parse_thread(self, line):
554+
if line.startswith('Thread'):
555+
return
556+
if self.null_frame_regex.search(line):
557+
print('warning: thread parser ignored null-frame: "%s"' % line)
558+
return
559+
frame_match = self.frame_regex.search(line)
560+
if frame_match:
561+
(frame_id, frame_img_name, _, frame_img_version, _,
562+
frame_addr, frame_ofs) = frame_match.groups()
563+
ident = frame_img_name
564+
self.thread.add_ident(ident)
565+
if ident not in self.crashlog.idents:
566+
self.crashlog.idents.append(ident)
567+
self.thread.frames.append(self.crashlog.Frame(int(frame_id), int(
568+
frame_addr, 0), frame_ofs))
569+
else:
570+
print('error: frame regex failed for line: "%s"' % line)
571+
572+
def parse_images(self, line):
573+
image_match = self.image_regex_uuid.search(line)
574+
if image_match:
575+
(img_lo, img_hi, img_name, _, img_version, _,
576+
_, img_uuid, img_path) = image_match.groups()
577+
image = self.crashlog.DarwinImage(int(img_lo, 0), int(img_hi, 0),
578+
img_name.strip(),
579+
img_version.strip()
580+
if img_version else "",
581+
uuid.UUID(img_uuid), img_path,
582+
self.verbose)
583+
self.crashlog.images.append(image)
584+
else:
585+
print("error: image regex failed for: %s" % line)
586+
587+
588+
def parse_thread_registers(self, line):
589+
stripped_line = line.strip()
590+
# "r12: 0x00007fff6b5939c8 r13: 0x0000000007000006 r14: 0x0000000000002a03 r15: 0x0000000000000c00"
591+
reg_values = re.findall(
592+
'([a-zA-Z0-9]+: 0[Xx][0-9a-fA-F]+) *', stripped_line)
593+
for reg_value in reg_values:
594+
(reg, value) = reg_value.split(': ')
595+
self.thread.registers[reg.strip()] = int(value, 0)
596+
597+
def parse_system(self, line):
598+
self.crashlog.system_profile.append(line)
599+
600+
def parse_instructions(self, line):
601+
pass
602+
587603

588604
def usage():
589605
print("Usage: lldb-symbolicate.py [-n name] executable-image")

0 commit comments

Comments
 (0)