diff options
author | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-09 01:08:37 +0000 |
---|---|---|
committer | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-09 01:08:37 +0000 |
commit | f14a91b0b78106c0ce0006cfaa9c0984994cf755 (patch) | |
tree | 2832ab23ca8173b14dbddda922b97f73bd197350 /tools/valgrind/valgrind_analyze.py | |
parent | eb2d54b529107d8af216604824367ba912cb8625 (diff) | |
download | chromium_src-f14a91b0b78106c0ce0006cfaa9c0984994cf755.zip chromium_src-f14a91b0b78106c0ce0006cfaa9c0984994cf755.tar.gz chromium_src-f14a91b0b78106c0ce0006cfaa9c0984994cf755.tar.bz2 |
Make a valgrind tool in the same vein as the purify one.
Currently supports the following commands:
./chrome_tests.sh --test base
./chrome_tests.sh --test net
./chrome_tests.sh --test ipc
./chrome_tests.sh --test unit
The valgrind_*.py files are my first real big chunks of python, so any suggestions would be greatly appreciated.
Review URL: http://codereview.chromium.org/16583
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@7796 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/valgrind/valgrind_analyze.py')
-rwxr-xr-x | tools/valgrind/valgrind_analyze.py | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/tools/valgrind/valgrind_analyze.py b/tools/valgrind/valgrind_analyze.py new file mode 100755 index 0000000..c73f6bf --- /dev/null +++ b/tools/valgrind/valgrind_analyze.py @@ -0,0 +1,176 @@ +#!/usr/bin/python +# Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# valgrind_analyze.py + +''' Given a valgrind XML file, parses errors and uniques them.''' + +import logging +import optparse +import os +import sys +from xml.dom.minidom import parse + +# These are functions (using C++ mangled names) that we look for in stack +# traces. We don't show stack frames while pretty printing when they are below +# any of the following: +_TOP_OF_STACK_POINTS = [ + # Don't show our testing framework. + "testing::Test::Run()", + # Also don't show the internals of libc/pthread. + "start_thread" +] + +def getTextOf(top_node, name): + ''' Returns all text in all DOM nodes with a certain |name| that are children + of |top_node|. + ''' + + text = "" + for nodes_named in top_node.getElementsByTagName(name): + text += "".join([node.data for node in nodes_named.childNodes + if node.nodeType == node.TEXT_NODE]) + return text + +def removeCommonRoot(source_dir, directory): + '''Returns a string with the string prefix |source_dir| removed from + |directory|.''' + if source_dir: + # Do this for safety, just in case directory is an absolute path outside of + # source_dir. + prefix = os.path.commonprefix([source_dir, directory]) + return directory[len(prefix) + 1:] + + return directory + +# Constants that give real names to the abbreviations in valgrind XML output. +INSTRUCTION_POINTER = "ip" +OBJECT_FILE = "obj" +FUNCTION_NAME = "fn" +SRC_FILE_DIR = "dir" +SRC_FILE_NAME = "file" +SRC_LINE = "line" + +class ValgrindError: + ''' Takes a <DOM Element: error> node and reads all the data from it. A + ValgrindError is immutable and is hashed on its pretty printed output. + ''' + + def __init__(self, source_dir, error_node): + ''' Copies all the relevant information out of the DOM and into object + properties. + + Args: + error_node: The <error></error> DOM node we're extracting from. + source_dir: Prefix that should be stripped from the <dir> node. + ''' + + self._kind = getTextOf(error_node, "kind") + self._what = getTextOf(error_node, "what") + + self._frames = [] + stack_node = error_node.getElementsByTagName("stack")[0] + + for frame in stack_node.getElementsByTagName("frame"): + frame_dict = { + INSTRUCTION_POINTER : getTextOf(frame, INSTRUCTION_POINTER), + OBJECT_FILE : getTextOf(frame, OBJECT_FILE), + FUNCTION_NAME : getTextOf(frame, FUNCTION_NAME), + SRC_FILE_DIR : removeCommonRoot( + source_dir, getTextOf(frame, SRC_FILE_DIR)), + SRC_FILE_NAME : getTextOf(frame, SRC_FILE_NAME), + SRC_LINE : getTextOf(frame, SRC_LINE) + } + + self._frames += [frame_dict] + + if frame_dict[FUNCTION_NAME] in _TOP_OF_STACK_POINTS: + break + + def __str__(self): + ''' Pretty print the type and stack frame of this specific error.''' + output = self._kind + "\n" + for frame in self._frames: + output += (" " + (frame[FUNCTION_NAME] or frame[INSTRUCTION_POINTER]) + + " (") + + if frame[SRC_FILE_DIR] != "": + output += (frame[SRC_FILE_DIR] + "/" + frame[SRC_FILE_DIR] + ":" + + frame[SRC_LINE]) + else: + output += frame[OBJECT_FILE] + output += ")\n" + + return output + + def UniqueString(self): + ''' String to use for object identity. Don't print this, use str(obj) + instead.''' + rep = self._kind + " " + for frame in self._frames: + rep += frame[FUNCTION_NAME] + + if frame[SRC_FILE_DIR] != "": + rep += frame[SRC_FILE_DIR] + "/" + frame[SRC_FILE_NAME] + else: + rep += frame[OBJECT_FILE] + + return rep + + def __hash__(self): + return hash(self.UniqueString()) + def __eq__(self, rhs): + return self.UniqueString() == rhs + +class ValgrindAnalyze: + ''' Given a set of Valgrind XML files, parse all the errors out of them, + unique them and output the results.''' + + def __init__(self, source_dir, files): + '''Reads in a set of files. + + Args: + source_dir: Path to top of source tree for this build + files: A list of filenames. + ''' + + self._errors = set() + for file in files: + raw_errors = parse(file).getElementsByTagName("error") + for raw_error in raw_errors: + self._errors.add(ValgrindError(source_dir, raw_error)) + + def Report(self): + if self._errors: + logging.error("FAIL! There were %s errors: " % len(self._errors)) + + for error in self._errors: + logging.error(error) + + return -1 + + logging.info("PASS! No errors found!") + return 0 + +def _main(): + '''For testing only. The ValgrindAnalyze class should be imported instead.''' + retcode = 0 + parser = optparse.OptionParser("usage: %prog [options] <files to analyze>") + parser.add_option("", "--source_dir", + help="path to top of source tree for this build" + "(used to normalize source paths in baseline)") + + (options, args) = parser.parse_args() + if not len(args) >= 1: + parser.error("no filename specified") + filenames = args + + analyzer = ValgrindAnalyze(options.source_dir, filenames) + retcode = analyzer.Report() + + sys.exit(retcode) + +if __name__ == "__main__": + _main() |