| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 # purify_test.py | 6 # purify_test.py |
| 7 | 7 |
| 8 '''Runs an exe through Valgrind and puts the intermediate files in a | 8 '''Runs an exe through Valgrind and puts the intermediate files in a |
| 9 directory. | 9 directory. |
| 10 ''' | 10 ''' |
| 11 | 11 |
| 12 import datetime | 12 import datetime |
| 13 import glob | 13 import glob |
| 14 import logging | 14 import logging |
| 15 import optparse | 15 import optparse |
| 16 import os | 16 import os |
| 17 import re | |
| 18 import shutil | 17 import shutil |
| 19 import sys | 18 import sys |
| 20 import time | |
| 21 | |
| 22 import google.path_utils | |
| 23 | 19 |
| 24 import common | 20 import common |
| 25 | 21 |
| 26 import valgrind_analyze | 22 import valgrind_analyze |
| 27 | 23 |
| 28 rmtree = shutil.rmtree | 24 rmtree = shutil.rmtree |
| 29 | 25 |
| 30 class Valgrind(): | 26 class Valgrind(object): |
| 27 |
| 28 """Abstract class for running Valgrind. |
| 29 |
| 30 Always subclass this and implement ValgrindCommand() with platform specific |
| 31 stuff. |
| 32 """ |
| 33 |
| 31 TMP_DIR = "valgrind.tmp" | 34 TMP_DIR = "valgrind.tmp" |
| 32 | 35 |
| 33 def __init__(self): | 36 def __init__(self): |
| 34 self._suppressions_files = [] | 37 self._suppressions_files = [] |
| 38 # If we have a valgrind.tmp directory, we failed to cleanup last time. |
| 39 if os.path.exists(self.TMP_DIR): |
| 40 shutil.rmtree(self.TMP_DIR) |
| 41 os.mkdir(self.TMP_DIR) |
| 35 | 42 |
| 36 def CreateOptionParser(self): | 43 def CreateOptionParser(self): |
| 37 self._parser = optparse.OptionParser("usage: %prog [options] <program to " | 44 self._parser = optparse.OptionParser("usage: %prog [options] <program to " |
| 38 "test>") | 45 "test>") |
| 39 self._parser.add_option("-e", "--echo_to_stdout", | |
| 40 dest="echo_to_stdout", action="store_true", default=False, | |
| 41 help="echo purify output to standard output") | |
| 42 self._parser.add_option("-t", "--timeout", | 46 self._parser.add_option("-t", "--timeout", |
| 43 dest="timeout", metavar="TIMEOUT", default=10000, | 47 dest="timeout", metavar="TIMEOUT", default=10000, |
| 44 help="timeout in seconds for the run (default 10000)") | 48 help="timeout in seconds for the run (default 10000)") |
| 45 self._parser.add_option("", "--source_dir", | 49 self._parser.add_option("", "--source_dir", |
| 46 help="path to top of source tree for this build" | 50 help="path to top of source tree for this build" |
| 47 "(used to normalize source paths in baseline)") | 51 "(used to normalize source paths in baseline)") |
| 48 self._parser.add_option("", "--suppressions", default=["."], | 52 self._parser.add_option("", "--suppressions", default=["."], |
| 49 action="append", | 53 action="append", |
| 50 help="path to a valgrind suppression file") | 54 help="path to a valgrind suppression file") |
| 51 self._parser.add_option("", "--generate_suppressions", action="store_true", | 55 self._parser.add_option("", "--generate_suppressions", action="store_true", |
| 52 default=False, | 56 default=False, |
| 53 help="Skip analysis and generate suppressions") | 57 help="Skip analysis and generate suppressions") |
| 54 self._parser.description = __doc__ | 58 self._parser.description = __doc__ |
| 55 | 59 |
| 56 def ParseArgv(self): | 60 def ParseArgv(self): |
| 57 self.CreateOptionParser() | 61 self.CreateOptionParser() |
| 58 self._options, self._args = self._parser.parse_args() | 62 self._options, self._args = self._parser.parse_args() |
| 59 self._timeout = int(self._options.timeout) | 63 self._timeout = int(self._options.timeout) |
| 60 self._suppressions = self._options.suppressions | 64 self._suppressions = self._options.suppressions |
| 61 self._generate_suppressions = self._options.generate_suppressions | 65 self._generate_suppressions = self._options.generate_suppressions |
| 62 self._source_dir = self._options.source_dir | 66 self._source_dir = self._options.source_dir |
| 63 return True | 67 return True |
| 64 | 68 |
| 65 def Setup(self): | 69 def Setup(self): |
| 66 return self.ParseArgv() | 70 return self.ParseArgv() |
| 67 | 71 |
| 72 def ValgrindCommand(self): |
| 73 """Get the valgrind command to run.""" |
| 74 raise RuntimeError, "Never use Valgrind directly. Always subclass and " \ |
| 75 "implement ValgrindCommand() at least" |
| 76 |
| 68 def Execute(self): | 77 def Execute(self): |
| 69 ''' Execute the app to be tested after successful instrumentation. | 78 ''' Execute the app to be tested after successful instrumentation. |
| 70 Full execution command-line provided by subclassers via proc.''' | 79 Full execution command-line provided by subclassers via proc.''' |
| 71 logging.info("starting execution...") | 80 logging.info("starting execution...") |
| 72 # note that self._args begins with the exe to be run | |
| 73 # TODO(erg): We probably want to get a version of valgrind that supports | |
| 74 # the "--track-origins" option... | |
| 75 proc = ["valgrind", "--smc-check=all", "--leak-check=full", | |
| 76 "--num-callers=30"] | |
| 77 | 81 |
| 78 # Either generate suppressions or load them. | 82 proc = self.ValgrindCommand() |
| 79 if self._generate_suppressions: | |
| 80 proc += ["--gen-suppressions=all"] | |
| 81 else: | |
| 82 proc += ["--xml=yes"] | |
| 83 | |
| 84 suppression_count = 0 | |
| 85 for suppression_file in self._suppressions: | |
| 86 if os.path.exists(suppression_file): | |
| 87 suppression_count += 1 | |
| 88 proc += ["--suppressions=%s" % suppression_file] | |
| 89 | |
| 90 if not suppression_count: | |
| 91 logging.warning("WARNING: NOT USING SUPPRESSIONS!") | |
| 92 | |
| 93 proc += ["--log-file=" + self.TMP_DIR + "/valgrind.%p"] + self._args | |
| 94 | |
| 95 # If we have a valgrind.tmp directory, we failed to cleanup last time. | |
| 96 if os.path.exists(self.TMP_DIR): | |
| 97 shutil.rmtree(self.TMP_DIR) | |
| 98 os.mkdir(self.TMP_DIR) | |
| 99 common.RunSubprocess(proc, self._timeout) | 83 common.RunSubprocess(proc, self._timeout) |
| 100 | 84 |
| 101 # Always return true, even if running the subprocess failed. We depend on | 85 # Always return true, even if running the subprocess failed. We depend on |
| 102 # Analyze to determine if the run was valid. (This behaviour copied from | 86 # Analyze to determine if the run was valid. (This behaviour copied from |
| 103 # the purify_test.py script.) | 87 # the purify_test.py script.) |
| 104 return True | 88 return True |
| 105 | 89 |
| 106 def Analyze(self): | 90 def Analyze(self): |
| 107 # Glob all the files in the "valgrind.tmp" directory | 91 # Glob all the files in the "valgrind.tmp" directory |
| 108 filenames = glob.glob(self.TMP_DIR + "/valgrind.*") | 92 filenames = glob.glob(self.TMP_DIR + "/valgrind.*") |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 144 end = datetime.datetime.now() | 128 end = datetime.datetime.now() |
| 145 seconds = (end - start).seconds | 129 seconds = (end - start).seconds |
| 146 hours = seconds / 3600 | 130 hours = seconds / 3600 |
| 147 seconds = seconds % 3600 | 131 seconds = seconds % 3600 |
| 148 minutes = seconds / 60 | 132 minutes = seconds / 60 |
| 149 seconds = seconds % 60 | 133 seconds = seconds % 60 |
| 150 logging.info("elapsed time: %02d:%02d:%02d" % (hours, minutes, seconds)) | 134 logging.info("elapsed time: %02d:%02d:%02d" % (hours, minutes, seconds)) |
| 151 return retcode | 135 return retcode |
| 152 | 136 |
| 153 | 137 |
| 138 class ValgrindLinux(Valgrind): |
| 139 |
| 140 """Valgrind on Linux.""" |
| 141 |
| 142 def __init__(self): |
| 143 Valgrind.__init__(self) |
| 144 |
| 145 def ValgrindCommand(self): |
| 146 """Get the valgrind command to run.""" |
| 147 # note that self._args begins with the exe to be run |
| 148 # TODO(erg): We probably want to get a version of valgrind that supports |
| 149 # the "--track-origins" option... |
| 150 proc = ["valgrind", "--smc-check=all", "--leak-check=full", |
| 151 "--num-callers=30"] |
| 152 |
| 153 # Either generate suppressions or load them. |
| 154 if self._generate_suppressions: |
| 155 proc += ["--gen-suppressions=all"] |
| 156 else: |
| 157 proc += ["--xml=yes"] |
| 158 |
| 159 suppression_count = 0 |
| 160 for suppression_file in self._suppressions: |
| 161 if os.path.exists(suppression_file): |
| 162 suppression_count += 1 |
| 163 proc += ["--suppressions=%s" % suppression_file] |
| 164 |
| 165 if not suppression_count: |
| 166 logging.warning("WARNING: NOT USING SUPPRESSIONS!") |
| 167 |
| 168 proc += ["--log-file=" + self.TMP_DIR + "/valgrind.%p"] + self._args |
| 169 return proc |
| 170 |
| 171 |
| 172 class ValgrindMac(Valgrind): |
| 173 |
| 174 """Valgrind on Mac OS X. |
| 175 |
| 176 Valgrind on OS X does not support suppressions (yet). |
| 177 """ |
| 178 |
| 179 def __init__(self): |
| 180 Valgrind.__init__(self) |
| 181 |
| 182 def ValgrindCommand(self): |
| 183 """Get the valgrind command to run.""" |
| 184 proc = ["valgrind", "--leak-check=full"] |
| 185 proc += ["--log-file=" + self.TMP_DIR + "/valgrind.%p"] + self._args |
| 186 return proc |
| 187 |
| 188 def Analyze(self): |
| 189 # TODO(nirnimesh): Implement analysis later. Valgrind on Mac is new so |
| 190 # analysis might not be useful until we have stable output from valgring |
| 191 return 0 |
| 192 |
| 154 | 193 |
| 155 if __name__ == "__main__": | 194 if __name__ == "__main__": |
| 156 valgrind = Valgrind() | 195 if sys.platform == 'darwin': # Mac |
| 157 retcode = valgrind.Main() | 196 valgrind = ValgrindMac() |
| 158 sys.exit(retcode) | 197 retcode = valgrind.Main() |
| 159 | 198 sys.exit(retcode) |
| 160 | 199 elif sys.platform == 'linux2': # Linux |
| 200 valgrind = ValgrindLinux() |
| 201 retcode = valgrind.Main() |
| 202 sys.exit(retcode) |
| 203 else: |
| 204 logging.error("Unknown platform: %s" % sys.platform) |
| 205 sys.exit(1) |
| OLD | NEW |