blob: 668b9c86dad1f1e911cd5eb267058109080fc44f [file]
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from collections import namedtuple
import re
from analysis.type_enums import CallStackFormatType
from analysis.type_enums import LanguageType
from analysis.type_enums import SanitizerType
class StartOfCallStack(
namedtuple('StartOfCallStack',
['priority', 'format_type', 'language_type', 'metadata'])):
"""Represents the start of a new callstack.
Properties:
priority (int): Priority of the new callstack.
format_type (CallStackFormatType): The format of the new callstack.
language_type (LanguageType): The language of the new callstack.
metadata (dict): Dict of metadata for the new callstack, e.g. pid of the
stack.
"""
__slots__ = ()
def __new__(cls, priority, format_type, language_type, metadata=None):
return super(cls, StartOfCallStack).__new__(cls, priority, format_type,
language_type, metadata or {})
def __str__(self): # pragma: no cover
return ('%s(priority = %d, format_type = %d, '
'language_type = %d, metadata = %s)' % (self.__class__.__name__,
self.priority,
self.format_type,
self.language_type,
self.metadata))
class CallStackDetector(object):
"""Class for detecting the start of a particular sort of CallStack."""
def __call__(self, line, flags=None):
"""Determines whether a line is the start of a new callstack or not.
Args:
line (str): The line to be checked.
flags (FlagManager): manager for keeping track of parsing flags.
Returns:
A ``StartOfCallStack`` or None if no callstack found.
"""
raise NotImplementedError()
class AndroidJobDetector(CallStackDetector):
"""Detects the start of an android job callstack."""
JAVA_LANG_CALLSTACK_START_PATTERN = r'^java\.'
JAVA_ORG_GHROMIUM_CALLSTACK_START_PATTERN = r'^org\.chromium\.'
JAVA_CAUSED_BY_CALLSTACK_START_PATTERN = r'^Caused by:'
JAVA_ANDROID_CALLSTACK_START_PATTERN = r'^(com\.google\.)?android\.'
JAVA_CALLSTACK_START_REGEX = re.compile(
'|'.join([JAVA_LANG_CALLSTACK_START_PATTERN,
JAVA_ORG_GHROMIUM_CALLSTACK_START_PATTERN,
JAVA_CAUSED_BY_CALLSTACK_START_PATTERN,
JAVA_ANDROID_CALLSTACK_START_PATTERN]))
def __call__(self, line, flags=None):
if AndroidJobDetector.JAVA_CALLSTACK_START_REGEX.match(line):
# Only assign the highest priority to fatal exception stack or segv
# stack.
if flags and flags.Get('java_main_stack_flag'):
flags.TurnOff('java_main_stack_flag')
return StartOfCallStack(0, CallStackFormatType.JAVA,
LanguageType.JAVA, {})
return StartOfCallStack(1, CallStackFormatType.JAVA,
LanguageType.JAVA, {})
return None
class SyzyasanDetector(CallStackDetector):
"""Detects the start of a syzyasn callstack."""
SYZYASAN_CRASH_CALLSTACK_START_REGEX = re.compile(r'^Crash stack:$')
SYZYASAN_NON_CRASH_CALLSTACK_START_REGEX = re.compile(
r'^(Allocation|Free) stack:$')
def __call__(self, line, flags=None):
# In syzyasan build, new stack starts with 'crash stack:',
# 'freed stack:', etc.
if SyzyasanDetector.SYZYASAN_CRASH_CALLSTACK_START_REGEX.match(line):
return StartOfCallStack(0, CallStackFormatType.SYZYASAN,
LanguageType.CPP, {})
# Other callstacks all get priority 1.
if SyzyasanDetector.SYZYASAN_NON_CRASH_CALLSTACK_START_REGEX.match(line):
return StartOfCallStack(1, CallStackFormatType.SYZYASAN,
LanguageType.CPP, {})
return None
class TsanDetector(CallStackDetector):
"""Detects the start of a thread sanitizer callstack."""
TSAN_CRASH_CALLSTACK_START_PATTERN1 = r'^(Read|Write) of size \d+'
TSAN_CRASH_CALLSTACK_START_PATTERN2 = r'.*(ERROR|WARNING): ?ThreadSanitizer'
TSAN_ALLOCATION_CALLSTACK_START_PATTERN = (
r'^Previous (write|read) of size \d+')
TSAN_LOCATION_CALLSTACK_START_PATTERN = (
r'^Location is heap block of size \d+')
TSAN_CRASH_CALLSTACK_START_REGEX = re.compile(
'|'.join([TSAN_CRASH_CALLSTACK_START_PATTERN1,
TSAN_CRASH_CALLSTACK_START_PATTERN2]))
TSAN_NON_CRASH_CALLSTACK_START_REGEX = re.compile(
'|'.join([TSAN_ALLOCATION_CALLSTACK_START_PATTERN,
TSAN_LOCATION_CALLSTACK_START_PATTERN]))
def __call__(self, line, flags=None):
# Crash stack gets priority 0.
if TsanDetector.TSAN_CRASH_CALLSTACK_START_REGEX.match(line):
return StartOfCallStack(0, CallStackFormatType.DEFAULT,
LanguageType.CPP, {})
# All other stacks get priority 1.
if TsanDetector.TSAN_NON_CRASH_CALLSTACK_START_REGEX.match(line):
return StartOfCallStack(1, CallStackFormatType.DEFAULT,
LanguageType.CPP, {})
return None
class UbsanDetector(CallStackDetector):
"""Detects the start of an undefined-behavior callstack."""
UBSAN_CALLSTACK_START_REGEX = re.compile(r'.*: runtime error: .*')
def __call__(self, line, flags=None):
if UbsanDetector.UBSAN_CALLSTACK_START_REGEX.match(line):
if flags and flags.Get('is_first_stack_flag'):
flags.TurnOff('is_first_stack_flag')
return StartOfCallStack(0, CallStackFormatType.DEFAULT,
LanguageType.CPP, {})
return StartOfCallStack(1, CallStackFormatType.DEFAULT,
LanguageType.CPP, {})
return None
class MsanDetector(CallStackDetector):
"""Detects the start of a memory sanitizer callstack."""
MSAN_CALLSTACK_START_REGEX = re.compile(
r'.*(ERROR|WARNING): ?MemorySanitizer')
MSAN_CREATION_CALLSTACK_START_MARKER = 'Uninitialized value was created by'
MSAN_STORAGE_CALLSTACK_START_MARKER = 'Uninitialized value was stored to'
def __call__(self, line, flags=None):
# Assign the only msan stack priority 0.
if MsanDetector.MSAN_CREATION_CALLSTACK_START_MARKER in line:
return StartOfCallStack(0, CallStackFormatType.DEFAULT,
LanguageType.CPP, {})
if MsanDetector.MSAN_STORAGE_CALLSTACK_START_MARKER in line:
return StartOfCallStack(1, CallStackFormatType.DEFAULT,
LanguageType.CPP, {})
if MsanDetector.MSAN_CALLSTACK_START_REGEX.match(line):
return StartOfCallStack(
2, CallStackFormatType.DEFAULT, LanguageType.CPP, {})
return None
class AsanDetector(CallStackDetector):
"""Detects the start of an address sanitizer callstack."""
ASAN_CRASH_CALLSTACK_START_REGEX1 = re.compile(
r'.*(ERROR|WARNING): ?AddressSanitizer')
ASAN_CRASH_CALLSTACK_START_REGEX2 = re.compile(
r'^(READ|WRITE) of size \d+ at|^backtrace:')
ASAN_FREED_CALLSTACK_START_PATTERN = (
r'^freed by thread T\d+ (.* )?here:')
ASAN_ALLOCATION_CALLSTACK_START_PATTERN = (
r'^(previously )?allocated by thread T\d+ (.* )?here:')
ASAN_OTHER_CALLSTACK_START_PATTERN = (
r'^Thread T\d+ (.* )?created by')
ASAN_NON_CRASH_CALLSTACK_START_PATTERN = re.compile(
'|'.join([ASAN_FREED_CALLSTACK_START_PATTERN,
ASAN_ALLOCATION_CALLSTACK_START_PATTERN,
ASAN_OTHER_CALLSTACK_START_PATTERN]))
def __call__(self, line, flags=None):
if AsanDetector.ASAN_CRASH_CALLSTACK_START_REGEX1.match(line):
return StartOfCallStack(
0, CallStackFormatType.DEFAULT, LanguageType.CPP, {})
# Crash stack gets priority 0.
if AsanDetector.ASAN_CRASH_CALLSTACK_START_REGEX2.match(line):
return StartOfCallStack(0, CallStackFormatType.DEFAULT,
LanguageType.CPP, {})
# All other callstack gets priority 1.
if AsanDetector.ASAN_NON_CRASH_CALLSTACK_START_PATTERN.match(line):
return StartOfCallStack(1, CallStackFormatType.DEFAULT,
LanguageType.CPP, {})
return None
class LibFuzzerDetector(CallStackDetector):
"""Detects the start of a libFuzzer stack."""
LIBFUZZER_STACK_START_REGEX = re.compile(r'.*(ERROR|WARNING): ?libFuzzer')
def __call__(self, line, flags=None):
if LibFuzzerDetector.LIBFUZZER_STACK_START_REGEX.match(line):
return StartOfCallStack(
0, CallStackFormatType.DEFAULT, LanguageType.CPP, {})
class DirectLeakDetector(CallStackDetector):
"""Detects the ``Direct-leak`` type of crash for clusterfuzz callstack."""
DIRECT_LEAK_REGEX = re.compile(r'Direct leak of .*')
def __call__(self, line, flags=None):
if DirectLeakDetector.DIRECT_LEAK_REGEX.match(line):
return StartOfCallStack(0, CallStackFormatType.DEFAULT,
LanguageType.CPP, {})
return None
class IndirectLeakDetector(CallStackDetector):
"""Detects the ``Indirect-leak`` type of crash for clusterfuzz callstack."""
INDIRECT_LEAK_REGEX = re.compile(r'Indirect leak of .*')
def __call__(self, line, flags=None):
if IndirectLeakDetector.INDIRECT_LEAK_REGEX.match(line):
return StartOfCallStack(0, CallStackFormatType.DEFAULT,
LanguageType.CPP, {})
return None
class ChromeCrashStackDetector(CallStackDetector):
"""Detects the start of an chromecrash(Fracas/Cracas) callstack."""
CHROME_CRASH_CALLSTACK_START_REGEX = re.compile(r'CRASHED \[(.*) @ 0x(.*)\]')
JAVA_CALLSTACK_START_REGEX = re.compile(r'\(JAVA\) CRASHED \[(.*) @ 0x(.*)\]')
def __call__(self, line, flags=None):
if ChromeCrashStackDetector.CHROME_CRASH_CALLSTACK_START_REGEX.match(line):
# Fracas only provide magic signature stack (crash stack).
return StartOfCallStack(0, CallStackFormatType.DEFAULT,
LanguageType.CPP, {})
if ChromeCrashStackDetector.JAVA_CALLSTACK_START_REGEX.match(line):
return StartOfCallStack(0, CallStackFormatType.DEFAULT,
LanguageType.JAVA, {})
return None
class GeneralSanitizerDetector(CallStackDetector):
"""A fallback detector which returns StartOfCallStack for general sanitizer.
==28956==ERROR: UndefinedBehaviorSanitizer:
#0 0x9feb4e in func0 media/filters/source_buffer_range.cc:129:43
#1 0xa0e702 in func1 media/filters/source_buffer_range_by_pts.cc:710:5
#2 0xa0d72b in func2 media/filters/source_buffer_range_by_pts.cc:225:3
"""
GENERAL_SANITIZER_REGEX = re.compile(r'.*(ERROR|WARNING): .*Sanitizer')
def __call__(self, line, flags=None):
if GeneralSanitizerDetector.GENERAL_SANITIZER_REGEX.match(line):
return StartOfCallStack(2, CallStackFormatType.DEFAULT,
LanguageType.CPP, {})
return None
class CheckFailureDetector(CallStackDetector):
"""A detector which detects check failure stack.
[FATAL:Vector.h(1829)] Check failed: position <= size() (1 vs. 0)
#0 0x6175963d in logging::LogMessage::~LogMessage() base/logging.cc:581:29
#1 0x6c79e803 in void WTF::Vector third_party/WebKit/Source/r.h:1829:3
#2 0x6c79e5ff in blink::AXNodeObject third_party/WebKit/S.cc:17
#3 0x6c79e21d in blink() third_party/WebKit/Source/t.cpp:2112:7
"""
CHECK_FAILURE_REGEX = re.compile(
r'\s*[[][^]]*[:]([^](]*).*[]].*Check failed[:]\s*(.*)')
def __call__(self, line, flags=None):
if CheckFailureDetector.CHECK_FAILURE_REGEX.match(line):
return StartOfCallStack(0, CallStackFormatType.DEFAULT,
LanguageType.CPP, {})
return None