OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/env python | |
2 # | |
3 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
4 # Use of this source code is governed by a BSD-style license that can be | |
5 # found in the LICENSE file | |
6 | |
7 import base64 | |
8 import os | |
9 import sys | |
10 import re | |
11 | |
12 from optparse import OptionParser | |
13 | |
14 """Extracts the list of resident symbols of a library loaded in a process. | |
15 | |
16 This scripts combines the extended output of memdump for a given process | |
17 (obtained through memdump -x PID) and the symbol table of a .so loaded in that | |
18 process (obtained through nm -C lib-with-symbols.so), filtering out only those | |
19 symbols that, at the time of the snapshot, were resident in memory (that are, | |
20 the symbols which start address belongs to a mapped page of the .so which was | |
21 resident at the time of the snapshot). | |
22 The aim is to perform a "code coverage"-like profiling of a binary, intersecting | |
23 run-time information (list of resident pages) and debug symbols. | |
24 """ | |
25 | |
26 _PAGE_SIZE = 4096 | |
27 | |
bulach
2013/07/23 08:10:58
nit: two \n between top levels, i.e., add another
Primiano Tucci (use gerrit)
2013/07/23 08:54:00
Done.
| |
28 def _TestBit(word, bit): | |
29 assert(bit >= 0 and bit < 8) | |
30 return not not ((word >> bit) & 1) | |
31 | |
32 def _HexAddr(addr): | |
33 return hex(addr)[2:].zfill(8) | |
34 | |
35 def _GetResidentPagesSet(memdump_fh, lib_name, verbose): | |
36 """Parses the memdump output and extracts the resident page set for lib_name. | |
37 Args: | |
38 memdump_fh: Handler of a memdump output file. | |
39 lib_name: A string containing the name of the library.so to be matched. | |
40 verbose: Print a verbose header for each mapping matched. | |
41 | |
42 Returns: | |
43 A set of resident pages (the key is the page index) for all the | |
44 mappings matching .*lib_name. | |
45 """ | |
46 resident_pages = set() | |
47 MAP_RX = re.compile( | |
48 r'^([0-9a-f]+)-([0-9a-f]+) ([\w-]+) ([0-9a-f]+) .* "(.*)" \[(.*)\]$') | |
49 for line in memdump_fh: | |
50 line = line.rstrip('\r\n') | |
51 if line.startswith('[ PID'): | |
52 continue | |
53 | |
54 r = MAP_RX.match(line) | |
55 if not r: | |
56 sys.stderr.write('Skipping %s from %s\n' % (line, memdump_file)) | |
57 continue | |
58 | |
59 map_start = int(r.group(1), 16) | |
60 map_end = int(r.group(2), 16) | |
61 prot = r.group(3) | |
62 offset = int(r.group(4), 16) | |
63 assert(offset % _PAGE_SIZE == 0) | |
64 lib = r.group(5) | |
65 enc_bitmap = r.group(6) | |
66 | |
67 if not lib.endswith(lib_name): | |
68 continue | |
69 | |
70 bitmap = base64.b64decode(enc_bitmap) | |
71 map_pages_count = (map_end - map_start + 1) / _PAGE_SIZE | |
72 bitmap_pages_count = len(bitmap) * 8 | |
73 | |
74 if verbose: | |
75 print 'Found %s: mapped %d pages in mode %s @ offset %s.' % ( | |
76 lib, map_pages_count, prot, _HexAddr(offset)) | |
77 print ' Map range in the process VA: [%s - %s]. Len: %s' % ( | |
78 _HexAddr(map_start), | |
79 _HexAddr(map_end), | |
80 _HexAddr(map_pages_count * _PAGE_SIZE)) | |
81 print ' Corresponding addresses in the binary: [%s - %s]. Len: %s' % ( | |
82 _HexAddr(offset), | |
83 _HexAddr(offset + map_end - map_start), | |
84 _HexAddr(map_pages_count * _PAGE_SIZE)) | |
85 print ' Bitmap: %d pages' % bitmap_pages_count | |
86 print '' | |
87 | |
88 assert(bitmap_pages_count >= map_pages_count) | |
89 for i in xrange(map_pages_count): | |
90 bitmap_idx = i / 8 | |
91 bitmap_off = i % 8 | |
92 if (bitmap_idx < len(bitmap) and | |
93 _TestBit(ord(bitmap[bitmap_idx]), bitmap_off)): | |
94 resident_pages.add(offset / _PAGE_SIZE + i) | |
95 return resident_pages | |
96 | |
97 | |
98 def main(argv): | |
99 NM_RX = re.compile(r'^([0-9a-f]+)\s+.*$') | |
100 | |
101 parser = OptionParser() | |
102 parser.add_option("-r", "--reverse", | |
103 action="store_true", dest="reverse", default=False, | |
104 help="Print out non present symbols") | |
105 parser.add_option("-v", "--verbose", | |
106 action="store_true", dest="verbose", default=False, | |
107 help="Print out verbose debug information.") | |
108 | |
109 (options, args) = parser.parse_args() | |
110 | |
111 if len(args) != 3: | |
112 print 'Usage: %s [-v] memdump.file nm.file library.so' % ( | |
113 os.path.basename(argv[0])) | |
114 return 1 | |
115 | |
116 memdump_file = args[0] | |
117 nm_file = args[1] | |
118 lib_name = args[2] | |
119 | |
120 if memdump_file == '-': | |
121 memdump_fh = sys.stdin.readlines() | |
122 else: | |
123 memdump_fh = open(memdump_file, 'r') | |
bulach
2013/07/23 08:10:58
nit: could do .readlines() here too so that the fi
Primiano Tucci (use gerrit)
2013/07/23 08:54:00
Right! Done.
| |
124 resident_pages = _GetResidentPagesSet(memdump_fh, lib_name, options.verbose) | |
125 | |
126 # Process the nm symbol table, filtering out the resident symbols. | |
127 nm_fh = open(nm_file, 'r') | |
128 for line in nm_fh: | |
129 line = line.rstrip('\r\n') | |
130 # Skip undefined symbols (lines with no address). | |
131 if line.startswith(' '): | |
132 continue | |
133 | |
134 r = NM_RX.match(line) | |
135 if not r: | |
136 sys.stderr.write('Skipping %s from %s\n' % (line, nm_file)) | |
137 continue | |
138 | |
139 sym_addr = int(r.group(1), 16) | |
140 sym_page = sym_addr / _PAGE_SIZE | |
141 last_sym_matched = (sym_page in resident_pages) | |
142 if (sym_page in resident_pages) != options.reverse: | |
143 print line | |
144 return 0 | |
145 | |
146 if __name__=='__main__': | |
bulach
2013/07/23 08:10:58
nit: space around ==
Primiano Tucci (use gerrit)
2013/07/23 08:54:00
Done.
| |
147 sys.exit(main(sys.argv)) | |
OLD | NEW |