OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Runs an exe through Valgrind and puts the intermediate files in a | 5 """Runs an exe through Valgrind and puts the intermediate files in a |
6 directory. | 6 directory. |
7 """ | 7 """ |
8 | 8 |
9 import datetime | 9 import datetime |
10 import glob | 10 import glob |
11 import logging | 11 import logging |
12 import optparse | 12 import optparse |
13 import os | 13 import os |
14 import re | 14 import re |
15 import shutil | 15 import shutil |
16 import stat | 16 import stat |
17 import subprocess | 17 import subprocess |
18 import sys | 18 import sys |
19 import tempfile | 19 import tempfile |
20 | 20 |
21 import common | 21 import common |
22 | 22 |
23 import drmemory_analyze | 23 import drmemory_analyze |
24 import memcheck_analyze | 24 import memcheck_analyze |
25 import tsan_analyze | |
26 | 25 |
27 class BaseTool(object): | 26 class BaseTool(object): |
28 """Abstract class for running Valgrind-, PIN-based and other dynamic | 27 """Abstract class for running dynamic error detection tools. |
29 error detector tools. | |
30 | 28 |
31 Always subclass this and implement ToolCommand with framework- and | 29 Always subclass this and implement ToolCommand with framework- and |
32 tool-specific stuff. | 30 tool-specific stuff. |
33 """ | 31 """ |
34 | 32 |
35 def __init__(self): | 33 def __init__(self): |
36 temp_parent_dir = None | 34 temp_parent_dir = None |
37 self.log_parent_dir = "" | 35 self.log_parent_dir = "" |
38 if common.IsWindows(): | 36 if common.IsWindows(): |
39 # gpu process on Windows Vista+ runs at Low Integrity and can only | 37 # gpu process on Windows Vista+ runs at Low Integrity and can only |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 self._parser.add_option("", "--keep_logs", action="store_true", | 98 self._parser.add_option("", "--keep_logs", action="store_true", |
101 default=False, | 99 default=False, |
102 help="store memory tool logs in the <tool>.logs " | 100 help="store memory tool logs in the <tool>.logs " |
103 "directory instead of /tmp.\nThis can be " | 101 "directory instead of /tmp.\nThis can be " |
104 "useful for tool developers/maintainers.\n" | 102 "useful for tool developers/maintainers.\n" |
105 "Please note that the <tool>.logs directory " | 103 "Please note that the <tool>.logs directory " |
106 "will be clobbered on tool startup.") | 104 "will be clobbered on tool startup.") |
107 | 105 |
108 # To add framework- or tool-specific flags, please add a hook using | 106 # To add framework- or tool-specific flags, please add a hook using |
109 # RegisterOptionParserHook in the corresponding subclass. | 107 # RegisterOptionParserHook in the corresponding subclass. |
110 # See ValgrindTool and ThreadSanitizerBase for examples. | 108 # See ValgrindTool for an example. |
111 for hook in self.option_parser_hooks: | 109 for hook in self.option_parser_hooks: |
112 hook(self, self._parser) | 110 hook(self, self._parser) |
113 | 111 |
114 def ParseArgv(self, args): | 112 def ParseArgv(self, args): |
115 self.CreateOptionParser() | 113 self.CreateOptionParser() |
116 | 114 |
117 # self._tool_flags will store those tool flags which we don't parse | 115 # self._tool_flags will store those tool flags which we don't parse |
118 # manually in this script. | 116 # manually in this script. |
119 self._tool_flags = [] | 117 self._tool_flags = [] |
120 known_args = [] | 118 known_args = [] |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
222 runtime_in_seconds < min_runtime_in_seconds): | 220 runtime_in_seconds < min_runtime_in_seconds): |
223 logging.error("Layout tests finished too quickly. " | 221 logging.error("Layout tests finished too quickly. " |
224 "It should have taken at least %d seconds. " | 222 "It should have taken at least %d seconds. " |
225 "Something went wrong?" % min_runtime_in_seconds) | 223 "Something went wrong?" % min_runtime_in_seconds) |
226 retcode = -1 | 224 retcode = -1 |
227 return retcode | 225 return retcode |
228 | 226 |
229 def Run(self, args, module, min_runtime_in_seconds=0): | 227 def Run(self, args, module, min_runtime_in_seconds=0): |
230 MODULES_TO_SANITY_CHECK = ["base"] | 228 MODULES_TO_SANITY_CHECK = ["base"] |
231 | 229 |
232 # TODO(timurrrr): this is a temporary workaround for http://crbug.com/47844 | |
233 if self.ToolName() == "tsan" and common.IsMac(): | |
234 MODULES_TO_SANITY_CHECK = [] | |
235 | |
236 check_sanity = module in MODULES_TO_SANITY_CHECK | 230 check_sanity = module in MODULES_TO_SANITY_CHECK |
237 return self.Main(args, check_sanity, min_runtime_in_seconds) | 231 return self.Main(args, check_sanity, min_runtime_in_seconds) |
238 | 232 |
239 | 233 |
240 class ValgrindTool(BaseTool): | 234 class ValgrindTool(BaseTool): |
241 """Abstract class for running Valgrind tools. | 235 """Abstract class for running Valgrind tools. |
242 | 236 |
243 Always subclass this and implement ToolSpecificFlags() and | 237 Always subclass this and implement ToolSpecificFlags() and |
244 ExtendOptionParser() for tool-specific stuff. | 238 ExtendOptionParser() for tool-specific stuff. |
245 """ | 239 """ |
246 def __init__(self): | 240 def __init__(self): |
247 super(ValgrindTool, self).__init__() | 241 super(ValgrindTool, self).__init__() |
248 self.RegisterOptionParserHook(ValgrindTool.ExtendOptionParser) | 242 self.RegisterOptionParserHook(ValgrindTool.ExtendOptionParser) |
249 | 243 |
250 def UseXML(self): | 244 def UseXML(self): |
251 # Override if tool prefers nonxml output | 245 # Override if tool prefers nonxml output |
252 return True | 246 return True |
253 | 247 |
254 def SelfContained(self): | |
255 # Returns true iff the tool is distibuted as a self-contained | |
256 # .sh script (e.g. ThreadSanitizer) | |
257 return False | |
258 | |
259 def ExtendOptionParser(self, parser): | 248 def ExtendOptionParser(self, parser): |
260 parser.add_option("", "--suppressions", default=[], | 249 parser.add_option("", "--suppressions", default=[], |
261 action="append", | 250 action="append", |
262 help="path to a valgrind suppression file") | 251 help="path to a valgrind suppression file") |
263 parser.add_option("", "--indirect", action="store_true", | 252 parser.add_option("", "--indirect", action="store_true", |
264 default=False, | 253 default=False, |
265 help="set BROWSER_WRAPPER rather than " | 254 help="set BROWSER_WRAPPER rather than " |
266 "running valgrind directly") | 255 "running valgrind directly") |
267 parser.add_option("", "--indirect_webkit_layout", action="store_true", | 256 parser.add_option("", "--indirect_webkit_layout", action="store_true", |
268 default=False, | 257 default=False, |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
347 logging.info("No real .dSYM for test_command. Line numbers will " | 336 logging.info("No real .dSYM for test_command. Line numbers will " |
348 "not be shown. Either tell xcode to generate .dSYM " | 337 "not be shown. Either tell xcode to generate .dSYM " |
349 "file, or use --generate_dsym option to this tool.") | 338 "file, or use --generate_dsym option to this tool.") |
350 | 339 |
351 def ToolCommand(self): | 340 def ToolCommand(self): |
352 """Get the valgrind command to run.""" | 341 """Get the valgrind command to run.""" |
353 # Note that self._args begins with the exe to be run. | 342 # Note that self._args begins with the exe to be run. |
354 tool_name = self.ToolName() | 343 tool_name = self.ToolName() |
355 | 344 |
356 # Construct the valgrind command. | 345 # Construct the valgrind command. |
357 if self.SelfContained(): | 346 if 'CHROME_VALGRIND' in os.environ: |
358 proc = ["valgrind-%s.sh" % tool_name] | 347 path = os.path.join(os.environ['CHROME_VALGRIND'], "bin", "valgrind") |
359 else: | 348 else: |
360 if 'CHROME_VALGRIND' in os.environ: | 349 path = "valgrind" |
361 path = os.path.join(os.environ['CHROME_VALGRIND'], "bin", "valgrind") | 350 proc = [path, "--tool=%s" % tool_name] |
362 else: | |
363 path = "valgrind" | |
364 proc = [path, "--tool=%s" % tool_name] | |
365 | 351 |
366 proc += ["--num-callers=%i" % int(self._options.num_callers)] | 352 proc += ["--num-callers=%i" % int(self._options.num_callers)] |
367 | 353 |
368 if self._options.trace_children: | 354 if self._options.trace_children: |
369 proc += ["--trace-children=yes"] | 355 proc += ["--trace-children=yes"] |
370 proc += ["--trace-children-skip='*dbus-daemon*'"] | 356 proc += ["--trace-children-skip='*dbus-daemon*'"] |
371 proc += ["--trace-children-skip='*dbus-launch*'"] | 357 proc += ["--trace-children-skip='*dbus-launch*'"] |
372 proc += ["--trace-children-skip='*perl*'"] | 358 proc += ["--trace-children-skip='*perl*'"] |
373 proc += ["--trace-children-skip='*python*'"] | 359 proc += ["--trace-children-skip='*python*'"] |
374 # This is really Python, but for some reason Valgrind follows it. | 360 # This is really Python, but for some reason Valgrind follows it. |
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
576 | 562 |
577 def Analyze(self, check_sanity=False): | 563 def Analyze(self, check_sanity=False): |
578 ret = self.GetAnalyzeResults(check_sanity) | 564 ret = self.GetAnalyzeResults(check_sanity) |
579 | 565 |
580 if ret != 0: | 566 if ret != 0: |
581 logging.info("Please see http://dev.chromium.org/developers/how-tos/" | 567 logging.info("Please see http://dev.chromium.org/developers/how-tos/" |
582 "using-valgrind for the info on Memcheck/Valgrind") | 568 "using-valgrind for the info on Memcheck/Valgrind") |
583 return ret | 569 return ret |
584 | 570 |
585 | 571 |
586 class PinTool(BaseTool): | |
587 """Abstract class for running PIN tools. | |
588 | |
589 Always subclass this and implement ToolSpecificFlags() and | |
590 ExtendOptionParser() for tool-specific stuff. | |
591 """ | |
592 def PrepareForTest(self): | |
593 pass | |
594 | |
595 def ToolSpecificFlags(self): | |
596 raise NotImplementedError, "This method should be implemented " \ | |
597 "in the tool-specific subclass" | |
598 | |
599 def ToolCommand(self): | |
600 """Get the PIN command to run.""" | |
601 | |
602 # Construct the PIN command. | |
603 pin_cmd = os.getenv("PIN_COMMAND") | |
604 if not pin_cmd: | |
605 raise RuntimeError, "Please set PIN_COMMAND environment variable " \ | |
606 "with the path to pin.exe" | |
607 proc = pin_cmd.split(" ") | |
608 | |
609 proc += self.ToolSpecificFlags() | |
610 | |
611 # The PIN command is constructed. | |
612 | |
613 # PIN requires -- to separate PIN flags from the executable name. | |
614 # self._args begins with the exe to be run. | |
615 proc += ["--"] | |
616 | |
617 proc += self._args | |
618 return proc | |
619 | |
620 | |
621 class ThreadSanitizerBase(object): | |
622 """ThreadSanitizer | |
623 Dynamic data race detector for Linux, Mac and Windows. | |
624 | |
625 http://code.google.com/p/data-race-test/wiki/ThreadSanitizer | |
626 | |
627 Since TSan works on both Valgrind (Linux, Mac) and PIN (Windows), we need | |
628 to have multiple inheritance | |
629 """ | |
630 | |
631 INFO_MESSAGE="Please see http://dev.chromium.org/developers/how-tos/" \ | |
632 "using-valgrind/threadsanitizer for the info on " \ | |
633 "ThreadSanitizer" | |
634 | |
635 def __init__(self): | |
636 super(ThreadSanitizerBase, self).__init__() | |
637 self.RegisterOptionParserHook(ThreadSanitizerBase.ExtendOptionParser) | |
638 | |
639 def ToolName(self): | |
640 return "tsan" | |
641 | |
642 def UseXML(self): | |
643 return False | |
644 | |
645 def SelfContained(self): | |
646 return True | |
647 | |
648 def ExtendOptionParser(self, parser): | |
649 parser.add_option("", "--hybrid", default="no", | |
650 dest="hybrid", | |
651 help="Finds more data races, may give false positive " | |
652 "reports unless the code is annotated") | |
653 parser.add_option("", "--announce-threads", default="yes", | |
654 dest="announce_threads", | |
655 help="Show the the stack traces of thread creation") | |
656 parser.add_option("", "--free-is-write", default="no", | |
657 dest="free_is_write", | |
658 help="Treat free()/operator delete as memory write. " | |
659 "This helps finding more data races, but (currently) " | |
660 "this may give false positive reports on std::string " | |
661 "internals, see http://code.google.com/p/data-race-test" | |
662 "/issues/detail?id=40") | |
663 | |
664 def EvalBoolFlag(self, flag_value): | |
665 if (flag_value in ["1", "true", "yes"]): | |
666 return True | |
667 elif (flag_value in ["0", "false", "no"]): | |
668 return False | |
669 raise RuntimeError, "Can't parse flag value (%s)" % flag_value | |
670 | |
671 def ToolSpecificFlags(self): | |
672 ret = [] | |
673 | |
674 ignore_files = ["ignores.txt"] | |
675 for platform_suffix in common.PlatformNames(): | |
676 ignore_files.append("ignores_%s.txt" % platform_suffix) | |
677 for ignore_file in ignore_files: | |
678 fullname = os.path.join(self._source_dir, | |
679 "tools", "valgrind", "tsan", ignore_file) | |
680 if os.path.exists(fullname): | |
681 fullname = common.NormalizeWindowsPath(fullname) | |
682 ret += ["--ignore=%s" % fullname] | |
683 | |
684 # This should shorten filepaths for local builds. | |
685 ret += ["--file-prefix-to-cut=%s/" % self._source_dir] | |
686 | |
687 # This should shorten filepaths on bots. | |
688 ret += ["--file-prefix-to-cut=build/src/"] | |
689 ret += ["--file-prefix-to-cut=out/Release/../../"] | |
690 | |
691 # This should shorten filepaths for functions intercepted in TSan. | |
692 ret += ["--file-prefix-to-cut=scripts/tsan/tsan/"] | |
693 ret += ["--file-prefix-to-cut=src/tsan/tsan/"] | |
694 | |
695 ret += ["--gen-suppressions=true"] | |
696 | |
697 if self.EvalBoolFlag(self._options.hybrid): | |
698 ret += ["--hybrid=yes"] # "no" is the default value for TSAN | |
699 | |
700 if self.EvalBoolFlag(self._options.announce_threads): | |
701 ret += ["--announce-threads"] | |
702 | |
703 if self.EvalBoolFlag(self._options.free_is_write): | |
704 ret += ["--free-is-write=yes"] | |
705 else: | |
706 ret += ["--free-is-write=no"] | |
707 | |
708 | |
709 # --show-pc flag is needed for parsing the error logs on Darwin. | |
710 if platform_suffix == 'mac': | |
711 ret += ["--show-pc=yes"] | |
712 ret += ["--show-pid=no"] | |
713 | |
714 boring_callers = common.BoringCallers(mangled=False, use_re_wildcards=False) | |
715 # TODO(timurrrr): In fact, we want "starting from .." instead of "below .." | |
716 for bc in boring_callers: | |
717 ret += ["--cut_stack_below=%s" % bc] | |
718 | |
719 return ret | |
720 | |
721 | |
722 class ThreadSanitizerPosix(ThreadSanitizerBase, ValgrindTool): | |
723 def ToolSpecificFlags(self): | |
724 proc = ThreadSanitizerBase.ToolSpecificFlags(self) | |
725 # The -v flag is needed for printing the list of used suppressions and | |
726 # obtaining addresses for loaded shared libraries on Mac. | |
727 proc += ["-v"] | |
728 return proc | |
729 | |
730 def CreateAnalyzer(self): | |
731 use_gdb = common.IsMac() | |
732 return tsan_analyze.TsanAnalyzer(use_gdb) | |
733 | |
734 def Analyze(self, check_sanity=False): | |
735 ret = self.GetAnalyzeResults(check_sanity) | |
736 | |
737 if ret != 0: | |
738 logging.info(self.INFO_MESSAGE) | |
739 return ret | |
740 | |
741 | |
742 class ThreadSanitizerWindows(ThreadSanitizerBase, PinTool): | |
743 | |
744 def __init__(self): | |
745 super(ThreadSanitizerWindows, self).__init__() | |
746 self.RegisterOptionParserHook(ThreadSanitizerWindows.ExtendOptionParser) | |
747 | |
748 def ExtendOptionParser(self, parser): | |
749 parser.add_option("", "--suppressions", default=[], | |
750 action="append", | |
751 help="path to TSan suppression file") | |
752 | |
753 | |
754 def ToolSpecificFlags(self): | |
755 add_env = { | |
756 "CHROME_ALLOCATOR" : "WINHEAP", | |
757 } | |
758 for k,v in add_env.iteritems(): | |
759 logging.info("export %s=%s", k, v) | |
760 os.putenv(k, v) | |
761 | |
762 proc = ThreadSanitizerBase.ToolSpecificFlags(self) | |
763 # On PIN, ThreadSanitizer has its own suppression mechanism | |
764 # and --log-file flag which work exactly on Valgrind. | |
765 suppression_count = 0 | |
766 for suppression_file in self._options.suppressions: | |
767 if os.path.exists(suppression_file): | |
768 suppression_count += 1 | |
769 suppression_file = common.NormalizeWindowsPath(suppression_file) | |
770 proc += ["--suppressions=%s" % suppression_file] | |
771 | |
772 if not suppression_count: | |
773 logging.warning("WARNING: NOT USING SUPPRESSIONS!") | |
774 | |
775 logfilename = self.log_dir + "/tsan.%p" | |
776 proc += ["--log-file=" + common.NormalizeWindowsPath(logfilename)] | |
777 | |
778 # TODO(timurrrr): Add flags for Valgrind trace children analog when we | |
779 # start running complex tests (e.g. UI) under TSan/Win. | |
780 | |
781 return proc | |
782 | |
783 def Analyze(self, check_sanity=False): | |
784 filenames = glob.glob(self.log_dir + "/tsan.*") | |
785 analyzer = tsan_analyze.TsanAnalyzer() | |
786 ret = analyzer.Report(filenames, None, check_sanity) | |
787 if ret != 0: | |
788 logging.info(self.INFO_MESSAGE) | |
789 return ret | |
790 | |
791 | |
792 class DrMemory(BaseTool): | 572 class DrMemory(BaseTool): |
793 """Dr.Memory | 573 """Dr.Memory |
794 Dynamic memory error detector for Windows. | 574 Dynamic memory error detector for Windows. |
795 | 575 |
796 http://dev.chromium.org/developers/how-tos/using-drmemory | 576 http://dev.chromium.org/developers/how-tos/using-drmemory |
797 It is not very mature at the moment, some things might not work properly. | 577 It is not very mature at the moment, some things might not work properly. |
798 """ | 578 """ |
799 | 579 |
800 def __init__(self, full_mode, pattern_mode): | 580 def __init__(self, full_mode, pattern_mode): |
801 super(DrMemory, self).__init__() | 581 super(DrMemory, self).__init__() |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
956 proc += ["-pattern", "0xf1fd", "-no_count_leaks", "-redzone_size", "0x20"] | 736 proc += ["-pattern", "0xf1fd", "-no_count_leaks", "-redzone_size", "0x20"] |
957 elif not self.full_mode: | 737 elif not self.full_mode: |
958 proc += ["-light"] | 738 proc += ["-light"] |
959 | 739 |
960 proc += self._tool_flags | 740 proc += self._tool_flags |
961 | 741 |
962 # Dr.Memory requires -- to separate tool flags from the executable name. | 742 # Dr.Memory requires -- to separate tool flags from the executable name. |
963 proc += ["--"] | 743 proc += ["--"] |
964 | 744 |
965 if self._options.indirect or self._options.indirect_webkit_layout: | 745 if self._options.indirect or self._options.indirect_webkit_layout: |
966 # TODO(timurrrr): reuse for TSan on Windows | |
967 wrapper_path = os.path.join(self._source_dir, | 746 wrapper_path = os.path.join(self._source_dir, |
968 "tools", "valgrind", "browser_wrapper_win.py") | 747 "tools", "valgrind", "browser_wrapper_win.py") |
969 wrapper = " ".join(["python", wrapper_path] + proc) | 748 wrapper = " ".join(["python", wrapper_path] + proc) |
970 self.CreateBrowserWrapper(wrapper) | 749 self.CreateBrowserWrapper(wrapper) |
971 logging.info("browser wrapper = " + " ".join(proc)) | 750 logging.info("browser wrapper = " + " ".join(proc)) |
972 if self._options.indirect_webkit_layout: | 751 if self._options.indirect_webkit_layout: |
973 proc = self._args | 752 proc = self._args |
974 # Layout tests want forward slashes. | 753 # Layout tests want forward slashes. |
975 wrapper = wrapper.replace('\\', '/') | 754 wrapper = wrapper.replace('\\', '/') |
976 proc += ["--wrapper", wrapper] | 755 proc += ["--wrapper", wrapper] |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1027 (self.log_dir, ppid)) | 806 (self.log_dir, ppid)) |
1028 ret |= analyzer.Report(ppid_filenames, testcase_name, False) | 807 ret |= analyzer.Report(ppid_filenames, testcase_name, False) |
1029 print "=====================================================" | 808 print "=====================================================" |
1030 sys.stdout.flush() | 809 sys.stdout.flush() |
1031 | 810 |
1032 logging.info("Please see http://dev.chromium.org/developers/how-tos/" | 811 logging.info("Please see http://dev.chromium.org/developers/how-tos/" |
1033 "using-drmemory for the info on Dr. Memory") | 812 "using-drmemory for the info on Dr. Memory") |
1034 return ret | 813 return ret |
1035 | 814 |
1036 | 815 |
1037 # RaceVerifier support. See | |
1038 # http://code.google.com/p/data-race-test/wiki/RaceVerifier for more details. | |
1039 class ThreadSanitizerRV1Analyzer(tsan_analyze.TsanAnalyzer): | |
1040 """ TsanAnalyzer that saves race reports to a file. """ | |
1041 | |
1042 TMP_FILE = "rvlog.tmp" | |
1043 | |
1044 def __init__(self, source_dir, use_gdb): | |
1045 super(ThreadSanitizerRV1Analyzer, self).__init__(use_gdb) | |
1046 self.out = open(self.TMP_FILE, "w") | |
1047 | |
1048 def Report(self, files, testcase, check_sanity=False): | |
1049 reports = self.GetReports(files) | |
1050 for report in reports: | |
1051 print >>self.out, report | |
1052 if len(reports) > 0: | |
1053 logging.info("RaceVerifier pass 1 of 2, found %i reports" % len(reports)) | |
1054 return -1 | |
1055 return 0 | |
1056 | |
1057 def CloseOutputFile(self): | |
1058 self.out.close() | |
1059 | |
1060 | |
1061 class ThreadSanitizerRV1Mixin(object): | |
1062 """RaceVerifier first pass. | |
1063 | |
1064 Runs ThreadSanitizer as usual, but hides race reports and collects them in a | |
1065 temporary file""" | |
1066 | |
1067 def __init__(self): | |
1068 super(ThreadSanitizerRV1Mixin, self).__init__() | |
1069 self.RegisterOptionParserHook(ThreadSanitizerRV1Mixin.ExtendOptionParser) | |
1070 | |
1071 def ExtendOptionParser(self, parser): | |
1072 parser.set_defaults(hybrid="yes") | |
1073 | |
1074 def CreateAnalyzer(self): | |
1075 use_gdb = common.IsMac() | |
1076 self.analyzer = ThreadSanitizerRV1Analyzer(self._source_dir, use_gdb) | |
1077 return self.analyzer | |
1078 | |
1079 def Cleanup(self): | |
1080 super(ThreadSanitizerRV1Mixin, self).Cleanup() | |
1081 self.analyzer.CloseOutputFile() | |
1082 | |
1083 | |
1084 class ThreadSanitizerRV2Mixin(object): | |
1085 """RaceVerifier second pass.""" | |
1086 | |
1087 def __init__(self): | |
1088 super(ThreadSanitizerRV2Mixin, self).__init__() | |
1089 self.RegisterOptionParserHook(ThreadSanitizerRV2Mixin.ExtendOptionParser) | |
1090 | |
1091 def ExtendOptionParser(self, parser): | |
1092 parser.add_option("", "--race-verifier-sleep-ms", | |
1093 dest="race_verifier_sleep_ms", default=10, | |
1094 help="duration of RaceVerifier delays") | |
1095 | |
1096 def ToolSpecificFlags(self): | |
1097 proc = super(ThreadSanitizerRV2Mixin, self).ToolSpecificFlags() | |
1098 proc += ['--race-verifier=%s' % ThreadSanitizerRV1Analyzer.TMP_FILE, | |
1099 '--race-verifier-sleep-ms=%d' % | |
1100 int(self._options.race_verifier_sleep_ms)] | |
1101 return proc | |
1102 | |
1103 def Cleanup(self): | |
1104 super(ThreadSanitizerRV2Mixin, self).Cleanup() | |
1105 os.unlink(ThreadSanitizerRV1Analyzer.TMP_FILE) | |
1106 | |
1107 | |
1108 class ThreadSanitizerRV1Posix(ThreadSanitizerRV1Mixin, ThreadSanitizerPosix): | |
1109 pass | |
1110 | |
1111 | |
1112 class ThreadSanitizerRV2Posix(ThreadSanitizerRV2Mixin, ThreadSanitizerPosix): | |
1113 pass | |
1114 | |
1115 | |
1116 class ThreadSanitizerRV1Windows(ThreadSanitizerRV1Mixin, | |
1117 ThreadSanitizerWindows): | |
1118 pass | |
1119 | |
1120 | |
1121 class ThreadSanitizerRV2Windows(ThreadSanitizerRV2Mixin, | |
1122 ThreadSanitizerWindows): | |
1123 pass | |
1124 | |
1125 | |
1126 class RaceVerifier(object): | |
1127 """Runs tests under RaceVerifier/Valgrind.""" | |
1128 | |
1129 MORE_INFO_URL = "http://code.google.com/p/data-race-test/wiki/RaceVerifier" | |
1130 | |
1131 def RV1Factory(self): | |
1132 if common.IsWindows(): | |
1133 return ThreadSanitizerRV1Windows() | |
1134 else: | |
1135 return ThreadSanitizerRV1Posix() | |
1136 | |
1137 def RV2Factory(self): | |
1138 if common.IsWindows(): | |
1139 return ThreadSanitizerRV2Windows() | |
1140 else: | |
1141 return ThreadSanitizerRV2Posix() | |
1142 | |
1143 def ToolName(self): | |
1144 return "tsan" | |
1145 | |
1146 def Main(self, args, check_sanity, min_runtime_in_seconds): | |
1147 logging.info("Running a TSan + RaceVerifier test. For more information, " + | |
1148 "see " + self.MORE_INFO_URL) | |
1149 cmd1 = self.RV1Factory() | |
1150 ret = cmd1.Main(args, check_sanity, min_runtime_in_seconds) | |
1151 # Verify race reports, if there are any. | |
1152 if ret == -1: | |
1153 logging.info("Starting pass 2 of 2. Running the same binary in " + | |
1154 "RaceVerifier mode to confirm possible race reports.") | |
1155 logging.info("For more information, see " + self.MORE_INFO_URL) | |
1156 cmd2 = self.RV2Factory() | |
1157 ret = cmd2.Main(args, check_sanity, min_runtime_in_seconds) | |
1158 else: | |
1159 logging.info("No reports, skipping RaceVerifier second pass") | |
1160 logging.info("Please see " + self.MORE_INFO_URL + " for more information " + | |
1161 "on RaceVerifier") | |
1162 return ret | |
1163 | |
1164 def Run(self, args, module, min_runtime_in_seconds=0): | |
1165 return self.Main(args, False, min_runtime_in_seconds) | |
1166 | |
1167 | |
1168 class ToolFactory: | 816 class ToolFactory: |
1169 def Create(self, tool_name): | 817 def Create(self, tool_name): |
1170 if tool_name == "memcheck": | 818 if tool_name == "memcheck": |
1171 return Memcheck() | 819 return Memcheck() |
1172 if tool_name == "tsan": | |
1173 if common.IsWindows(): | |
1174 return ThreadSanitizerWindows() | |
1175 else: | |
1176 return ThreadSanitizerPosix() | |
1177 if tool_name == "drmemory" or tool_name == "drmemory_light": | 820 if tool_name == "drmemory" or tool_name == "drmemory_light": |
1178 # TODO(timurrrr): remove support for "drmemory" when buildbots are | 821 # TODO(timurrrr): remove support for "drmemory" when buildbots are |
1179 # switched to drmemory_light OR make drmemory==drmemory_full the default | 822 # switched to drmemory_light OR make drmemory==drmemory_full the default |
1180 # mode when the tool is mature enough. | 823 # mode when the tool is mature enough. |
1181 return DrMemory(False, False) | 824 return DrMemory(False, False) |
1182 if tool_name == "drmemory_full": | 825 if tool_name == "drmemory_full": |
1183 return DrMemory(True, False) | 826 return DrMemory(True, False) |
1184 if tool_name == "drmemory_pattern": | 827 if tool_name == "drmemory_pattern": |
1185 return DrMemory(False, True) | 828 return DrMemory(False, True) |
1186 if tool_name == "tsan_rv": | |
1187 return RaceVerifier() | |
1188 try: | 829 try: |
1189 platform_name = common.PlatformNames()[0] | 830 platform_name = common.PlatformNames()[0] |
1190 except common.NotImplementedError: | 831 except common.NotImplementedError: |
1191 platform_name = sys.platform + "(Unknown)" | 832 platform_name = sys.platform + "(Unknown)" |
1192 raise RuntimeError, "Unknown tool (tool=%s, platform=%s)" % (tool_name, | 833 raise RuntimeError, "Unknown tool (tool=%s, platform=%s)" % (tool_name, |
1193 platform_name) | 834 platform_name) |
1194 | 835 |
1195 def CreateTool(tool): | 836 def CreateTool(tool): |
1196 return ToolFactory().Create(tool) | 837 return ToolFactory().Create(tool) |
OLD | NEW |