| 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 # memcheck_analyze.py | 6 # memcheck_analyze.py |
| 7 | 7 |
| 8 ''' Given a valgrind XML file, parses errors and uniques them.''' | 8 ''' Given a valgrind XML file, parses errors and uniques them.''' |
| 9 | 9 |
| 10 import gdb_helper |
| 11 |
| 10 import logging | 12 import logging |
| 11 import optparse | 13 import optparse |
| 12 import os | 14 import os |
| 13 import re | |
| 14 import subprocess | 15 import subprocess |
| 15 import sys | 16 import sys |
| 16 import tempfile | |
| 17 import time | 17 import time |
| 18 from xml.dom.minidom import parse | 18 from xml.dom.minidom import parse |
| 19 from xml.parsers.expat import ExpatError | 19 from xml.parsers.expat import ExpatError |
| 20 | 20 |
| 21 # Global symbol table (yuck) | 21 # Global symbol table (yuck) |
| 22 TheAddressTable = None | 22 TheAddressTable = None |
| 23 | 23 |
| 24 GDB_LINE_RE = re.compile(r'Line ([0-9]*) of "([^"]*)".*') | |
| 25 | |
| 26 def _GdbOutputToFileLine(output_line): | |
| 27 ''' Parse the gdb output line, return a pair (file, line num) ''' | |
| 28 match = GDB_LINE_RE.match(output_line) | |
| 29 if match: | |
| 30 return match.groups()[1], match.groups()[0] | |
| 31 else: | |
| 32 return None | |
| 33 | |
| 34 def ResolveAddressesWithinABinary(binary_name, load_address, address_list): | |
| 35 ''' For each address, return a pair (file, line num) ''' | |
| 36 commands = tempfile.NamedTemporaryFile() | |
| 37 commands.write('add-symbol-file "%s" %s\n' % (binary_name, load_address)) | |
| 38 for addr in address_list: | |
| 39 commands.write('info line *%s\n' % addr) | |
| 40 commands.write('quit\n') | |
| 41 commands.flush() | |
| 42 gdb_commandline = 'gdb -batch -x %s 2>/dev/null' % commands.name | |
| 43 gdb_pipe = os.popen(gdb_commandline) | |
| 44 result = gdb_pipe.readlines() | |
| 45 | |
| 46 address_count = 0 | |
| 47 ret = {} | |
| 48 for line in result: | |
| 49 if line.startswith('Line'): | |
| 50 ret[address_list[address_count]] = _GdbOutputToFileLine(line) | |
| 51 address_count += 1 | |
| 52 if line.startswith('No line'): | |
| 53 ret[address_list[address_count]] = (None, None) | |
| 54 address_count += 1 | |
| 55 gdb_pipe.close() | |
| 56 commands.close() | |
| 57 return ret | |
| 58 | |
| 59 class _AddressTable(object): | |
| 60 ''' Object to do batched line number lookup. ''' | |
| 61 def __init__(self): | |
| 62 self._load_addresses = {} | |
| 63 self._binaries = {} | |
| 64 self._all_resolved = False | |
| 65 | |
| 66 def AddBinaryAt(self, binary, load_address): | |
| 67 ''' Register a new shared library or executable. ''' | |
| 68 self._load_addresses[binary] = load_address | |
| 69 | |
| 70 def Add(self, binary, address): | |
| 71 ''' Register a lookup request. ''' | |
| 72 if binary == '': | |
| 73 logging.warn('adding address %s in empty binary?' % address) | |
| 74 if binary in self._binaries: | |
| 75 self._binaries[binary].append(address) | |
| 76 else: | |
| 77 self._binaries[binary] = [address] | |
| 78 self._all_resolved = False | |
| 79 | |
| 80 def ResolveAll(self): | |
| 81 ''' Carry out all lookup requests. ''' | |
| 82 self._translation = {} | |
| 83 for binary in self._binaries.keys(): | |
| 84 if binary != '' and binary in self._load_addresses: | |
| 85 load_address = self._load_addresses[binary] | |
| 86 addr = ResolveAddressesWithinABinary(binary, load_address, self._binarie
s[binary]) | |
| 87 self._translation[binary] = addr | |
| 88 self._all_resolved = True | |
| 89 | |
| 90 def GetFileLine(self, binary, addr): | |
| 91 ''' Get the (filename, linenum) result of a previously-registered lookup req
uest. ''' | |
| 92 if self._all_resolved: | |
| 93 if binary in self._translation: | |
| 94 if addr in self._translation[binary]: | |
| 95 return self._translation[binary][addr] | |
| 96 return (None, None) | |
| 97 | 24 |
| 98 # These are functions (using C++ mangled names) that we look for in stack | 25 # These are functions (using C++ mangled names) that we look for in stack |
| 99 # traces. We don't show stack frames while pretty printing when they are below | 26 # traces. We don't show stack frames while pretty printing when they are below |
| 100 # any of the following: | 27 # any of the following: |
| 101 _TOP_OF_STACK_POINTS = [ | 28 _TOP_OF_STACK_POINTS = [ |
| 102 # Don't show our testing framework. | 29 # Don't show our testing framework. |
| 103 "testing::Test::Run()", | 30 "testing::Test::Run()", |
| 104 # Also don't show the internals of libc/pthread. | 31 # Also don't show the internals of libc/pthread. |
| 105 "start_thread" | 32 "start_thread" |
| 106 ] | 33 ] |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 322 '''Reads in a set of files. | 249 '''Reads in a set of files. |
| 323 | 250 |
| 324 Args: | 251 Args: |
| 325 source_dir: Path to top of source tree for this build | 252 source_dir: Path to top of source tree for this build |
| 326 files: A list of filenames. | 253 files: A list of filenames. |
| 327 show_all_leaks: whether to show even less important leaks | 254 show_all_leaks: whether to show even less important leaks |
| 328 ''' | 255 ''' |
| 329 | 256 |
| 330 if use_gdb: | 257 if use_gdb: |
| 331 global TheAddressTable | 258 global TheAddressTable |
| 332 TheAddressTable = _AddressTable() | 259 TheAddressTable = gdb_helper.AddressTable() |
| 333 self._errors = set() | 260 self._errors = set() |
| 334 badfiles = set() | 261 badfiles = set() |
| 335 start = time.time() | 262 start = time.time() |
| 336 self._parse_failed = False | 263 self._parse_failed = False |
| 337 for file in files: | 264 for file in files: |
| 338 # Wait up to three minutes for valgrind to finish writing all files, | 265 # Wait up to three minutes for valgrind to finish writing all files, |
| 339 # but after that, just skip incomplete files and warn. | 266 # but after that, just skip incomplete files and warn. |
| 340 f = open(file, "r+") | 267 f = open(file, "r+") |
| 341 found = False | 268 found = False |
| 342 firstrun = True | 269 firstrun = True |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 428 parser.error("no filename specified") | 355 parser.error("no filename specified") |
| 429 filenames = args | 356 filenames = args |
| 430 | 357 |
| 431 analyzer = MemcheckAnalyze(options.source_dir, filenames, use_gdb=True) | 358 analyzer = MemcheckAnalyze(options.source_dir, filenames, use_gdb=True) |
| 432 retcode = analyzer.Report() | 359 retcode = analyzer.Report() |
| 433 | 360 |
| 434 sys.exit(retcode) | 361 sys.exit(retcode) |
| 435 | 362 |
| 436 if __name__ == "__main__": | 363 if __name__ == "__main__": |
| 437 _main() | 364 _main() |
| OLD | NEW |