OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python |
| 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 |
| 4 # found in the LICENSE file. |
| 5 |
| 6 # purify_test.py |
| 7 |
| 8 '''Runs an exe through Valgrind and puts the intermediate files in a |
| 9 directory. |
| 10 ''' |
| 11 |
| 12 import datetime |
| 13 import glob |
| 14 import logging |
| 15 import optparse |
| 16 import os |
| 17 import re |
| 18 import shutil |
| 19 import sys |
| 20 import time |
| 21 |
| 22 import google.path_utils |
| 23 |
| 24 import common |
| 25 |
| 26 import valgrind_analyze |
| 27 |
| 28 rmtree = shutil.rmtree |
| 29 |
| 30 class Valgrind(): |
| 31 TMP_DIR = "valgrind.tmp" |
| 32 |
| 33 def __init__(self): |
| 34 self._data_dir = None |
| 35 |
| 36 def CreateOptionParser(self): |
| 37 self._parser = optparse.OptionParser("usage: %prog [options] <program to " |
| 38 "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", |
| 43 dest="timeout", metavar="TIMEOUT", default=10000, |
| 44 help="timeout in seconds for the run (default 10000)") |
| 45 self._parser.add_option("", "--source_dir", |
| 46 help="path to top of source tree for this build" |
| 47 "(used to normalize source paths in baseline)") |
| 48 self._parser.add_option("", "--data_dir", default=".", |
| 49 help="path to where purify data files live") |
| 50 self._parser.add_option("", "--generate_suppressions", action="store_true", |
| 51 default=False, |
| 52 help="Skip analysis and generate suppressions") |
| 53 self._parser.description = __doc__ |
| 54 |
| 55 def ParseArgv(self): |
| 56 self.CreateOptionParser() |
| 57 self._options, self._args = self._parser.parse_args() |
| 58 self._timeout = int(self._options.timeout) |
| 59 self._data_dir = self._options.data_dir |
| 60 self._generate_suppressions = self._options.generate_suppressions |
| 61 self._source_dir = self._options.source_dir |
| 62 return True |
| 63 |
| 64 def Setup(self): |
| 65 return self.ParseArgv() |
| 66 |
| 67 def Execute(self): |
| 68 ''' Execute the app to be tested after successful instrumentation. |
| 69 Full execution command-line provided by subclassers via proc.''' |
| 70 logging.info("starting execution...") |
| 71 # note that self._args begins with the exe to be run |
| 72 proc = ["valgrind", "--smc-check=all", "--leak-check=full", |
| 73 "--track-origins=yes", "--num-callers=30"] |
| 74 |
| 75 # Either generate suppressions or load them. |
| 76 if self._generate_suppressions: |
| 77 proc += ["--gen-suppressions=all"] |
| 78 else: |
| 79 proc += ["--xml=yes"] |
| 80 |
| 81 suppressions = os.path.join(self._data_dir, "suppressions.txt") |
| 82 if os.path.exists(suppressions): |
| 83 proc += ["--suppressions=%s" % suppressions] |
| 84 else: |
| 85 logging.warning("WARNING: NOT USING SUPPRESSIONS!") |
| 86 |
| 87 proc += ["--log-file=" + self.TMP_DIR + "/valgrind.%p"] + self._args |
| 88 |
| 89 # If we have a valgrind.tmp directory, we failed to cleanup last time. |
| 90 if os.path.exists(self.TMP_DIR): |
| 91 shutil.rmtree(self.TMP_DIR) |
| 92 os.mkdir(self.TMP_DIR) |
| 93 common.RunSubprocess(proc, self._timeout) |
| 94 |
| 95 # Always return true, even if running the subprocess failed. We depend on |
| 96 # Analyze to determine if the run was valid. (This behaviour copied from |
| 97 # the purify_test.py script.) |
| 98 return True |
| 99 |
| 100 def Analyze(self): |
| 101 # Glob all the files in the "valgrind.tmp" directory |
| 102 filenames = glob.glob(self.TMP_DIR + "/valgrind.*") |
| 103 analyzer = valgrind_analyze.ValgrindAnalyze(self._source_dir, filenames) |
| 104 analyzer.Report() |
| 105 return 1 |
| 106 |
| 107 def Cleanup(self): |
| 108 # Right now, we can cleanup by deleting our temporary directory. Other |
| 109 # cleanup is still a TODO? |
| 110 shutil.rmtree(self.TMP_DIR) |
| 111 return True |
| 112 |
| 113 def RunTestsAndAnalyze(self): |
| 114 self.Execute() |
| 115 if self._generate_suppressions: |
| 116 logging.info("Skipping analysis to let you look at the raw output...") |
| 117 return 0 |
| 118 |
| 119 retcode = self.Analyze() |
| 120 if retcode: |
| 121 logging.error("Analyze failed.") |
| 122 return retcode |
| 123 logging.info("Execution and analysis completed successfully.") |
| 124 return 0 |
| 125 |
| 126 def Main(self): |
| 127 '''Call this to run through the whole process: Setup, Execute, Analyze''' |
| 128 start = datetime.datetime.now() |
| 129 retcode = -1 |
| 130 if self.Setup(): |
| 131 retcode = self.RunTestsAndAnalyze() |
| 132 |
| 133 # Skip cleanup on generate. |
| 134 if not self._generate_suppressions: |
| 135 self.Cleanup() |
| 136 else: |
| 137 logging.error("Setup failed") |
| 138 end = datetime.datetime.now() |
| 139 seconds = (end - start).seconds |
| 140 hours = seconds / 3600 |
| 141 seconds = seconds % 3600 |
| 142 minutes = seconds / 60 |
| 143 seconds = seconds % 60 |
| 144 logging.info("elapsed time: %02d:%02d:%02d" % (hours, minutes, seconds)) |
| 145 return retcode |
| 146 |
| 147 |
| 148 |
| 149 if __name__ == "__main__": |
| 150 valgrind = Valgrind() |
| 151 retcode = valgrind.Main() |
| 152 sys.exit(retcode) |
| 153 |
| 154 |
OLD | NEW |