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).
235 print "The following are hardcoded in as vtable roots: %s" % vtable_roots
239 if __name__ == "__main__":