blob: c6bbe665a239f39b806d000b91940dd72c0d2e25 [file] [log] [blame]
Guido van Rossum15f27fb1992-12-10 00:00:58 +00001#!/usr/local/bin/python
Guido van Rossumec758ea1991-06-04 20:36:54 +00002
3# objgraph
4#
5# Read "nm -o" input (on IRIX: "nm -Bo") of a set of libraries or modules
6# and print various interesting listings, such as:
7#
8# - which names are used but not defined in the set (and used where),
9# - which names are defined in the set (and where),
10# - which modules use which other modules,
11# - which modules are used by which other modules.
12#
13# Usage: objgraph [-cdu] [file] ...
14# -c: print callers per objectfile
15# -d: print callees per objectfile
16# -u: print usage of undefined symbols
17# If none of -cdu is specified, all are assumed.
18# Use "nm -o" to generate the input (on IRIX: "nm -Bo"),
19# e.g.: nm -o /lib/libc.a | objgraph
20
21
22import sys
23import string
Guido van Rossum15f27fb1992-12-10 00:00:58 +000024import os
Guido van Rossumec758ea1991-06-04 20:36:54 +000025import getopt
Guido van Rossum15f27fb1992-12-10 00:00:58 +000026import regex
Guido van Rossumec758ea1991-06-04 20:36:54 +000027
28# Types of symbols.
29#
30definitions = 'TRGDSBAEC'
31externals = 'UV'
32ignore = 'Nntrgdsbavuc'
33
34# Regular expression to parse "nm -o" output.
35#
Guido van Rossum15f27fb1992-12-10 00:00:58 +000036matcher = regex.compile('\(.*\):\t?........ \(.\) \(.*\)$')
Guido van Rossumec758ea1991-06-04 20:36:54 +000037
38# Store "item" in "dict" under "key".
39# The dictionary maps keys to lists of items.
40# If there is no list for the key yet, it is created.
41#
42def store(dict, key, item):
43 if dict.has_key(key):
44 dict[key].append(item)
45 else:
46 dict[key] = [item]
47
48# Return a flattened version of a list of strings: the concatenation
49# of its elements with intervening spaces.
50#
51def flat(list):
52 s = ''
53 for item in list:
54 s = s + ' ' + item
55 return s[1:]
56
57# Global variables mapping defined/undefined names to files and back.
58#
59file2undef = {}
60def2file = {}
61file2def = {}
62undef2file = {}
63
64# Read one input file and merge the data into the tables.
65# Argument is an open file.
66#
67def readinput(file):
68 while 1:
Guido van Rossum15f27fb1992-12-10 00:00:58 +000069 s = file.readline()
Guido van Rossumec758ea1991-06-04 20:36:54 +000070 if not s:
71 break
Guido van Rossum15f27fb1992-12-10 00:00:58 +000072 # If you get any output from this line,
Guido van Rossumec758ea1991-06-04 20:36:54 +000073 # it is probably caused by an unexpected input line:
Guido van Rossum15f27fb1992-12-10 00:00:58 +000074 if matcher.search(s) < 0: s; continue # Shouldn't happen
75 (ra, rb), (r1a, r1b), (r2a, r2b), (r3a, r3b) = matcher.regs[:4]
Guido van Rossumec758ea1991-06-04 20:36:54 +000076 fn, name, type = s[r1a:r1b], s[r3a:r3b], s[r2a:r2b]
77 if type in definitions:
78 store(def2file, name, fn)
79 store(file2def, fn, name)
80 elif type in externals:
81 store(file2undef, fn, name)
82 store(undef2file, name, fn)
83 elif not type in ignore:
84 print fn + ':' + name + ': unknown type ' + type
85
86# Print all names that were undefined in some module and where they are
87# defined.
88#
89def printcallee():
90 flist = file2undef.keys()
91 flist.sort()
92 for file in flist:
93 print file + ':'
94 elist = file2undef[file]
95 elist.sort()
96 for ext in elist:
97 if len(ext) >= 8:
98 tabs = '\t'
99 else:
100 tabs = '\t\t'
101 if not def2file.has_key(ext):
102 print '\t' + ext + tabs + ' *undefined'
103 else:
104 print '\t' + ext + tabs + flat(def2file[ext])
105
106# Print for each module the names of the other modules that use it.
107#
108def printcaller():
109 files = file2def.keys()
110 files.sort()
111 for file in files:
112 callers = []
113 for label in file2def[file]:
114 if undef2file.has_key(label):
115 callers = callers + undef2file[label]
116 if callers:
117 callers.sort()
118 print file + ':'
119 lastfn = ''
120 for fn in callers:
121 if fn <> lastfn:
122 print '\t' + fn
123 lastfn = fn
124 else:
125 print file + ': unused'
126
127# Print undefine names and where they are used.
128#
129def printundef():
130 undefs = {}
131 for file in file2undef.keys():
132 for ext in file2undef[file]:
133 if not def2file.has_key(ext):
134 store(undefs, ext, file)
135 elist = undefs.keys()
136 elist.sort()
137 for ext in elist:
138 print ext + ':'
139 flist = undefs[ext]
140 flist.sort()
141 for file in flist:
142 print '\t' + file
143
144# Print warning messages about names defined in more than one file.
145#
146def warndups():
147 savestdout = sys.stdout
148 sys.stdout = sys.stderr
149 names = def2file.keys()
150 names.sort()
151 for name in names:
152 if len(def2file[name]) > 1:
153 print 'warning:', name, 'multiply defined:',
154 print flat(def2file[name])
155 sys.stdout = savestdout
156
157# Main program
158#
159def main():
160 try:
161 optlist, args = getopt.getopt(sys.argv[1:], 'cdu')
162 except getopt.error:
163 sys.stdout = sys.stderr
Guido van Rossum15f27fb1992-12-10 00:00:58 +0000164 print 'Usage:', os.path.basename(sys.argv[0]),
165 print '[-cdu] [file] ...'
Guido van Rossumec758ea1991-06-04 20:36:54 +0000166 print '-c: print callers per objectfile'
167 print '-d: print callees per objectfile'
168 print '-u: print usage of undefined symbols'
169 print 'If none of -cdu is specified, all are assumed.'
170 print 'Use "nm -o" to generate the input (on IRIX: "nm -Bo"),'
171 print 'e.g.: nm -o /lib/libc.a | objgraph'
172 return 1
173 optu = optc = optd = 0
174 for opt, void in optlist:
Guido van Rossumbdfcfcc1992-01-01 19:35:13 +0000175 if opt == '-u':
Guido van Rossumec758ea1991-06-04 20:36:54 +0000176 optu = 1
Guido van Rossumbdfcfcc1992-01-01 19:35:13 +0000177 elif opt == '-c':
Guido van Rossumec758ea1991-06-04 20:36:54 +0000178 optc = 1
Guido van Rossumbdfcfcc1992-01-01 19:35:13 +0000179 elif opt == '-d':
Guido van Rossumec758ea1991-06-04 20:36:54 +0000180 optd = 1
Guido van Rossumbdfcfcc1992-01-01 19:35:13 +0000181 if optu == optc == optd == 0:
Guido van Rossumec758ea1991-06-04 20:36:54 +0000182 optu = optc = optd = 1
183 if not args:
184 args = ['-']
185 for file in args:
Guido van Rossumbdfcfcc1992-01-01 19:35:13 +0000186 if file == '-':
Guido van Rossumec758ea1991-06-04 20:36:54 +0000187 readinput(sys.stdin)
188 else:
189 readinput(open(file, 'r'))
190 #
191 warndups()
192 #
193 more = (optu + optc + optd > 1)
194 if optd:
195 if more:
196 print '---------------All callees------------------'
197 printcallee()
198 if optu:
199 if more:
200 print '---------------Undefined callees------------'
201 printundef()
202 if optc:
203 if more:
204 print '---------------All Callers------------------'
205 printcaller()
206 return 0
207
208# Call the main program.
209# Use its return value as exit status.
210# Catch interrupts to avoid stack trace.
211#
212try:
213 sys.exit(main())
214except KeyboardInterrupt:
215 sys.exit(1)