| 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 280 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 387 # Collect these and print them at the end. | 314 # Collect these and print them at the end. |
| 388 # | 315 # |
| 389 # With our patch for https://bugs.kde.org/show_bug.cgi?id=205000 in, | 316 # With our patch for https://bugs.kde.org/show_bug.cgi?id=205000 in, |
| 390 # the file also includes records of the form | 317 # the file also includes records of the form |
| 391 # <load_obj><obj>/usr/lib/libgcc_s.1.dylib</obj><ip>0x27000</ip></load_obj> | 318 # <load_obj><obj>/usr/lib/libgcc_s.1.dylib</obj><ip>0x27000</ip></load_obj> |
| 392 # giving the filename and load address of each binary that was mapped | 319 # giving the filename and load address of each binary that was mapped |
| 393 # into the process. | 320 # into the process. |
| 394 | 321 |
| 395 global TheAddressTable | 322 global TheAddressTable |
| 396 if use_gdb: | 323 if use_gdb: |
| 397 TheAddressTable = _AddressTable() | 324 TheAddressTable = gdb_helper.AddressTable() |
| 398 self._errors = set() | 325 self._errors = set() |
| 399 self._suppcounts = {} | 326 self._suppcounts = {} |
| 400 badfiles = set() | 327 badfiles = set() |
| 401 start = time.time() | 328 start = time.time() |
| 402 self._parse_failed = False | 329 self._parse_failed = False |
| 403 for file in files: | 330 for file in files: |
| 404 # Wait up to three minutes for valgrind to finish writing all files, | 331 # Wait up to three minutes for valgrind to finish writing all files, |
| 405 # but after that, just skip incomplete files and warn. | 332 # but after that, just skip incomplete files and warn. |
| 406 f = open(file, "r+") | 333 f = open(file, "r+") |
| 407 found = False | 334 found = False |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 521 parser.error("no filename specified") | 448 parser.error("no filename specified") |
| 522 filenames = args | 449 filenames = args |
| 523 | 450 |
| 524 analyzer = MemcheckAnalyze(options.source_dir, filenames, use_gdb=True) | 451 analyzer = MemcheckAnalyze(options.source_dir, filenames, use_gdb=True) |
| 525 retcode = analyzer.Report() | 452 retcode = analyzer.Report() |
| 526 | 453 |
| 527 sys.exit(retcode) | 454 sys.exit(retcode) |
| 528 | 455 |
| 529 if __name__ == "__main__": | 456 if __name__ == "__main__": |
| 530 _main() | 457 _main() |
| OLD | NEW |