diff options
-rw-r--r-- | tools/valgrind/tsan_analyze.py | 37 | ||||
-rwxr-xr-x | tools/valgrind/valgrind_test.py | 79 |
2 files changed, 82 insertions, 34 deletions
diff --git a/tools/valgrind/tsan_analyze.py b/tools/valgrind/tsan_analyze.py index cb4b403..6da9d65 100644 --- a/tools/valgrind/tsan_analyze.py +++ b/tools/valgrind/tsan_analyze.py @@ -34,7 +34,7 @@ class _StackTraceLine(object): else: return self.raw_line_.replace(self.binary, '%s:%s' % (file, line)) -class TsanAnalyzer: +class TsanAnalyzer(object): ''' Given a set of ThreadSanitizer output files, parse all the errors out of them, unique them and output the results.''' @@ -56,6 +56,8 @@ class TsanAnalyzer: 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" + def __init__(self, source_dir, use_gdb=False): '''Reads in a set of files. @@ -104,6 +106,9 @@ class TsanAnalyzer: break 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()) self.ReadLine() @@ -125,12 +130,12 @@ class TsanAnalyzer: self.used_suppressions[supp_name] = count self.cur_fd_.close() - def Report(self, files, check_sanity=False): - '''Reads in a set of files and prints ThreadSanitizer report. + def GetReports(self, files): + '''Extracts reports from a set of files. - Args: - files: A list of filenames. - check_sanity: if true, search for SANITY_TEST_SUPPRESSIONS + 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 @@ -144,6 +149,17 @@ class TsanAnalyzer: self.ParseReportFile(file) if self._use_gdb: TheAddressTable.ResolveAll() + return [''.join(report_lines) for report_lines in self.reports] + + def Report(self, files, 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 + ''' + + reports = self.GetReports(files) is_sane = False print "-----------------------------------------------------" @@ -157,12 +173,9 @@ class TsanAnalyzer: sys.stdout.flush() retcode = 0 - if len(self.reports) > 0: - logging.error("FAIL! Found %i reports" % len(self.reports)) - for report_list in self.reports: - report = '' - for line in report_list: - report += str(line) + if len(reports) > 0: + logging.error("FAIL! Found %i report(s)" % len(reports)) + for report in reports: logging.error('\n' + report) retcode = -1 diff --git a/tools/valgrind/valgrind_test.py b/tools/valgrind/valgrind_test.py index d1ec87e..6ed5d5c 100755 --- a/tools/valgrind/valgrind_test.py +++ b/tools/valgrind/valgrind_test.py @@ -41,6 +41,11 @@ class BaseTool(object): TMP_DIR = "testing.tmp" def __init__(self): + # If we have a testing.tmp directory, we didn't cleanup last time. + if os.path.exists(BaseTool.TMP_DIR): + shutil.rmtree(BaseTool.TMP_DIR) + os.mkdir(BaseTool.TMP_DIR) + self.option_parser_hooks = [] def ToolName(self): @@ -418,7 +423,7 @@ class ValgrindTool(BaseTool): os.putenv("BROWSER_WRAPPER", indirect_fname) logging.info('export BROWSER_WRAPPER=' + indirect_fname) - def CreateAnalyzer(self, filenames): + def CreateAnalyzer(self): raise NotImplementedError, "This method should be implemented " \ "in the tool-specific subclass" @@ -767,8 +772,33 @@ class DrMemory(BaseTool): # 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__(source_dir, use_gdb) + self.out = open(self.TMP_FILE, "w") + + def Report(self, files, 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): - """First pass: run ThreadSanitizer as usual, but don't clean up logs""" + """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__() @@ -777,13 +807,17 @@ class ThreadSanitizerRV1Mixin(object): def ExtendOptionParser(self, parser): parser.set_defaults(hybrid="yes") - def ParseArgv(self, args): - ret = super(ThreadSanitizerRV1Mixin, self).ParseArgv(args) - self._nocleanup_on_exit = True - return ret + 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): - """Second pass: run the same command line in RaceVerifier mode""" + """RaceVerifier second pass.""" def __init__(self): super(ThreadSanitizerRV2Mixin, self).__init__() @@ -796,11 +830,16 @@ class ThreadSanitizerRV2Mixin(object): def ToolSpecificFlags(self): proc = super(ThreadSanitizerRV2Mixin, self).ToolSpecificFlags() - proc += ['--race-verifier=' + self.TMP_DIR + '/race.log', + 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 @@ -818,6 +857,8 @@ class ThreadSanitizerRV2Windows(ThreadSanitizerRV2Mixin, 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() @@ -830,23 +871,22 @@ class RaceVerifier(object): else: return ThreadSanitizerRV2Posix() - def JoinLogs(self): - """Concatenate all logs from the first pass""" - filenames = glob.glob(BaseTool.TMP_DIR + "/tsan" + ".*") - out_filename = BaseTool.TMP_DIR + "/race.log" - data = [open(fn, "r").read() for fn in filenames] - open(out_filename, "w").write("\n".join(data)) - def Main(self, args, check_sanity): + logging.info("Running a TSan + RaceVerifier test. For more information, " + + "see " + self.MORE_INFO_URL) cmd1 = self.RV1Factory() ret = cmd1.Main(args, check_sanity) # Verify race reports, if there are any. if ret == -1: - self.JoinLogs() + 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) else: - logging.info("No reports, skipping RaceVerifier step") + logging.info("No reports, skipping RaceVerifier second pass") + logging.info("Please see " + self.MORE_INFO_URL + " for more information " + + "on RaceVerifier") return ret @@ -873,11 +913,6 @@ class ToolFactory: platform_name) def RunTool(argv, module): - # If we have a testing.tmp directory, we didn't cleanup last time. - if os.path.exists(BaseTool.TMP_DIR): - shutil.rmtree(BaseTool.TMP_DIR) - os.mkdir(BaseTool.TMP_DIR) - # TODO(timurrrr): customize optparse instead tool_name = "memcheck" args = argv[1:] |