Index: tools/valgrind/valgrind_test.py |
diff --git a/tools/valgrind/valgrind_test.py b/tools/valgrind/valgrind_test.py |
index 24fa32acb49850935a7ccbdd846b9fb08de821a7..5576ed3177655d43038fc6e24609227e95610000 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: |