diff options
-rw-r--r-- | tools/valgrind/README | 6 | ||||
-rwxr-xr-x | tools/valgrind/chrome_tests.py | 2 | ||||
-rwxr-xr-x | tools/valgrind/chrome_tests.sh | 34 | ||||
-rwxr-xr-x | tools/valgrind/suppressions.py | 60 | ||||
-rw-r--r-- | tools/valgrind/tsan/README | 5 | ||||
-rwxr-xr-x | tools/valgrind/tsan_analyze.py | 271 | ||||
-rwxr-xr-x | tools/valgrind/valgrind.sh | 8 | ||||
-rw-r--r-- | tools/valgrind/valgrind_test.py | 371 |
8 files changed, 22 insertions, 735 deletions
diff --git a/tools/valgrind/README b/tools/valgrind/README index ad26439..68f793f 100644 --- a/tools/valgrind/README +++ b/tools/valgrind/README @@ -2,3 +2,9 @@ Historically this directory has been a home for Valgrind and ThreadSanitizer. Since then other memory tools used in Chromium started squatting here and the name became confusing. We're replacing tools/valgrind with tools/memory/ new tools should go there. + +Attention: ThreadSanitizer v1 has been retired and files in this dir +should not be used anymore. Please refer to +http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2 +for the instructions on using ThreadSanitizer v2. +Namely, the suppressions now reside in base/debug/tsan_suppressions.cc diff --git a/tools/valgrind/chrome_tests.py b/tools/valgrind/chrome_tests.py index e43c605..eef3175 100755 --- a/tools/valgrind/chrome_tests.py +++ b/tools/valgrind/chrome_tests.py @@ -33,7 +33,7 @@ class ExecutableNotFound(Exception): pass class BadBinary(Exception): pass class ChromeTests: - SLOW_TOOLS = ["memcheck", "tsan", "tsan_rv", "drmemory"] + SLOW_TOOLS = ["memcheck", "drmemory"] LAYOUT_TESTS_DEFAULT_CHUNK_SIZE = 300 def __init__(self, options, args, test): diff --git a/tools/valgrind/chrome_tests.sh b/tools/valgrind/chrome_tests.sh index df5e8e7..479138e 100755 --- a/tools/valgrind/chrome_tests.sh +++ b/tools/valgrind/chrome_tests.sh @@ -9,11 +9,10 @@ export THISDIR=`dirname $0` ARGV_COPY="$@" -# We need to set CHROME_VALGRIND iff using Memcheck or TSan-Valgrind: +# We need to set CHROME_VALGRIND iff using Memcheck: # tools/valgrind/chrome_tests.sh --tool memcheck # or # tools/valgrind/chrome_tests.sh --tool=memcheck -# (same for "--tool=tsan") tool="memcheck" # Default to memcheck. while (( "$#" )) do @@ -35,14 +34,6 @@ case "$tool" in "memcheck") NEEDS_VALGRIND=1 ;; - "tsan" | "tsan_rv") - if [ "`uname -s`" == CYGWIN* ] - then - NEEDS_PIN=1 - else - NEEDS_VALGRIND=1 - fi - ;; "drmemory" | "drmemory_light" | "drmemory_full" | "drmemory_pattern") NEEDS_DRMEMORY=1 ;; @@ -95,28 +86,5 @@ then fi fi -if [ "$NEEDS_PIN" == "1" ] -then - if [ -z "$PIN_COMMAND" ] - then - # Set up PIN_COMMAND to invoke TSan. - TSAN_PATH="$THISDIR/../../third_party/tsan" - TSAN_SFX="$TSAN_PATH/tsan-x86-windows-sfx.exe" - echo "$TSAN_SFX" - if [ ! -f $TSAN_SFX ] - then - echo "Can't find ThreadSanitizer executables." - echo "See http://www.chromium.org/developers/how-tos/using-valgrind/threadsanitizer/threadsanitizer-on-windows" - echo "for the instructions on how to get them." - exit 1 - fi - - chmod +x "$TSAN_SFX" # Cygwin won't run it without +x. - "$TSAN_SFX" -o"$TSAN_PATH"/unpacked -y - export PIN_COMMAND="$TSAN_PATH/unpacked/tsan-x86-windows/tsan.bat" - fi -fi - - PYTHONPATH=$THISDIR/../python/google python \ "$THISDIR/chrome_tests.py" $ARGV_COPY diff --git a/tools/valgrind/suppressions.py b/tools/valgrind/suppressions.py index c655e12..e066bed 100755 --- a/tools/valgrind/suppressions.py +++ b/tools/valgrind/suppressions.py @@ -45,25 +45,15 @@ def GetSuppressions(): supp_filename = JOIN(suppressions_root, "memcheck", "suppressions.txt") vg_common = ReadSuppressionsFromFile(supp_filename) - supp_filename = JOIN(suppressions_root, "tsan", "suppressions.txt") - tsan_common = ReadSuppressionsFromFile(supp_filename) - result['common_suppressions'] = vg_common + tsan_common + result['common_suppressions'] = vg_common supp_filename = JOIN(suppressions_root, "memcheck", "suppressions_linux.txt") vg_linux = ReadSuppressionsFromFile(supp_filename) - supp_filename = JOIN(suppressions_root, "tsan", "suppressions_linux.txt") - tsan_linux = ReadSuppressionsFromFile(supp_filename) - result['linux_suppressions'] = vg_linux + tsan_linux + result['linux_suppressions'] = vg_linux supp_filename = JOIN(suppressions_root, "memcheck", "suppressions_mac.txt") vg_mac = ReadSuppressionsFromFile(supp_filename) - supp_filename = JOIN(suppressions_root, "tsan", "suppressions_mac.txt") - tsan_mac = ReadSuppressionsFromFile(supp_filename) - result['mac_suppressions'] = vg_mac + tsan_mac - - supp_filename = JOIN(suppressions_root, "tsan", "suppressions_win32.txt") - tsan_win = ReadSuppressionsFromFile(supp_filename) - result['win_suppressions'] = tsan_win + result['mac_suppressions'] = vg_mac supp_filename = JOIN(suppressions_root, "drmemory", "suppressions.txt") result['drmem_suppressions'] = ReadSuppressionsFromFile(supp_filename) @@ -147,7 +137,6 @@ def FilenameToTool(filename): """Return the name of the tool that a file is related to, or None. Example mappings: - tools/valgrind/tsan/suppressions.txt -> tsan tools/valgrind/drmemory/suppressions.txt -> drmemory tools/valgrind/drmemory/suppressions_full.txt -> drmemory tools/valgrind/memcheck/suppressions.txt -> memcheck @@ -156,7 +145,7 @@ def FilenameToTool(filename): filename = os.path.abspath(filename) parts = filename.split(os.sep) tool = parts[-2] - if tool in ('drmemory', 'memcheck', 'tsan'): + if tool in ('drmemory', 'memcheck'): return tool return None @@ -166,7 +155,6 @@ def ReadSuppressionsFromFile(filename): tool_to_parser = { "drmemory": ReadDrMemorySuppressions, "memcheck": ReadValgrindStyleSuppressions, - "tsan": ReadValgrindStyleSuppressions, } tool = FilenameToTool(filename) assert tool in tool_to_parser, ( @@ -188,15 +176,14 @@ def ReadSuppressionsFromFile(filename): class ValgrindStyleSuppression(Suppression): """A suppression using the Valgrind syntax. - Most tools, even ones that are not Valgrind-based, use this syntax, ie - TSan, etc. + Most tools, even ones that are not Valgrind-based, use this syntax. Attributes: Same as Suppression. """ def __init__(self, description, type, stack, defined_at): - """Creates a suppression using the Memcheck and TSan, syntax.""" + """Creates a suppression using the Memcheck syntax.""" regex = '{\n.*\n%s\n' % type for line in stack: if line == ELLIPSIS: @@ -296,17 +283,14 @@ def ReadValgrindStyleSuppressions(lines, supp_descriptor): cur_descr = line continue elif not cur_type: - if (not line.startswith("Memcheck:") and - not line.startswith("ThreadSanitizer:")): + if not line.startswith("Memcheck:"): raise SuppressionError( - 'Expected "Memcheck:TYPE" or "ThreadSanitizer:TYPE", ' - 'got "%s"' % line, + 'Expected "Memcheck:TYPE", got "%s"' % line, "%s:%d" % (supp_descriptor, nline)) supp_type = line.split(':')[1] if not supp_type in ["Addr1", "Addr2", "Addr4", "Addr8", "Cond", "Free", "Jump", "Leak", "Overlap", "Param", "Value1", "Value2", "Value4", "Value8", - "Race", "UnlockNonLocked", "InvalidLock", "Unaddressable", "Uninitialized"]: raise SuppressionError('Unknown suppression type "%s"' % supp_type, "%s:%d" % (supp_descriptor, nline)) @@ -335,7 +319,6 @@ def PresubmitCheckSuppressions(supps): if re.search("<.*suppression.name.here>", s.description): # Suppression name line is # <insert_a_suppression_name_here> for Memcheck, - # <Put your suppression name here> for TSan, # name=<insert_a_suppression_name_here> for DrMemory errors.append( SuppressionError( @@ -365,9 +348,6 @@ def PresubmitCheck(input_api, output_api): errors = [] - # TODO(timurrrr): warn on putting suppressions into a wrong file, - # e.g. TSan suppression in a memcheck file. - for f in filenames: try: supps = ReadSuppressionsFromFile(f) @@ -629,17 +609,6 @@ def SelfTest(): fun:expression }""" - test_tsan_stack = """{ - test - ThreadSanitizer:Race - fun:absolutly - fun:brilliant - obj:condition - fun:detection - fun:expression - }""" - - positive_memcheck_suppressions_1 = [ "{\nzzz\nMemcheck:Leak\nfun:absolutly\n}", "{\nzzz\nMemcheck:Leak\nfun:ab*ly\n}", @@ -680,11 +649,6 @@ def SelfTest(): "{\nzzz\nMemcheck:Addr4\n...\nfun:detection\n}", ] - positive_tsan_suppressions = [ - "{\nzzz\nThreadSanitizer:Race\n...\nobj:condition\n}", - "{\nzzz\nThreadSanitizer:Race\nfun:absolutly\n}", - ] - negative_memcheck_suppressions_1 = [ "{\nzzz\nMemcheck:Leak\nfun:abnormal\n}", "{\nzzz\nMemcheck:Leak\nfun:ab*liant\n}", @@ -723,11 +687,6 @@ def SelfTest(): "{\nzzz\nMemcheck:Addr8\nfun:brilliant\n}", ] - negative_tsan_suppressions = [ - "{\nzzz\nThreadSanitizer:Leak\nfun:absolutly\n}", - "{\nzzz\nThreadSanitizer:Race\nfun:brilliant\n}", - ] - TestStack(test_memcheck_stack_1, positive_memcheck_suppressions_1, negative_memcheck_suppressions_1) @@ -740,8 +699,6 @@ def SelfTest(): TestStack(test_memcheck_stack_4, positive_memcheck_suppressions_4, negative_memcheck_suppressions_4) - TestStack(test_tsan_stack, positive_tsan_suppressions, - negative_tsan_suppressions) # TODO(timurrrr): add TestFailPresubmit tests. @@ -932,7 +889,6 @@ def SelfTest(): # Test FilenameToTool. filenames_to_tools = { - "tools/valgrind/tsan/suppressions.txt": "tsan", "tools/valgrind/drmemory/suppressions.txt": "drmemory", "tools/valgrind/drmemory/suppressions_full.txt": "drmemory", "tools/valgrind/memcheck/suppressions.txt": "memcheck", diff --git a/tools/valgrind/tsan/README b/tools/valgrind/tsan/README deleted file mode 100644 index 132b5c6..0000000 --- a/tools/valgrind/tsan/README +++ /dev/null @@ -1,5 +0,0 @@ -Attention: ThreadSanitizer v1 has been retired and files in this dir -should not be used anymore. Please refer to -http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2 -for the instructions on using ThreadSanitizer v2. -Namely, the suppressions now reside in base/debug/tsan_suppressions.cc diff --git a/tools/valgrind/tsan_analyze.py b/tools/valgrind/tsan_analyze.py deleted file mode 100755 index 2c744e2..0000000 --- a/tools/valgrind/tsan_analyze.py +++ /dev/null @@ -1,271 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2011 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. - -# tsan_analyze.py - -''' Given a ThreadSanitizer output file, parses errors and uniques them.''' - -import gdb_helper - -from collections import defaultdict -import hashlib -import logging -import optparse -import os -import re -import subprocess -import sys -import time - -import common - -# Global symbol table (ugh) -TheAddressTable = None - -class _StackTraceLine(object): - def __init__(self, line, address, binary): - self.raw_line_ = line - self.address = address - self.binary = binary - def __str__(self): - global TheAddressTable - file, line = TheAddressTable.GetFileLine(self.binary, self.address) - if (file is None) or (line is None): - return self.raw_line_ - else: - return self.raw_line_.replace(self.binary, '%s:%s' % (file, line)) - -class TsanAnalyzer(object): - ''' Given a set of ThreadSanitizer output files, parse all the errors out of - them, unique them and output the results.''' - - LOAD_LIB_RE = re.compile('--[0-9]+-- ([^(:]*) \((0x[0-9a-f]+)\)') - TSAN_LINE_RE = re.compile('==[0-9]+==\s*[#0-9]+\s*' - '([0-9A-Fa-fx]+):' - '(?:[^ ]* )*' - '([^ :\n]+)' - '') - THREAD_CREATION_STR = ("INFO: T.* " - "(has been created by T.* at this point|is program's main thread)") - - SANITY_TEST_SUPPRESSION = ("ThreadSanitizer sanity test " - "(ToolsSanityTest.DataRace)") - TSAN_RACE_DESCRIPTION = "Possible data race" - TSAN_WARNING_DESCRIPTION = ("Unlocking a non-locked lock" - "|accessing an invalid lock" - "|which did not acquire this lock") - RACE_VERIFIER_LINE = "Confirmed a race|unexpected race" - TSAN_ASSERTION = "Assertion failed: " - - def __init__(self, use_gdb=False): - '''Reads in a set of files.''' - - self._use_gdb = use_gdb - self._cur_testcase = None - - def ReadLine(self): - self.line_ = self.cur_fd_.readline() - self.stack_trace_line_ = None - if not self._use_gdb: - return - global TheAddressTable - match = TsanAnalyzer.LOAD_LIB_RE.match(self.line_) - if match: - binary, ip = match.groups() - TheAddressTable.AddBinaryAt(binary, ip) - return - match = TsanAnalyzer.TSAN_LINE_RE.match(self.line_) - if match: - address, binary_name = match.groups() - stack_trace_line = _StackTraceLine(self.line_, address, binary_name) - TheAddressTable.Add(stack_trace_line.binary, stack_trace_line.address) - self.stack_trace_line_ = stack_trace_line - - def ReadSection(self): - """ Example of a section: - ==4528== WARNING: Possible data race: {{{ - ==4528== T20 (L{}): - ==4528== #0 MyTest::Foo1 - ==4528== #1 MyThread::ThreadBody - ==4528== Concurrent write happened at this point: - ==4528== T19 (L{}): - ==4528== #0 MyTest::Foo2 - ==4528== #1 MyThread::ThreadBody - ==4528== }}} - ------- suppression ------- - { - <Put your suppression name here> - ThreadSanitizer:Race - fun:MyTest::Foo1 - fun:MyThread::ThreadBody - } - ------- end suppression ------- - """ - result = [self.line_] - if re.search("{{{", self.line_): - while not re.search('}}}', self.line_): - self.ReadLine() - if self.stack_trace_line_ is None: - result.append(self.line_) - else: - result.append(self.stack_trace_line_) - self.ReadLine() - if re.match('-+ suppression -+', self.line_): - # We need to calculate the suppression hash and prepend a line like - # "Suppression (error hash=#0123456789ABCDEF#):" so the buildbot can - # extract the suppression snippet. - supp = "" - while not re.match('-+ end suppression -+', self.line_): - self.ReadLine() - supp += self.line_ - self.ReadLine() - if self._cur_testcase: - result.append("The report came from the `%s` test.\n" % \ - self._cur_testcase) - result.append("Suppression (error hash=#%016X#):\n" % \ - (int(hashlib.md5(supp).hexdigest()[:16], 16))) - result.append(" For more info on using suppressions see " - "http://dev.chromium.org/developers/how-tos/using-valgrind/threadsanitizer#TOC-Suppressing-data-races\n") - result.append(supp) - else: - self.ReadLine() - - return result - - def ReadTillTheEnd(self): - result = [self.line_] - while self.line_: - self.ReadLine() - result.append(self.line_) - return result - - def ParseReportFile(self, filename): - '''Parses a report file and returns a list of ThreadSanitizer reports. - - - Args: - filename: report filename. - Returns: - list of (list of (str iff self._use_gdb, _StackTraceLine otherwise)). - ''' - ret = [] - self.cur_fd_ = open(filename, 'r') - - while True: - # Read ThreadSanitizer reports. - self.ReadLine() - if not self.line_: - break - - while True: - tmp = [] - while re.search(TsanAnalyzer.RACE_VERIFIER_LINE, self.line_): - tmp.append(self.line_) - self.ReadLine() - while re.search(TsanAnalyzer.THREAD_CREATION_STR, self.line_): - tmp.extend(self.ReadSection()) - if re.search(TsanAnalyzer.TSAN_RACE_DESCRIPTION, self.line_): - tmp.extend(self.ReadSection()) - ret.append(tmp) # includes RaceVerifier and thread creation stacks - elif (re.search(TsanAnalyzer.TSAN_WARNING_DESCRIPTION, self.line_) and - not common.IsWindows()): # workaround for http://crbug.com/53198 - tmp.extend(self.ReadSection()) - ret.append(tmp) - else: - break - - tmp = [] - if re.search(TsanAnalyzer.TSAN_ASSERTION, self.line_): - tmp.extend(self.ReadTillTheEnd()) - ret.append(tmp) - break - - match = re.search("used_suppression:\s+([0-9]+)\s(.*)", self.line_) - if match: - count, supp_name = match.groups() - count = int(count) - self.used_suppressions[supp_name] += count - self.cur_fd_.close() - return ret - - def GetReports(self, files): - '''Extracts reports from a set of files. - - Reads a set of files and returns a list of all discovered - ThreadSanitizer race reports. As a side effect, populates - self.used_suppressions with appropriate info. - ''' - - global TheAddressTable - if self._use_gdb: - TheAddressTable = gdb_helper.AddressTable() - else: - TheAddressTable = None - reports = [] - self.used_suppressions = defaultdict(int) - for file in files: - reports.extend(self.ParseReportFile(file)) - if self._use_gdb: - TheAddressTable.ResolveAll() - # Make each line of each report a string. - reports = map(lambda(x): map(str, x), reports) - return [''.join(report_lines) for report_lines in reports] - - def Report(self, files, testcase, check_sanity=False): - '''Reads in a set of files and prints ThreadSanitizer report. - - Args: - files: A list of filenames. - check_sanity: if true, search for SANITY_TEST_SUPPRESSIONS - ''' - - # We set up _cur_testcase class-wide variable to avoid passing it through - # about 5 functions. - self._cur_testcase = testcase - reports = self.GetReports(files) - self._cur_testcase = None # just in case, shouldn't be used anymore - - common.PrintUsedSuppressionsList(self.used_suppressions) - - - retcode = 0 - if reports: - sys.stdout.flush() - sys.stderr.flush() - logging.info("FAIL! Found %i report(s)" % len(reports)) - for report in reports: - logging.info('\n' + report) - sys.stdout.flush() - retcode = -1 - - # Report tool's insanity even if there were errors. - if (check_sanity and - TsanAnalyzer.SANITY_TEST_SUPPRESSION not in self.used_suppressions): - logging.error("FAIL! Sanity check failed!") - retcode = -3 - - if retcode != 0: - return retcode - - logging.info("PASS: No reports found") - return 0 - - -def main(): - '''For testing only. The TsanAnalyzer class should be imported instead.''' - parser = optparse.OptionParser("usage: %prog <files to analyze>") - - (options, args) = parser.parse_args() - if not args: - parser.error("no filename specified") - filenames = args - - logging.getLogger().setLevel(logging.INFO) - analyzer = TsanAnalyzer(use_gdb=True) - return analyzer.Report(filenames, None) - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/tools/valgrind/valgrind.sh b/tools/valgrind/valgrind.sh index 52f634a..59b4071 100755 --- a/tools/valgrind/valgrind.sh +++ b/tools/valgrind/valgrind.sh @@ -29,13 +29,6 @@ setup_memcheck() { "--num-callers=30") } -setup_tsan() { - RUN_COMMAND="valgrind-tsan.sh" - IGNORE_FILE="$THISDIR/tsan/ignores.txt" - DEFAULT_TOOL_FLAGS=("--announce-threads" "--pure-happens-before=yes" \ - "--ignore=$IGNORE_FILE") -} - setup_unknown() { echo "Unknown tool \"$TOOL_NAME\" specified, the result is not guaranteed" DEFAULT_TOOL_FLAGS=() @@ -65,7 +58,6 @@ fi case $TOOL_NAME in memcheck*) setup_memcheck "$1";; - tsan*) setup_tsan;; *) setup_unknown;; esac diff --git a/tools/valgrind/valgrind_test.py b/tools/valgrind/valgrind_test.py index 24fa32ac..5576ed3 100644 --- a/tools/valgrind/valgrind_test.py +++ b/tools/valgrind/valgrind_test.py @@ -22,11 +22,9 @@ import common import drmemory_analyze import memcheck_analyze -import tsan_analyze class BaseTool(object): - """Abstract class for running Valgrind-, PIN-based and other dynamic - error detector tools. + """Abstract class for running dynamic error detection tools. Always subclass this and implement ToolCommand with framework- and tool-specific stuff. @@ -107,7 +105,7 @@ class BaseTool(object): # To add framework- or tool-specific flags, please add a hook using # RegisterOptionParserHook in the corresponding subclass. - # See ValgrindTool and ThreadSanitizerBase for examples. + # See ValgrindTool for an example. for hook in self.option_parser_hooks: hook(self, self._parser) @@ -229,10 +227,6 @@ class BaseTool(object): def Run(self, args, module, min_runtime_in_seconds=0): MODULES_TO_SANITY_CHECK = ["base"] - # TODO(timurrrr): this is a temporary workaround for http://crbug.com/47844 - if self.ToolName() == "tsan" and common.IsMac(): - MODULES_TO_SANITY_CHECK = [] - check_sanity = module in MODULES_TO_SANITY_CHECK return self.Main(args, check_sanity, min_runtime_in_seconds) @@ -251,11 +245,6 @@ class ValgrindTool(BaseTool): # Override if tool prefers nonxml output return True - def SelfContained(self): - # Returns true iff the tool is distibuted as a self-contained - # .sh script (e.g. ThreadSanitizer) - return False - def ExtendOptionParser(self, parser): parser.add_option("", "--suppressions", default=[], action="append", @@ -354,14 +343,11 @@ class ValgrindTool(BaseTool): tool_name = self.ToolName() # Construct the valgrind command. - if self.SelfContained(): - proc = ["valgrind-%s.sh" % tool_name] + if 'CHROME_VALGRIND' in os.environ: + path = os.path.join(os.environ['CHROME_VALGRIND'], "bin", "valgrind") else: - if 'CHROME_VALGRIND' in os.environ: - path = os.path.join(os.environ['CHROME_VALGRIND'], "bin", "valgrind") - else: - path = "valgrind" - proc = [path, "--tool=%s" % tool_name] + path = "valgrind" + proc = [path, "--tool=%s" % tool_name] proc += ["--num-callers=%i" % int(self._options.num_callers)] @@ -583,212 +569,6 @@ class Memcheck(ValgrindTool): return ret -class PinTool(BaseTool): - """Abstract class for running PIN tools. - - Always subclass this and implement ToolSpecificFlags() and - ExtendOptionParser() for tool-specific stuff. - """ - def PrepareForTest(self): - pass - - def ToolSpecificFlags(self): - raise NotImplementedError, "This method should be implemented " \ - "in the tool-specific subclass" - - def ToolCommand(self): - """Get the PIN command to run.""" - - # Construct the PIN command. - pin_cmd = os.getenv("PIN_COMMAND") - if not pin_cmd: - raise RuntimeError, "Please set PIN_COMMAND environment variable " \ - "with the path to pin.exe" - proc = pin_cmd.split(" ") - - proc += self.ToolSpecificFlags() - - # The PIN command is constructed. - - # PIN requires -- to separate PIN flags from the executable name. - # self._args begins with the exe to be run. - proc += ["--"] - - proc += self._args - return proc - - -class ThreadSanitizerBase(object): - """ThreadSanitizer - Dynamic data race detector for Linux, Mac and Windows. - - http://code.google.com/p/data-race-test/wiki/ThreadSanitizer - - Since TSan works on both Valgrind (Linux, Mac) and PIN (Windows), we need - to have multiple inheritance - """ - - INFO_MESSAGE="Please see http://dev.chromium.org/developers/how-tos/" \ - "using-valgrind/threadsanitizer for the info on " \ - "ThreadSanitizer" - - def __init__(self): - super(ThreadSanitizerBase, self).__init__() - self.RegisterOptionParserHook(ThreadSanitizerBase.ExtendOptionParser) - - def ToolName(self): - return "tsan" - - def UseXML(self): - return False - - def SelfContained(self): - return True - - def ExtendOptionParser(self, parser): - parser.add_option("", "--hybrid", default="no", - dest="hybrid", - help="Finds more data races, may give false positive " - "reports unless the code is annotated") - parser.add_option("", "--announce-threads", default="yes", - dest="announce_threads", - help="Show the the stack traces of thread creation") - parser.add_option("", "--free-is-write", default="no", - dest="free_is_write", - help="Treat free()/operator delete as memory write. " - "This helps finding more data races, but (currently) " - "this may give false positive reports on std::string " - "internals, see http://code.google.com/p/data-race-test" - "/issues/detail?id=40") - - def EvalBoolFlag(self, flag_value): - if (flag_value in ["1", "true", "yes"]): - return True - elif (flag_value in ["0", "false", "no"]): - return False - raise RuntimeError, "Can't parse flag value (%s)" % flag_value - - def ToolSpecificFlags(self): - ret = [] - - ignore_files = ["ignores.txt"] - for platform_suffix in common.PlatformNames(): - ignore_files.append("ignores_%s.txt" % platform_suffix) - for ignore_file in ignore_files: - fullname = os.path.join(self._source_dir, - "tools", "valgrind", "tsan", ignore_file) - if os.path.exists(fullname): - fullname = common.NormalizeWindowsPath(fullname) - ret += ["--ignore=%s" % fullname] - - # This should shorten filepaths for local builds. - ret += ["--file-prefix-to-cut=%s/" % self._source_dir] - - # This should shorten filepaths on bots. - ret += ["--file-prefix-to-cut=build/src/"] - ret += ["--file-prefix-to-cut=out/Release/../../"] - - # This should shorten filepaths for functions intercepted in TSan. - ret += ["--file-prefix-to-cut=scripts/tsan/tsan/"] - ret += ["--file-prefix-to-cut=src/tsan/tsan/"] - - ret += ["--gen-suppressions=true"] - - if self.EvalBoolFlag(self._options.hybrid): - ret += ["--hybrid=yes"] # "no" is the default value for TSAN - - if self.EvalBoolFlag(self._options.announce_threads): - ret += ["--announce-threads"] - - if self.EvalBoolFlag(self._options.free_is_write): - ret += ["--free-is-write=yes"] - else: - ret += ["--free-is-write=no"] - - - # --show-pc flag is needed for parsing the error logs on Darwin. - if platform_suffix == 'mac': - ret += ["--show-pc=yes"] - ret += ["--show-pid=no"] - - boring_callers = common.BoringCallers(mangled=False, use_re_wildcards=False) - # TODO(timurrrr): In fact, we want "starting from .." instead of "below .." - for bc in boring_callers: - ret += ["--cut_stack_below=%s" % bc] - - return ret - - -class ThreadSanitizerPosix(ThreadSanitizerBase, ValgrindTool): - def ToolSpecificFlags(self): - proc = ThreadSanitizerBase.ToolSpecificFlags(self) - # The -v flag is needed for printing the list of used suppressions and - # obtaining addresses for loaded shared libraries on Mac. - proc += ["-v"] - return proc - - def CreateAnalyzer(self): - use_gdb = common.IsMac() - return tsan_analyze.TsanAnalyzer(use_gdb) - - def Analyze(self, check_sanity=False): - ret = self.GetAnalyzeResults(check_sanity) - - if ret != 0: - logging.info(self.INFO_MESSAGE) - return ret - - -class ThreadSanitizerWindows(ThreadSanitizerBase, PinTool): - - def __init__(self): - super(ThreadSanitizerWindows, self).__init__() - self.RegisterOptionParserHook(ThreadSanitizerWindows.ExtendOptionParser) - - def ExtendOptionParser(self, parser): - parser.add_option("", "--suppressions", default=[], - action="append", - help="path to TSan suppression file") - - - def ToolSpecificFlags(self): - add_env = { - "CHROME_ALLOCATOR" : "WINHEAP", - } - for k,v in add_env.iteritems(): - logging.info("export %s=%s", k, v) - os.putenv(k, v) - - proc = ThreadSanitizerBase.ToolSpecificFlags(self) - # On PIN, ThreadSanitizer has its own suppression mechanism - # and --log-file flag which work exactly on Valgrind. - suppression_count = 0 - for suppression_file in self._options.suppressions: - if os.path.exists(suppression_file): - suppression_count += 1 - suppression_file = common.NormalizeWindowsPath(suppression_file) - proc += ["--suppressions=%s" % suppression_file] - - if not suppression_count: - logging.warning("WARNING: NOT USING SUPPRESSIONS!") - - logfilename = self.log_dir + "/tsan.%p" - proc += ["--log-file=" + common.NormalizeWindowsPath(logfilename)] - - # TODO(timurrrr): Add flags for Valgrind trace children analog when we - # start running complex tests (e.g. UI) under TSan/Win. - - return proc - - def Analyze(self, check_sanity=False): - filenames = glob.glob(self.log_dir + "/tsan.*") - analyzer = tsan_analyze.TsanAnalyzer() - ret = analyzer.Report(filenames, None, check_sanity) - if ret != 0: - logging.info(self.INFO_MESSAGE) - return ret - - class DrMemory(BaseTool): """Dr.Memory Dynamic memory error detector for Windows. @@ -963,7 +743,6 @@ class DrMemory(BaseTool): proc += ["--"] if self._options.indirect or self._options.indirect_webkit_layout: - # TODO(timurrrr): reuse for TSan on Windows wrapper_path = os.path.join(self._source_dir, "tools", "valgrind", "browser_wrapper_win.py") wrapper = " ".join(["python", wrapper_path] + proc) @@ -1034,146 +813,10 @@ class DrMemory(BaseTool): return ret -# RaceVerifier support. See -# http://code.google.com/p/data-race-test/wiki/RaceVerifier for more details. -class ThreadSanitizerRV1Analyzer(tsan_analyze.TsanAnalyzer): - """ TsanAnalyzer that saves race reports to a file. """ - - TMP_FILE = "rvlog.tmp" - - def __init__(self, source_dir, use_gdb): - super(ThreadSanitizerRV1Analyzer, self).__init__(use_gdb) - self.out = open(self.TMP_FILE, "w") - - def Report(self, files, testcase, check_sanity=False): - reports = self.GetReports(files) - for report in reports: - print >>self.out, report - if len(reports) > 0: - logging.info("RaceVerifier pass 1 of 2, found %i reports" % len(reports)) - return -1 - return 0 - - def CloseOutputFile(self): - self.out.close() - - -class ThreadSanitizerRV1Mixin(object): - """RaceVerifier first pass. - - Runs ThreadSanitizer as usual, but hides race reports and collects them in a - temporary file""" - - def __init__(self): - super(ThreadSanitizerRV1Mixin, self).__init__() - self.RegisterOptionParserHook(ThreadSanitizerRV1Mixin.ExtendOptionParser) - - def ExtendOptionParser(self, parser): - parser.set_defaults(hybrid="yes") - - def CreateAnalyzer(self): - use_gdb = common.IsMac() - self.analyzer = ThreadSanitizerRV1Analyzer(self._source_dir, use_gdb) - return self.analyzer - - def Cleanup(self): - super(ThreadSanitizerRV1Mixin, self).Cleanup() - self.analyzer.CloseOutputFile() - - -class ThreadSanitizerRV2Mixin(object): - """RaceVerifier second pass.""" - - def __init__(self): - super(ThreadSanitizerRV2Mixin, self).__init__() - self.RegisterOptionParserHook(ThreadSanitizerRV2Mixin.ExtendOptionParser) - - def ExtendOptionParser(self, parser): - parser.add_option("", "--race-verifier-sleep-ms", - dest="race_verifier_sleep_ms", default=10, - help="duration of RaceVerifier delays") - - def ToolSpecificFlags(self): - proc = super(ThreadSanitizerRV2Mixin, self).ToolSpecificFlags() - proc += ['--race-verifier=%s' % ThreadSanitizerRV1Analyzer.TMP_FILE, - '--race-verifier-sleep-ms=%d' % - int(self._options.race_verifier_sleep_ms)] - return proc - - def Cleanup(self): - super(ThreadSanitizerRV2Mixin, self).Cleanup() - os.unlink(ThreadSanitizerRV1Analyzer.TMP_FILE) - - -class ThreadSanitizerRV1Posix(ThreadSanitizerRV1Mixin, ThreadSanitizerPosix): - pass - - -class ThreadSanitizerRV2Posix(ThreadSanitizerRV2Mixin, ThreadSanitizerPosix): - pass - - -class ThreadSanitizerRV1Windows(ThreadSanitizerRV1Mixin, - ThreadSanitizerWindows): - pass - - -class ThreadSanitizerRV2Windows(ThreadSanitizerRV2Mixin, - ThreadSanitizerWindows): - pass - - -class RaceVerifier(object): - """Runs tests under RaceVerifier/Valgrind.""" - - MORE_INFO_URL = "http://code.google.com/p/data-race-test/wiki/RaceVerifier" - - def RV1Factory(self): - if common.IsWindows(): - return ThreadSanitizerRV1Windows() - else: - return ThreadSanitizerRV1Posix() - - def RV2Factory(self): - if common.IsWindows(): - return ThreadSanitizerRV2Windows() - else: - return ThreadSanitizerRV2Posix() - - def ToolName(self): - return "tsan" - - def Main(self, args, check_sanity, min_runtime_in_seconds): - logging.info("Running a TSan + RaceVerifier test. For more information, " + - "see " + self.MORE_INFO_URL) - cmd1 = self.RV1Factory() - ret = cmd1.Main(args, check_sanity, min_runtime_in_seconds) - # Verify race reports, if there are any. - if ret == -1: - logging.info("Starting pass 2 of 2. Running the same binary in " + - "RaceVerifier mode to confirm possible race reports.") - logging.info("For more information, see " + self.MORE_INFO_URL) - cmd2 = self.RV2Factory() - ret = cmd2.Main(args, check_sanity, min_runtime_in_seconds) - else: - logging.info("No reports, skipping RaceVerifier second pass") - logging.info("Please see " + self.MORE_INFO_URL + " for more information " + - "on RaceVerifier") - return ret - - def Run(self, args, module, min_runtime_in_seconds=0): - return self.Main(args, False, min_runtime_in_seconds) - - class ToolFactory: def Create(self, tool_name): if tool_name == "memcheck": return Memcheck() - if tool_name == "tsan": - if common.IsWindows(): - return ThreadSanitizerWindows() - else: - return ThreadSanitizerPosix() if tool_name == "drmemory" or tool_name == "drmemory_light": # TODO(timurrrr): remove support for "drmemory" when buildbots are # switched to drmemory_light OR make drmemory==drmemory_full the default @@ -1183,8 +826,6 @@ class ToolFactory: return DrMemory(True, False) if tool_name == "drmemory_pattern": return DrMemory(False, True) - if tool_name == "tsan_rv": - return RaceVerifier() try: platform_name = common.PlatformNames()[0] except common.NotImplementedError: |