summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/valgrind/tsan_analyze.py37
-rwxr-xr-xtools/valgrind/valgrind_test.py79
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:]