Update to 2.7.3
[profile/ivi/python.git] / Tools / scripts / objgraph.py
1 #! /usr/bin/env 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
22 import sys
23 import os
24 import getopt
25 import re
26
27 # Types of symbols.
28 #
29 definitions = 'TRGDSBAEC'
30 externals = 'UV'
31 ignore = 'Nntrgdsbavuc'
32
33 # Regular expression to parse "nm -o" output.
34 #
35 matcher = re.compile('(.*):\t?........ (.) (.*)$')
36
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 #
41 def store(dict, key, item):
42     if dict.has_key(key):
43         dict[key].append(item)
44     else:
45         dict[key] = [item]
46
47 # Return a flattened version of a list of strings: the concatenation
48 # of its elements with intervening spaces.
49 #
50 def flat(list):
51     s = ''
52     for item in list:
53         s = s + ' ' + item
54     return s[1:]
55
56 # Global variables mapping defined/undefined names to files and back.
57 #
58 file2undef = {}
59 def2file = {}
60 file2def = {}
61 undef2file = {}
62
63 # Read one input file and merge the data into the tables.
64 # Argument is an open file.
65 #
66 def readinput(fp):
67     while 1:
68         s = fp.readline()
69         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:
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 #
88 def printcallee():
89     flist = file2undef.keys()
90     flist.sort()
91     for filename in flist:
92         print filename + ':'
93         elist = file2undef[filename]
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 #
107 def printcaller():
108     files = file2def.keys()
109     files.sort()
110     for filename in files:
111         callers = []
112         for label in file2def[filename]:
113             if undef2file.has_key(label):
114                 callers = callers + undef2file[label]
115         if callers:
116             callers.sort()
117             print filename + ':'
118             lastfn = ''
119             for fn in callers:
120                 if fn <> lastfn:
121                     print '\t' + fn
122                 lastfn = fn
123         else:
124             print filename + ': unused'
125
126 # Print undefined names and where they are used.
127 #
128 def printundef():
129     undefs = {}
130     for filename in file2undef.keys():
131         for ext in file2undef[filename]:
132             if not def2file.has_key(ext):
133                 store(undefs, ext, filename)
134     elist = undefs.keys()
135     elist.sort()
136     for ext in elist:
137         print ext + ':'
138         flist = undefs[ext]
139         flist.sort()
140         for filename in flist:
141             print '\t' + filename
142
143 # Print warning messages about names defined in more than one file.
144 #
145 def 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 #
158 def main():
159     try:
160         optlist, args = getopt.getopt(sys.argv[1:], 'cdu')
161     except getopt.error:
162         sys.stdout = sys.stderr
163         print 'Usage:', os.path.basename(sys.argv[0]),
164         print           '[-cdu] [file] ...'
165         print '-c: print callers per objectfile'
166         print '-d: print callees per objectfile'
167         print '-u: print usage of undefined symbols'
168         print 'If none of -cdu is specified, all are assumed.'
169         print 'Use "nm -o" to generate the input (on IRIX: "nm -Bo"),'
170         print 'e.g.: nm -o /lib/libc.a | objgraph'
171         return 1
172     optu = optc = optd = 0
173     for opt, void in optlist:
174         if opt == '-u':
175             optu = 1
176         elif opt == '-c':
177             optc = 1
178         elif opt == '-d':
179             optd = 1
180     if optu == optc == optd == 0:
181         optu = optc = optd = 1
182     if not args:
183         args = ['-']
184     for filename in args:
185         if filename == '-':
186             readinput(sys.stdin)
187         else:
188             readinput(open(filename, 'r'))
189     #
190     warndups()
191     #
192     more = (optu + optc + optd > 1)
193     if optd:
194         if more:
195             print '---------------All callees------------------'
196         printcallee()
197     if optu:
198         if more:
199             print '---------------Undefined callees------------'
200         printundef()
201     if optc:
202         if more:
203             print '---------------All Callers------------------'
204         printcaller()
205     return 0
206
207 # Call the main program.
208 # Use its return value as exit status.
209 # Catch interrupts to avoid stack trace.
210 #
211 if __name__ == '__main__':
212     try:
213         sys.exit(main())
214     except KeyboardInterrupt:
215         sys.exit(1)