| 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 |