blob: 1e1fce07dd5e3bb5d96347d627a9786fc3008a4d [file] [log] [blame]
Benjamin Peterson90f5ba52010-03-11 22:53:45 +00001#! /usr/bin/env python3
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
Guido van Rossum15f27fb1992-12-10 00:00:58 +000023import os
Guido van Rossumec758ea1991-06-04 20:36:54 +000024import getopt
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000025import re
Guido van Rossumec758ea1991-06-04 20:36:54 +000026
27# Types of symbols.
28#
29definitions = 'TRGDSBAEC'
30externals = 'UV'
31ignore = 'Nntrgdsbavuc'
32
33# Regular expression to parse "nm -o" output.
34#
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000035matcher = re.compile('(.*):\t?........ (.) (.*)$')
Guido van Rossumec758ea1991-06-04 20:36:54 +000036
37# Store "item" in "dict" under "key".
38# The dictionary maps keys to lists of items.
39# If there is no list for the key yet, it is created.
40#
41def store(dict, key, item):
Georg Brandl8efadf52008-05-16 15:23:30 +000042 if key in dict:
Tim Peters70c43782001-01-17 08:48:39 +000043 dict[key].append(item)
44 else:
45 dict[key] = [item]
Guido van Rossumec758ea1991-06-04 20:36:54 +000046
47# Return a flattened version of a list of strings: the concatenation
48# of its elements with intervening spaces.
49#
50def flat(list):
Tim Peters70c43782001-01-17 08:48:39 +000051 s = ''
52 for item in list:
53 s = s + ' ' + item
54 return s[1:]
Guido van Rossumec758ea1991-06-04 20:36:54 +000055
56# Global variables mapping defined/undefined names to files and back.
57#
58file2undef = {}
59def2file = {}
60file2def = {}
61undef2file = {}
62
63# Read one input file and merge the data into the tables.
64# Argument is an open file.
65#
Andrew M. Kuchlingac6df952003-05-13 18:14:25 +000066def readinput(fp):
Tim Peters70c43782001-01-17 08:48:39 +000067 while 1:
Andrew M. Kuchlingac6df952003-05-13 18:14:25 +000068 s = fp.readline()
Tim Peters70c43782001-01-17 08:48:39 +000069 if not s:
70 break
71 # If you get any output from this line,
72 # it is probably caused by an unexpected input line:
73 if matcher.search(s) < 0: s; continue # Shouldn't happen
74 (ra, rb), (r1a, r1b), (r2a, r2b), (r3a, r3b) = matcher.regs[:4]
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:
Collin Winter6afaeb72007-08-03 17:06:41 +000083 print(fn + ':' + name + ': unknown type ' + type)
Guido van Rossumec758ea1991-06-04 20:36:54 +000084
85# Print all names that were undefined in some module and where they are
86# defined.
87#
88def printcallee():
Georg Brandl8efadf52008-05-16 15:23:30 +000089 flist = sorted(file2undef.keys())
Andrew M. Kuchlingac6df952003-05-13 18:14:25 +000090 for filename in flist:
Collin Winter6afaeb72007-08-03 17:06:41 +000091 print(filename + ':')
Andrew M. Kuchlingac6df952003-05-13 18:14:25 +000092 elist = file2undef[filename]
Tim Peters70c43782001-01-17 08:48:39 +000093 elist.sort()
94 for ext in elist:
95 if len(ext) >= 8:
96 tabs = '\t'
97 else:
98 tabs = '\t\t'
Georg Brandl8efadf52008-05-16 15:23:30 +000099 if ext not in def2file:
Collin Winter6afaeb72007-08-03 17:06:41 +0000100 print('\t' + ext + tabs + ' *undefined')
Tim Peters70c43782001-01-17 08:48:39 +0000101 else:
Collin Winter6afaeb72007-08-03 17:06:41 +0000102 print('\t' + ext + tabs + flat(def2file[ext]))
Guido van Rossumec758ea1991-06-04 20:36:54 +0000103
104# Print for each module the names of the other modules that use it.
105#
106def printcaller():
Georg Brandl8efadf52008-05-16 15:23:30 +0000107 files = sorted(file2def.keys())
Andrew M. Kuchlingac6df952003-05-13 18:14:25 +0000108 for filename in files:
Tim Peters70c43782001-01-17 08:48:39 +0000109 callers = []
Andrew M. Kuchlingac6df952003-05-13 18:14:25 +0000110 for label in file2def[filename]:
Georg Brandl8efadf52008-05-16 15:23:30 +0000111 if label in undef2file:
Tim Peters70c43782001-01-17 08:48:39 +0000112 callers = callers + undef2file[label]
113 if callers:
114 callers.sort()
Collin Winter6afaeb72007-08-03 17:06:41 +0000115 print(filename + ':')
Tim Peters70c43782001-01-17 08:48:39 +0000116 lastfn = ''
117 for fn in callers:
Georg Brandl8efadf52008-05-16 15:23:30 +0000118 if fn != lastfn:
Collin Winter6afaeb72007-08-03 17:06:41 +0000119 print('\t' + fn)
Tim Peters70c43782001-01-17 08:48:39 +0000120 lastfn = fn
121 else:
Collin Winter6afaeb72007-08-03 17:06:41 +0000122 print(filename + ': unused')
Guido van Rossumec758ea1991-06-04 20:36:54 +0000123
Andrew M. Kuchlingac6df952003-05-13 18:14:25 +0000124# Print undefined names and where they are used.
Guido van Rossumec758ea1991-06-04 20:36:54 +0000125#
126def printundef():
Tim Peters70c43782001-01-17 08:48:39 +0000127 undefs = {}
Georg Brandl8efadf52008-05-16 15:23:30 +0000128 for filename in list(file2undef.keys()):
Andrew M. Kuchlingac6df952003-05-13 18:14:25 +0000129 for ext in file2undef[filename]:
Georg Brandl8efadf52008-05-16 15:23:30 +0000130 if ext not in def2file:
Andrew M. Kuchlingac6df952003-05-13 18:14:25 +0000131 store(undefs, ext, filename)
Georg Brandl8efadf52008-05-16 15:23:30 +0000132 elist = sorted(undefs.keys())
Tim Peters70c43782001-01-17 08:48:39 +0000133 for ext in elist:
Collin Winter6afaeb72007-08-03 17:06:41 +0000134 print(ext + ':')
Georg Brandl8efadf52008-05-16 15:23:30 +0000135 flist = sorted(undefs[ext])
Andrew M. Kuchlingac6df952003-05-13 18:14:25 +0000136 for filename in flist:
Collin Winter6afaeb72007-08-03 17:06:41 +0000137 print('\t' + filename)
Guido van Rossumec758ea1991-06-04 20:36:54 +0000138
139# Print warning messages about names defined in more than one file.
140#
141def warndups():
Tim Peters70c43782001-01-17 08:48:39 +0000142 savestdout = sys.stdout
143 sys.stdout = sys.stderr
Georg Brandl8efadf52008-05-16 15:23:30 +0000144 names = sorted(def2file.keys())
Tim Peters70c43782001-01-17 08:48:39 +0000145 for name in names:
146 if len(def2file[name]) > 1:
Collin Winter6afaeb72007-08-03 17:06:41 +0000147 print('warning:', name, 'multiply defined:', end=' ')
148 print(flat(def2file[name]))
Tim Peters70c43782001-01-17 08:48:39 +0000149 sys.stdout = savestdout
Guido van Rossumec758ea1991-06-04 20:36:54 +0000150
151# Main program
152#
153def main():
Tim Peters70c43782001-01-17 08:48:39 +0000154 try:
155 optlist, args = getopt.getopt(sys.argv[1:], 'cdu')
156 except getopt.error:
157 sys.stdout = sys.stderr
Collin Winter6afaeb72007-08-03 17:06:41 +0000158 print('Usage:', os.path.basename(sys.argv[0]), end=' ')
159 print('[-cdu] [file] ...')
160 print('-c: print callers per objectfile')
161 print('-d: print callees per objectfile')
162 print('-u: print usage of undefined symbols')
163 print('If none of -cdu is specified, all are assumed.')
164 print('Use "nm -o" to generate the input (on IRIX: "nm -Bo"),')
165 print('e.g.: nm -o /lib/libc.a | objgraph')
Tim Peters70c43782001-01-17 08:48:39 +0000166 return 1
167 optu = optc = optd = 0
168 for opt, void in optlist:
169 if opt == '-u':
170 optu = 1
171 elif opt == '-c':
172 optc = 1
173 elif opt == '-d':
174 optd = 1
175 if optu == optc == optd == 0:
176 optu = optc = optd = 1
177 if not args:
178 args = ['-']
Andrew M. Kuchlingac6df952003-05-13 18:14:25 +0000179 for filename in args:
180 if filename == '-':
Tim Peters70c43782001-01-17 08:48:39 +0000181 readinput(sys.stdin)
182 else:
Andrew M. Kuchlingac6df952003-05-13 18:14:25 +0000183 readinput(open(filename, 'r'))
Tim Peters70c43782001-01-17 08:48:39 +0000184 #
185 warndups()
186 #
187 more = (optu + optc + optd > 1)
188 if optd:
189 if more:
Collin Winter6afaeb72007-08-03 17:06:41 +0000190 print('---------------All callees------------------')
Tim Peters70c43782001-01-17 08:48:39 +0000191 printcallee()
192 if optu:
193 if more:
Collin Winter6afaeb72007-08-03 17:06:41 +0000194 print('---------------Undefined callees------------')
Tim Peters70c43782001-01-17 08:48:39 +0000195 printundef()
196 if optc:
197 if more:
Collin Winter6afaeb72007-08-03 17:06:41 +0000198 print('---------------All Callers------------------')
Tim Peters70c43782001-01-17 08:48:39 +0000199 printcaller()
200 return 0
Guido van Rossumec758ea1991-06-04 20:36:54 +0000201
202# Call the main program.
203# Use its return value as exit status.
204# Catch interrupts to avoid stack trace.
205#
Andrew M. Kuchlinge236b382004-08-09 17:27:55 +0000206if __name__ == '__main__':
207 try:
208 sys.exit(main())
209 except KeyboardInterrupt:
210 sys.exit(1)