| OLD | NEW |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. | 1 # Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 from collections import namedtuple |
| 5 import re | 6 import re |
| 6 | 7 |
| 7 from crash.type_enums import CallStackFormatType | 8 from crash.type_enums import CallStackFormatType |
| 8 from crash.type_enums import LanguageType | 9 from crash.type_enums import LanguageType |
| 9 from crash.type_enums import SanitizerType | 10 from crash.type_enums import SanitizerType |
| 10 | 11 |
| 11 | 12 |
| 13 class StartOfCallStack( |
| 14 namedtuple('StartOfCallStack', |
| 15 ['priority', 'format_type', 'language_type', 'metadata'])): |
| 16 """Represents the start of a new callstack. |
| 17 |
| 18 Properties: |
| 19 priority (int): Priority of the new callstack. |
| 20 format_type (CallStackFormatType): The format of the new callstack. |
| 21 language_type (LanguageType): The language of the new callstack. |
| 22 metadata (dict): Dict of metadata for the new callstack, e.g. pid of the |
| 23 stack. |
| 24 """ |
| 25 __slots__ = () |
| 26 |
| 27 def __new__(cls, priority, format_type, language_type, metadata=None): |
| 28 return super(cls, StartOfCallStack).__new__(cls, priority, format_type, |
| 29 language_type, metadata or {}) |
| 30 |
| 31 def __str__(self): # pragma: no cover |
| 32 return ('%s(priority = %d, format_type = %d, ' |
| 33 'language_type = %d, metadata = %s)' % (self.__class__.__name__, |
| 34 self.priority, |
| 35 self.format_type, |
| 36 self.language_type, |
| 37 self.metadata)) |
| 38 |
| 39 |
| 12 class CallStackDetector(object): | 40 class CallStackDetector(object): |
| 13 """Class for detecting the start of a particular sort of CallStack.""" | 41 """Class for detecting the start of a particular sort of CallStack.""" |
| 14 | 42 |
| 15 # TODO(crbug.com/677321) Make the return to be a namedtuple. | 43 def __call__(self, line, flags=None): |
| 16 def IsStartOfNewCallStack(self, line, flags=None): | |
| 17 """Determines whether a line is the start of a new callstack or not. | 44 """Determines whether a line is the start of a new callstack or not. |
| 18 | 45 |
| 19 Args: | 46 Args: |
| 20 line (str): The line to be checked. | 47 line (str): The line to be checked. |
| 21 flags (FlagManager): manager for keeping track of parsing flags. | 48 flags (FlagManager): manager for keeping track of parsing flags. |
| 22 | 49 |
| 23 Returns: | 50 Returns: |
| 24 A tuple - | 51 A ``StartOfCallStack`` or None if no callstack found. |
| 25 (is_new_callstack, priority, format_type, language_type, metadata) | |
| 26 is_new_callstack (bool): Boolean indicating whether this is the start of | |
| 27 a new callstack. | |
| 28 priority (int): Priority of a callstack. | |
| 29 format_type (CallStackFormatType): The format of the new callstack, or | |
| 30 None if there is no new callstack. | |
| 31 language_type (LanguageType): The language of the new callstack, or None | |
| 32 if there is no new callstack. | |
| 33 metadata (dict): Dict of metadata for the new callstack, e.g. pid of | |
| 34 the stack. | |
| 35 """ | 52 """ |
| 36 raise NotImplementedError() | 53 raise NotImplementedError() |
| 37 | 54 |
| 38 | 55 |
| 39 class AndroidJobDetector(CallStackDetector): | 56 class AndroidJobDetector(CallStackDetector): |
| 40 """Detects the start of an android job callstack.""" | 57 """Detects the start of an android job callstack.""" |
| 41 JAVA_LANG_CALLSTACK_START_PATTERN = r'^java\.[A-Za-z0-9$._]+' | 58 JAVA_LANG_CALLSTACK_START_PATTERN = r'^java\.[A-Za-z0-9$._]+' |
| 42 JAVA_ORG_GHROMIUM_CALLSTACK_START_PATTERN = r'^org\.chromium\.[A-Za-z0-9$._]+' | 59 JAVA_ORG_GHROMIUM_CALLSTACK_START_PATTERN = r'^org\.chromium\.[A-Za-z0-9$._]+' |
| 43 JAVA_CAUSED_BY_CALLSTACK_START_PATTERN = r'^Caused by:' | 60 JAVA_CAUSED_BY_CALLSTACK_START_PATTERN = r'^Caused by:' |
| 44 JAVA_ANDROID_CALLSTACK_START_PATTERN = ( | 61 JAVA_ANDROID_CALLSTACK_START_PATTERN = ( |
| 45 r'^(com\.google\.)?android\.[A-Za-z0-9$._]+') | 62 r'^(com\.google\.)?android\.[A-Za-z0-9$._]+') |
| 46 | 63 |
| 47 JAVA_CALLSTACK_START_REGEX = re.compile( | 64 JAVA_CALLSTACK_START_REGEX = re.compile( |
| 48 '|'.join([JAVA_LANG_CALLSTACK_START_PATTERN, | 65 '|'.join([JAVA_LANG_CALLSTACK_START_PATTERN, |
| 49 JAVA_ORG_GHROMIUM_CALLSTACK_START_PATTERN, | 66 JAVA_ORG_GHROMIUM_CALLSTACK_START_PATTERN, |
| 50 JAVA_CAUSED_BY_CALLSTACK_START_PATTERN, | 67 JAVA_CAUSED_BY_CALLSTACK_START_PATTERN, |
| 51 JAVA_ANDROID_CALLSTACK_START_PATTERN])) | 68 JAVA_ANDROID_CALLSTACK_START_PATTERN])) |
| 52 | 69 |
| 53 def IsStartOfNewCallStack(self, line, flags=None): | 70 def __call__(self, line, flags=None): |
| 54 if AndroidJobDetector.JAVA_CALLSTACK_START_REGEX.match(line): | 71 if AndroidJobDetector.JAVA_CALLSTACK_START_REGEX.match(line): |
| 55 # Only assign the highest priority to fatal exception stack or segv | 72 # Only assign the highest priority to fatal exception stack or segv |
| 56 # stack. | 73 # stack. |
| 57 if flags and flags.Get('java_main_stack_flag'): | 74 if flags and flags.Get('java_main_stack_flag'): |
| 58 flags.TurnOff('java_main_stack_flag') | 75 flags.TurnOff('java_main_stack_flag') |
| 59 return True, 0, CallStackFormatType.JAVA, LanguageType.JAVA, {} | 76 return StartOfCallStack(0, CallStackFormatType.JAVA, |
| 77 LanguageType.JAVA, {}) |
| 60 | 78 |
| 61 return True, 1, CallStackFormatType.JAVA, LanguageType.JAVA, {} | 79 return StartOfCallStack(1, CallStackFormatType.JAVA, |
| 80 LanguageType.JAVA, {}) |
| 62 | 81 |
| 63 return False, None, None, None, None | 82 return None |
| 64 | 83 |
| 65 | 84 |
| 66 class SyzyasanDetector(CallStackDetector): | 85 class SyzyasanDetector(CallStackDetector): |
| 67 """Detects the start of a syzyasn callstack.""" | 86 """Detects the start of a syzyasn callstack.""" |
| 68 SYZYASAN_CRASH_CALLSTACK_START_REGEX = re.compile(r'^Crash stack:$') | 87 SYZYASAN_CRASH_CALLSTACK_START_REGEX = re.compile(r'^Crash stack:$') |
| 69 SYZYASAN_NON_CRASH_CALLSTACK_START_REGEX = re.compile(r'^(?!Crash).* stack:$') | 88 SYZYASAN_NON_CRASH_CALLSTACK_START_REGEX = re.compile(r'^(?!Crash).* stack:$') |
| 70 | 89 |
| 71 def IsStartOfNewCallStack(self, line, flags=None): | 90 def __call__(self, line, flags=None): |
| 72 # In syzyasan build, new stack starts with 'crash stack:', | 91 # In syzyasan build, new stack starts with 'crash stack:', |
| 73 # 'freed stack:', etc. | 92 # 'freed stack:', etc. |
| 74 if SyzyasanDetector.SYZYASAN_CRASH_CALLSTACK_START_REGEX.match(line): | 93 if SyzyasanDetector.SYZYASAN_CRASH_CALLSTACK_START_REGEX.match(line): |
| 75 return True, 0, CallStackFormatType.SYZYASAN, LanguageType.CPP, {} | 94 return StartOfCallStack(0, CallStackFormatType.SYZYASAN, |
| 95 LanguageType.CPP, {}) |
| 76 # Other callstacks all get priority 1. | 96 # Other callstacks all get priority 1. |
| 77 if SyzyasanDetector.SYZYASAN_NON_CRASH_CALLSTACK_START_REGEX.match(line): | 97 if SyzyasanDetector.SYZYASAN_NON_CRASH_CALLSTACK_START_REGEX.match(line): |
| 78 return True, 1, CallStackFormatType.SYZYASAN, LanguageType.CPP, {} | 98 return StartOfCallStack(1, CallStackFormatType.SYZYASAN, |
| 99 LanguageType.CPP, {}) |
| 79 | 100 |
| 80 return False, None, None, None, None | 101 return None |
| 81 | 102 |
| 82 | 103 |
| 83 class TsanDetector(CallStackDetector): | 104 class TsanDetector(CallStackDetector): |
| 84 """Detects the start of a thread sanitizer callstack.""" | 105 """Detects the start of a thread sanitizer callstack.""" |
| 85 TSAN_CRASH_CALLSTACK_START_PATTERN1 = r'^(Read|Write) of size \d+' | 106 TSAN_CRASH_CALLSTACK_START_PATTERN1 = r'^(Read|Write) of size \d+' |
| 86 TSAN_CRASH_CALLSTACK_START_PATTERN2 = r'^[A-Z]+: ThreadSanitizer' | 107 TSAN_CRASH_CALLSTACK_START_PATTERN2 = r'^[A-Z]+: ThreadSanitizer' |
| 87 TSAN_ALLOCATION_CALLSTACK_START_PATTERN = ( | 108 TSAN_ALLOCATION_CALLSTACK_START_PATTERN = ( |
| 88 r'^Previous (write|read) of size \d+') | 109 r'^Previous (write|read) of size \d+') |
| 89 TSAN_LOCATION_CALLSTACK_START_PATTERN = ( | 110 TSAN_LOCATION_CALLSTACK_START_PATTERN = ( |
| 90 r'^Location is heap block of size \d+') | 111 r'^Location is heap block of size \d+') |
| 91 | 112 |
| 92 TSAN_CRASH_CALLSTACK_START_REGEX = re.compile( | 113 TSAN_CRASH_CALLSTACK_START_REGEX = re.compile( |
| 93 '|'.join([TSAN_CRASH_CALLSTACK_START_PATTERN1, | 114 '|'.join([TSAN_CRASH_CALLSTACK_START_PATTERN1, |
| 94 TSAN_CRASH_CALLSTACK_START_PATTERN2])) | 115 TSAN_CRASH_CALLSTACK_START_PATTERN2])) |
| 95 | 116 |
| 96 TSAN_NON_CRASH_CALLSTACK_START_REGEX = re.compile( | 117 TSAN_NON_CRASH_CALLSTACK_START_REGEX = re.compile( |
| 97 '|'.join([TSAN_ALLOCATION_CALLSTACK_START_PATTERN, | 118 '|'.join([TSAN_ALLOCATION_CALLSTACK_START_PATTERN, |
| 98 TSAN_LOCATION_CALLSTACK_START_PATTERN])) | 119 TSAN_LOCATION_CALLSTACK_START_PATTERN])) |
| 99 | 120 |
| 100 def IsStartOfNewCallStack(self, line, flags=None): | 121 def __call__(self, line, flags=None): |
| 101 # Crash stack gets priority 0. | 122 # Crash stack gets priority 0. |
| 102 if TsanDetector.TSAN_CRASH_CALLSTACK_START_REGEX.match(line): | 123 if TsanDetector.TSAN_CRASH_CALLSTACK_START_REGEX.match(line): |
| 103 return True, 0, CallStackFormatType.DEFAULT, LanguageType.CPP, {} | 124 return StartOfCallStack(0, CallStackFormatType.DEFAULT, |
| 125 LanguageType.CPP, {}) |
| 104 | 126 |
| 105 # All other stacks get priority 1. | 127 # All other stacks get priority 1. |
| 106 if TsanDetector.TSAN_NON_CRASH_CALLSTACK_START_REGEX.match(line): | 128 if TsanDetector.TSAN_NON_CRASH_CALLSTACK_START_REGEX.match(line): |
| 107 return True, 1, CallStackFormatType.DEFAULT, LanguageType.CPP, {} | 129 return StartOfCallStack(1, CallStackFormatType.DEFAULT, |
| 130 LanguageType.CPP, {}) |
| 108 | 131 |
| 109 return False, None, None, None, None | 132 return None |
| 110 | 133 |
| 111 | 134 |
| 112 class UbsanDetector(CallStackDetector): | 135 class UbsanDetector(CallStackDetector): |
| 113 """Detects the start of an undefined-behavior callstack.""" | 136 """Detects the start of an undefined-behavior callstack.""" |
| 114 UBSAN_CALLSTACK_START_REGEX = re.compile(r'^.*: runtime error: .*$') | 137 UBSAN_CALLSTACK_START_REGEX = re.compile(r'^.*: runtime error: .*$') |
| 115 | 138 |
| 116 def IsStartOfNewCallStack(self, line, flags=None): | 139 def __call__(self, line, flags=None): |
| 117 if UbsanDetector.UBSAN_CALLSTACK_START_REGEX.match(line): | 140 if UbsanDetector.UBSAN_CALLSTACK_START_REGEX.match(line): |
| 118 if flags and flags.Get('is_first_stack_flag'): | 141 if flags and flags.Get('is_first_stack_flag'): |
| 119 flags.TurnOff('is_first_stack_flag') | 142 flags.TurnOff('is_first_stack_flag') |
| 120 return True, 0, CallStackFormatType.DEFAULT, LanguageType.CPP, {} | 143 return StartOfCallStack(0, CallStackFormatType.DEFAULT, |
| 144 LanguageType.CPP, {}) |
| 121 | 145 |
| 122 return True, 1, CallStackFormatType.DEFAULT, LanguageType.CPP, {} | 146 return StartOfCallStack(1, CallStackFormatType.DEFAULT, |
| 147 LanguageType.CPP, {}) |
| 123 | 148 |
| 124 return False, None, None, None, None | 149 return None |
| 125 | 150 |
| 126 | 151 |
| 127 class MsanDetector(CallStackDetector): | 152 class MsanDetector(CallStackDetector): |
| 128 """Detects the start of a memory sanitizer callstack.""" | 153 """Detects the start of a memory sanitizer callstack.""" |
| 129 MSAN_CALLSTACK_START_REGEX = re.compile(r'^==(\d+)== ?([A-Z]+:|\w+Sanitizer)') | 154 MSAN_CALLSTACK_START_REGEX = re.compile(r'^==(\d+)== ?([A-Z]+:|\w+Sanitizer)') |
| 130 MSAN_CREATION_CALLSTACK_START_MARKER = 'Uninitialized value was created by' | 155 MSAN_CREATION_CALLSTACK_START_MARKER = 'Uninitialized value was created by' |
| 131 MSAN_STORAGE_CALLSTACK_START_MARKER = 'Uninitialized value was stored to' | 156 MSAN_STORAGE_CALLSTACK_START_MARKER = 'Uninitialized value was stored to' |
| 132 | 157 |
| 133 def IsStartOfNewCallStack(self, line, flags=None): | 158 def __call__(self, line, flags=None): |
| 134 # Assign the only msan stack priority 0. | 159 # Assign the only msan stack priority 0. |
| 135 if MsanDetector.MSAN_CREATION_CALLSTACK_START_MARKER in line: | 160 if MsanDetector.MSAN_CREATION_CALLSTACK_START_MARKER in line: |
| 136 return True, 0, CallStackFormatType.DEFAULT, LanguageType.CPP, {} | 161 return StartOfCallStack(0, CallStackFormatType.DEFAULT, |
| 162 LanguageType.CPP, {}) |
| 137 if MsanDetector.MSAN_STORAGE_CALLSTACK_START_MARKER in line: | 163 if MsanDetector.MSAN_STORAGE_CALLSTACK_START_MARKER in line: |
| 138 return True, 1, CallStackFormatType.DEFAULT, LanguageType.CPP, {} | 164 return StartOfCallStack(1, CallStackFormatType.DEFAULT, |
| 165 LanguageType.CPP, {}) |
| 139 msan_callstack_start_regex = ( | 166 msan_callstack_start_regex = ( |
| 140 MsanDetector.MSAN_CALLSTACK_START_REGEX.match(line)) | 167 MsanDetector.MSAN_CALLSTACK_START_REGEX.match(line)) |
| 141 if msan_callstack_start_regex: | 168 if msan_callstack_start_regex: |
| 142 return (True, 2, CallStackFormatType.DEFAULT, LanguageType.CPP, | 169 return StartOfCallStack( |
| 143 {'pid': int(msan_callstack_start_regex.group(1).strip())}) | 170 2, CallStackFormatType.DEFAULT, LanguageType.CPP, |
| 171 {'pid': int(msan_callstack_start_regex.group(1).strip())}) |
| 144 | 172 |
| 145 return False, None, None, None, None | 173 return None |
| 146 | 174 |
| 147 | 175 |
| 148 class AsanDetector(CallStackDetector): | 176 class AsanDetector(CallStackDetector): |
| 149 """Detects the start of an address sanitizer callstack.""" | 177 """Detects the start of an address sanitizer callstack.""" |
| 150 ASAN_CRASH_CALLSTACK_START_REGEX1 = re.compile( | 178 ASAN_CRASH_CALLSTACK_START_REGEX1 = re.compile( |
| 151 r'^==(\d+)== ?([A-Z]+:|\w+Sanitizer)') | 179 r'^==(\d+)== ?([A-Z]+:|\w+Sanitizer)') |
| 152 ASAN_CRASH_CALLSTACK_START_REGEX2 = re.compile( | 180 ASAN_CRASH_CALLSTACK_START_REGEX2 = re.compile( |
| 153 r'^(READ|WRITE) of size \d+ at|^backtrace:') | 181 r'^(READ|WRITE) of size \d+ at|^backtrace:') |
| 154 | 182 |
| 155 ASAN_FREED_CALLSTACK_START_PATTERN = ( | 183 ASAN_FREED_CALLSTACK_START_PATTERN = ( |
| 156 r'^freed by thread T\d+ (.* )?here:') | 184 r'^freed by thread T\d+ (.* )?here:') |
| 157 ASAN_ALLOCATION_CALLSTACK_START_PATTERN = ( | 185 ASAN_ALLOCATION_CALLSTACK_START_PATTERN = ( |
| 158 r'^(previously )?allocated by thread T\d+ (.* )?here:') | 186 r'^(previously )?allocated by thread T\d+ (.* )?here:') |
| 159 ASAN_OTHER_CALLSTACK_START_PATTERN = ( | 187 ASAN_OTHER_CALLSTACK_START_PATTERN = ( |
| 160 r'^Thread T\d+ (.* )?created by') | 188 r'^Thread T\d+ (.* )?created by') |
| 161 | 189 |
| 162 ASAN_NON_CRASH_CALLSTACK_START_PATTERN = re.compile( | 190 ASAN_NON_CRASH_CALLSTACK_START_PATTERN = re.compile( |
| 163 '|'.join([ASAN_FREED_CALLSTACK_START_PATTERN, | 191 '|'.join([ASAN_FREED_CALLSTACK_START_PATTERN, |
| 164 ASAN_ALLOCATION_CALLSTACK_START_PATTERN, | 192 ASAN_ALLOCATION_CALLSTACK_START_PATTERN, |
| 165 ASAN_OTHER_CALLSTACK_START_PATTERN])) | 193 ASAN_OTHER_CALLSTACK_START_PATTERN])) |
| 166 | 194 |
| 167 def IsStartOfNewCallStack(self, line, flags=None): | 195 def __call__(self, line, flags=None): |
| 168 asan_crash_callstack_start_regex1_match = ( | 196 asan_crash_callstack_start_regex1_match = ( |
| 169 AsanDetector.ASAN_CRASH_CALLSTACK_START_REGEX1.match(line)) | 197 AsanDetector.ASAN_CRASH_CALLSTACK_START_REGEX1.match(line)) |
| 170 if asan_crash_callstack_start_regex1_match: | 198 if asan_crash_callstack_start_regex1_match: |
| 171 return (True, 0, CallStackFormatType.DEFAULT, LanguageType.CPP, | 199 return StartOfCallStack( |
| 172 {'pid': int( | 200 0, CallStackFormatType.DEFAULT, LanguageType.CPP, |
| 173 asan_crash_callstack_start_regex1_match.group(1).strip())}) | 201 {'pid': int( |
| 202 asan_crash_callstack_start_regex1_match.group(1).strip())}) |
| 174 | 203 |
| 175 # Crash stack gets priority 0. | 204 # Crash stack gets priority 0. |
| 176 if AsanDetector.ASAN_CRASH_CALLSTACK_START_REGEX2.match(line): | 205 if AsanDetector.ASAN_CRASH_CALLSTACK_START_REGEX2.match(line): |
| 177 return True, 0, CallStackFormatType.DEFAULT, LanguageType.CPP, {} | 206 return StartOfCallStack(0, CallStackFormatType.DEFAULT, |
| 207 LanguageType.CPP, {}) |
| 178 | 208 |
| 179 # All other callstack gets priority 1. | 209 # All other callstack gets priority 1. |
| 180 if AsanDetector.ASAN_NON_CRASH_CALLSTACK_START_PATTERN.match(line): | 210 if AsanDetector.ASAN_NON_CRASH_CALLSTACK_START_PATTERN.match(line): |
| 181 return True, 1, CallStackFormatType.DEFAULT, LanguageType.CPP, {} | 211 return StartOfCallStack(1, CallStackFormatType.DEFAULT, |
| 212 LanguageType.CPP, {}) |
| 182 | 213 |
| 183 return False, None, None, None, None | 214 return None |
| 184 | 215 |
| 185 | 216 |
| 186 class ChromeCrashStackDetector(CallStackDetector): | 217 class ChromeCrashStackDetector(CallStackDetector): |
| 187 """Detects the start of an chromecrash(Fracas/Cracas) callstack.""" | 218 """Detects the start of an chromecrash(Fracas/Cracas) callstack.""" |
| 188 CHROME_CRASH_CALLSTACK_START_REGEX = re.compile(r'CRASHED \[(.*) @ 0x(.*)\]') | 219 CHROME_CRASH_CALLSTACK_START_REGEX = re.compile(r'CRASHED \[(.*) @ 0x(.*)\]') |
| 189 JAVA_CALLSTACK_START_REGEX = re.compile(r'\(JAVA\) CRASHED \[(.*) @ 0x(.*)\]') | 220 JAVA_CALLSTACK_START_REGEX = re.compile(r'\(JAVA\) CRASHED \[(.*) @ 0x(.*)\]') |
| 190 | 221 |
| 191 def IsStartOfNewCallStack(self, line, flags=None): | 222 def __call__(self, line, flags=None): |
| 192 if ChromeCrashStackDetector.CHROME_CRASH_CALLSTACK_START_REGEX.match(line): | 223 if ChromeCrashStackDetector.CHROME_CRASH_CALLSTACK_START_REGEX.match(line): |
| 193 # Fracas only provide magic signature stack (crash stack). | 224 # Fracas only provide magic signature stack (crash stack). |
| 194 return True, 0, CallStackFormatType.DEFAULT, LanguageType.CPP, {} | 225 return StartOfCallStack(0, CallStackFormatType.DEFAULT, |
| 226 LanguageType.CPP, {}) |
| 195 | 227 |
| 196 if ChromeCrashStackDetector.JAVA_CALLSTACK_START_REGEX.match(line): | 228 if ChromeCrashStackDetector.JAVA_CALLSTACK_START_REGEX.match(line): |
| 197 return True, 0, CallStackFormatType.DEFAULT, LanguageType.JAVA, {} | 229 return StartOfCallStack(0, CallStackFormatType.DEFAULT, |
| 230 LanguageType.JAVA, {}) |
| 198 | 231 |
| 199 return False, None, None, None, None | 232 return None |
| OLD | NEW |