blob: b45bba2ae30cafe6981fbd7b5e4c24353e6609dc [file] [log] [blame]
Guido van Rossumec758ea1991-06-04 20:36:54 +00001#!/usr/local/python
2
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
24import path
25import getopt
26import regexp
27
28# Types of symbols.
29#
30definitions = 'TRGDSBAEC'
31externals = 'UV'
32ignore = 'Nntrgdsbavuc'
33
34# Regular expression to parse "nm -o" output.
35#
36matcher = regexp.compile('(.*):\t?........ (.) (.*)$')
37
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:
69 s = file.readline(200) # Arbitrary, but reasonable limit
70 if not s:
71 break
72 # If you get an exception on this line,
73 # it is probably caused by an unexpected input line:
74 (ra, rb), (r1a, r1b), (r2a, r2b), (r3a, r3b) = matcher.exec(s)
75 fn, name, type = s[r1a:r1b], s[r3a:r3b], s[r2a:r2b]
76 if type in definitions:
77 store(def2file, name, fn)
78 store(file2def, fn, name)
79 elif type in externals:
80 store(file2undef, fn, name)
81 store(undef2file, name, fn)
82 elif not type in ignore:
83 print fn + ':' + name + ': unknown type ' + type
84
85# Print all names that were undefined in some module and where they are
86# defined.
87#
88def printcallee():
89 flist = file2undef.keys()
90 flist.sort()
91 for file in flist:
92 print file + ':'
93 elist = file2undef[file]
94 elist.sort()
95 for ext in elist:
96 if len(ext) >= 8:
97 tabs = '\t'
98 else:
99 tabs = '\t\t'
100 if not def2file.has_key(ext):
101 print '\t' + ext + tabs + ' *undefined'
102 else:
103 print '\t' + ext + tabs + flat(def2file[ext])
104
105# Print for each module the names of the other modules that use it.
106#
107def printcaller():
108 files = file2def.keys()
109 files.sort()
110 for file in files:
111 callers = []
112 for label in file2def[file]:
113 if undef2file.has_key(label):
114 callers = callers + undef2file[label]
115 if callers:
116 callers.sort()
117 print file + ':'
118 lastfn = ''
119 for fn in callers:
120 if fn <> lastfn:
121 print '\t' + fn
122 lastfn = fn
123 else:
124 print file + ': unused'
125
126# Print undefine names and where they are used.
127#
128def printundef():
129 undefs = {}
130 for file in file2undef.keys():
131 for ext in file2undef[file]:
132 if not def2file.has_key(ext):
133 store(undefs, ext, file)
134 elist = undefs.keys()
135 elist.sort()
136 for ext in elist:
137 print ext + ':'
138 flist = undefs[ext]
139 flist.sort()
140 for file in flist:
141 print '\t' + file
142
143# Print warning messages about names defined in more than one file.
144#
145def warndups():
146 savestdout = sys.stdout
147 sys.stdout = sys.stderr
148 names = def2file.keys()
149 names.sort()
150 for name in names:
151 if len(def2file[name]) > 1:
152 print 'warning:', name, 'multiply defined:',
153 print flat(def2file[name])
154 sys.stdout = savestdout
155
156# Main program
157#
158def main():
159 try:
160 optlist, args = getopt.getopt(sys.argv[1:], 'cdu')
161 except getopt.error:
162 sys.stdout = sys.stderr
163 print 'Usage:', path.basename(sys.argv[0]), '[-cdu] [file] ...'
164 print '-c: print callers per objectfile'
165 print '-d: print callees per objectfile'
166 print '-u: print usage of undefined symbols'
167 print 'If none of -cdu is specified, all are assumed.'
168 print 'Use "nm -o" to generate the input (on IRIX: "nm -Bo"),'
169 print 'e.g.: nm -o /lib/libc.a | objgraph'
170 return 1
171 optu = optc = optd = 0
172 for opt, void in optlist:
173 if opt = '-u':
174 optu = 1
175 elif opt = '-c':
176 optc = 1
177 elif opt = '-d':
178 optd = 1
179 if optu = optc = optd = 0:
180 optu = optc = optd = 1
181 if not args:
182 args = ['-']
183 for file in args:
184 if file = '-':
185 readinput(sys.stdin)
186 else:
187 readinput(open(file, 'r'))
188 #
189 warndups()
190 #
191 more = (optu + optc + optd > 1)
192 if optd:
193 if more:
194 print '---------------All callees------------------'
195 printcallee()
196 if optu:
197 if more:
198 print '---------------Undefined callees------------'
199 printundef()
200 if optc:
201 if more:
202 print '---------------All Callers------------------'
203 printcaller()
204 return 0
205
206# Call the main program.
207# Use its return value as exit status.
208# Catch interrupts to avoid stack trace.
209#
210try:
211 sys.exit(main())
212except KeyboardInterrupt:
213 sys.exit(1)