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 |