8 ## hash from symbol name to list of symbols with that name,
9 ## where the list of symbols contains a list representing each symbol
13 def createBacklinks(name, syms):
17 ## for each ref, add ourselves as a referencer
18 if symbols.has_key(r):
24 def markSymbol(frm, name):
25 if not symbols.has_key(name):
26 print "%s referenced but was not in the objdump"
28 ## print ambiguous references unless they are internal noise like ".L129"
29 if len(syms) > 1 and name[0] != '.':
30 print "Reference to symbol '%s' from '%s' is ambiguous, marking all '%s'" % (name, frm, name)
34 pass ## already marked
41 def cmpFilename(a, b):
47 def sizeAsString(bytes):
49 return "%d bytes" % bytes
50 elif bytes < 1024*1024:
51 return "%.2gK" % (bytes / 1024.0)
53 return "%.2gM" % (bytes / 1024.0 / 1024.0)
58 for (name, syms) in symbols.items():
59 s = syms[0] ## we always mark all or none for now
60 if not s[4] and name[0] != '.': ## skip .L129 type symbols
63 filename = "unknown file"
64 list.append ((name, filename, s[5], s[7]))
70 list.sort(cmpFilename)
73 if next_filename != filename:
74 if total_this_file > 0:
75 file_summaries.append (" %s may be unused in %s" % (sizeAsString(total_this_file), filename))
76 print "%s has these symbols not reachable from exported symbols:" % next_filename
77 filename = next_filename
79 print " %s %s" % (l[0], sizeAsString(l[3]))
80 total_unused = total_unused + l[3]
81 total_this_file = total_this_file + l[3]
83 print " referenced from %s" % trace
85 for fs in file_summaries:
87 print "%s total may be unused" % sizeAsString(total_unused)
91 ## 0001aa44 <_dbus_message_get_network_data>:
92 sym_re = re.compile ('([0-9a-f]+) <([^>]+)>:')
93 ## 1aa49: e8 00 00 00 00 call 1aa4e <_dbus_message_get_network_data+0xa>
94 ref_re = re.compile (' <([^>]+)> *$')
95 ## /home/hp/dbus-cvs/dbus/dbus/dbus-message.c:139
96 file_re = re.compile ('^(\/[^:].*):[0-9]+$')
97 ## _dbus_message_get_network_data+0xa
98 funcname_re = re.compile ('([^+]+)\+[0-9a-fx]+')
99 ## 00005410 T dbus_address_entries_free
100 dynsym_re = re.compile ('T ([^ \n]+)$')
102 filename = sys.argv[1]
105 objdump -D --demangle -l %s
108 command = string.strip (command)
110 print "Running: %s" % command
112 f = os.popen(command)
114 ## first we find which functions reference which other functions
116 lines = f.readlines()
123 match = sym_re.match(l)
125 addr = match.group(1)
126 name = match.group(2)
128 match = ref_re.search(l)
130 target = match.group(1)
132 match = file_re.match(l)
134 file = match.group(1)
137 ## 0 symname, 1 address, 2 references, 3 filename, 4 reached, 5 referenced-by 6 backlinked 7 approx size
138 item = [name, addr, [], None, 0, [], 0, 0]
139 if symbols.has_key(name):
140 symbols[name].append(item)
142 symbols[name] = [item]
145 prev_addr = long(current_sym[1], 16)
146 our_addr = long(item[1], 16)
147 item[7] = our_addr - prev_addr
149 print "Computed negative size %d for %s" % (item[7], item[0])
154 elif target and current_sym:
155 match = funcname_re.match(target)
157 ## dump the "+address"
158 target = match.group(1)
159 if target == current_sym[0]:
160 pass ## skip self-references
162 current_sym[2].append (target)
164 elif file and current_sym:
165 if file.startswith('/usr/include'):
166 ## inlined libc thingy
168 elif current_sym[0].startswith('.debug'):
171 elif current_sym[3] and current_sym[3] != file:
172 raise Exception ("%s in both %s and %s" % (current_sym[0], current_sym[3], file))
174 current_sym[3] = file
176 ## now we need to find the roots (exported symbols)
177 command = "nm -D %s" % filename
178 print "Running: %s" % command
179 f = os.popen(command)
180 lines = f.readlines ()
182 match = dynsym_re.search(l)
184 name = match.group(1)
185 if roots.has_key(name):
186 raise Exception("symbol %s exported twice?" % name)
190 print "%d symbols exported from this object" % len(roots)
192 ## these functions are used only indirectly, so we don't
193 ## notice they are used. Manually add them as roots...
194 vtable_roots = ['unix_finalize',
197 'unix_connection_set',
199 'unix_live_messages_changed',
201 'handle_client_data_cookie_sha1_mech',
202 'handle_client_data_external_mech',
203 'handle_server_data_cookie_sha1_mech',
204 'handle_server_data_external_mech',
205 'handle_client_initial_response_cookie_sha1_mech',
206 'handle_client_initial_response_external_mech',
207 'handle_client_shutdown_cookie_sha1_mech',
208 'handle_client_shutdown_external_mech',
209 'handle_server_shutdown_cookie_sha1_mech',
210 'handle_server_shutdown_external_mech'
213 for vr in vtable_roots:
214 if roots.has_key(vr):
215 raise Exception("%s is already a root" % vr)
218 for k in roots.keys():
219 markSymbol("root", k)
221 for (k, v) in symbols.items():
222 createBacklinks(k, v)
226 The symbols mentioned below don't appear to be reachable starting from
227 the dynamic exports of the library. However, this program is pretty
228 dumb; a limitation that creates false positives is that it can only
229 trace 'reachable' through hardcoded function calls, if a function is
230 called only through a vtable, it won't be marked reachable (and
231 neither will its children in the call graph).
233 Also, the sizes mentioned are more or less completely bogus.
237 print "The following are hardcoded in as vtable roots: %s" % vtable_roots
241 if __name__ == "__main__":