fork of https://github.com/rust-rocksdb/rust-rocksdb for nextgraph
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
6.5 KiB
181 lines
6.5 KiB
# 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())
|
|
|