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 |