diff options
Diffstat (limited to 'src/rocksdb/build_tools/error_filter.py')
-rw-r--r-- | src/rocksdb/build_tools/error_filter.py | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/src/rocksdb/build_tools/error_filter.py b/src/rocksdb/build_tools/error_filter.py new file mode 100644 index 000000000..c42df1f91 --- /dev/null +++ b/src/rocksdb/build_tools/error_filter.py @@ -0,0 +1,181 @@ +# Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +# This source code is licensed under both the GPLv2 (found in the +# COPYING file in the root directory) and Apache 2.0 License +# (found in the LICENSE.Apache file in the root directory). + +"""Filter for error messages in test output: + - Receives merged stdout/stderr from test on stdin + - Finds patterns of known error messages for test name (first argument) + - Prints those error messages to stdout +""" + +from __future__ import absolute_import, division, print_function, unicode_literals + +import re +import sys + + +class ErrorParserBase(object): + def parse_error(self, line): + """Parses a line of test output. If it contains an error, returns a + formatted message describing the error; otherwise, returns None. + Subclasses must override this method. + """ + raise NotImplementedError + + +class GTestErrorParser(ErrorParserBase): + """A parser that remembers the last test that began running so it can print + that test's name upon detecting failure. + """ + + _GTEST_NAME_PATTERN = re.compile(r"\[ RUN \] (\S+)$") + # format: '<filename or "unknown file">:<line #>: Failure' + _GTEST_FAIL_PATTERN = re.compile(r"(unknown file|\S+:\d+): Failure$") + + def __init__(self): + self._last_gtest_name = "Unknown test" + + def parse_error(self, line): + gtest_name_match = self._GTEST_NAME_PATTERN.match(line) + if gtest_name_match: + self._last_gtest_name = gtest_name_match.group(1) + return None + gtest_fail_match = self._GTEST_FAIL_PATTERN.match(line) + if gtest_fail_match: + return "%s failed: %s" % (self._last_gtest_name, gtest_fail_match.group(1)) + return None + + +class MatchErrorParser(ErrorParserBase): + """A simple parser that returns the whole line if it matches the pattern.""" + + def __init__(self, pattern): + self._pattern = re.compile(pattern) + + def parse_error(self, line): + if self._pattern.match(line): + return line + return None + + +class CompilerErrorParser(MatchErrorParser): + def __init__(self): + # format (compile error): + # '<filename>:<line #>:<column #>: error: <error msg>' + # format (link error): + # '<filename>:<line #>: error: <error msg>' + # The below regex catches both + super(CompilerErrorParser, self).__init__(r"\S+:\d+: error:") + + +class ScanBuildErrorParser(MatchErrorParser): + def __init__(self): + super(ScanBuildErrorParser, self).__init__(r"scan-build: \d+ bugs found.$") + + +class DbCrashErrorParser(MatchErrorParser): + def __init__(self): + super(DbCrashErrorParser, self).__init__(r"\*\*\*.*\^$|TEST FAILED.") + + +class WriteStressErrorParser(MatchErrorParser): + def __init__(self): + super(WriteStressErrorParser, self).__init__( + r"ERROR: write_stress died with exitcode=\d+" + ) + + +class AsanErrorParser(MatchErrorParser): + def __init__(self): + super(AsanErrorParser, self).__init__(r"==\d+==ERROR: AddressSanitizer:") + + +class UbsanErrorParser(MatchErrorParser): + def __init__(self): + # format: '<filename>:<line #>:<column #>: runtime error: <error msg>' + super(UbsanErrorParser, self).__init__(r"\S+:\d+:\d+: runtime error:") + + +class ValgrindErrorParser(MatchErrorParser): + def __init__(self): + # just grab the summary, valgrind doesn't clearly distinguish errors + # from other log messages. + super(ValgrindErrorParser, self).__init__(r"==\d+== ERROR SUMMARY:") + + +class CompatErrorParser(MatchErrorParser): + def __init__(self): + super(CompatErrorParser, self).__init__(r"==== .*[Ee]rror.* ====$") + + +class TsanErrorParser(MatchErrorParser): + def __init__(self): + super(TsanErrorParser, self).__init__(r"WARNING: ThreadSanitizer:") + + +_TEST_NAME_TO_PARSERS = { + "punit": [CompilerErrorParser, GTestErrorParser], + "unit": [CompilerErrorParser, GTestErrorParser], + "release": [CompilerErrorParser, GTestErrorParser], + "unit_481": [CompilerErrorParser, GTestErrorParser], + "release_481": [CompilerErrorParser, GTestErrorParser], + "clang_unit": [CompilerErrorParser, GTestErrorParser], + "clang_release": [CompilerErrorParser, GTestErrorParser], + "clang_analyze": [CompilerErrorParser, ScanBuildErrorParser], + "code_cov": [CompilerErrorParser, GTestErrorParser], + "unity": [CompilerErrorParser, GTestErrorParser], + "lite": [CompilerErrorParser], + "lite_test": [CompilerErrorParser, GTestErrorParser], + "stress_crash": [CompilerErrorParser, DbCrashErrorParser], + "stress_crash_with_atomic_flush": [CompilerErrorParser, DbCrashErrorParser], + "stress_crash_with_txn": [CompilerErrorParser, DbCrashErrorParser], + "write_stress": [CompilerErrorParser, WriteStressErrorParser], + "asan": [CompilerErrorParser, GTestErrorParser, AsanErrorParser], + "asan_crash": [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser], + "asan_crash_with_atomic_flush": [ + CompilerErrorParser, + AsanErrorParser, + DbCrashErrorParser, + ], + "asan_crash_with_txn": [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser], + "ubsan": [CompilerErrorParser, GTestErrorParser, UbsanErrorParser], + "ubsan_crash": [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser], + "ubsan_crash_with_atomic_flush": [ + CompilerErrorParser, + UbsanErrorParser, + DbCrashErrorParser, + ], + "ubsan_crash_with_txn": [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser], + "valgrind": [CompilerErrorParser, GTestErrorParser, ValgrindErrorParser], + "tsan": [CompilerErrorParser, GTestErrorParser, TsanErrorParser], + "format_compatible": [CompilerErrorParser, CompatErrorParser], + "run_format_compatible": [CompilerErrorParser, CompatErrorParser], + "no_compression": [CompilerErrorParser, GTestErrorParser], + "run_no_compression": [CompilerErrorParser, GTestErrorParser], + "regression": [CompilerErrorParser], + "run_regression": [CompilerErrorParser], +} + + +def main(): + if len(sys.argv) != 2: + return "Usage: %s <test name>" % sys.argv[0] + test_name = sys.argv[1] + if test_name not in _TEST_NAME_TO_PARSERS: + return "Unknown test name: %s" % test_name + + error_parsers = [] + for parser_cls in _TEST_NAME_TO_PARSERS[test_name]: + error_parsers.append(parser_cls()) + + for line in sys.stdin: + line = line.strip() + for error_parser in error_parsers: + error_msg = error_parser.parse_error(line) + if error_msg is not None: + print(error_msg) + + +if __name__ == "__main__": + sys.exit(main()) |