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