|
1 #!/usr/bin/env python |
|
2 |
|
3 # This Source Code Form is subject to the terms of the Mozilla Public |
|
4 # License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
6 |
|
7 from __future__ import with_statement |
|
8 |
|
9 import sys |
|
10 import os |
|
11 import re |
|
12 import bisect |
|
13 |
|
14 def prettyFileName(name): |
|
15 if name.startswith("../") or name.startswith("..\\"): |
|
16 # dom_quickstubs.cpp and many .h files show up with relative paths that are useless |
|
17 # and/or don't correspond to the layout of the source tree. |
|
18 return os.path.basename(name) + ":" |
|
19 elif name.startswith("hg:"): |
|
20 bits = name.split(":") |
|
21 if len(bits) == 4: |
|
22 (junk, repo, path, rev) = bits |
|
23 # We could construct an hgweb URL with /file/ or /annotate/, like this: |
|
24 # return "http://%s/annotate/%s/%s#l" % (repo, rev, path) |
|
25 return path + ":" |
|
26 return name + ":" |
|
27 |
|
28 class SymbolFile: |
|
29 def __init__(self, fn): |
|
30 addrs = [] # list of addresses, which will be sorted once we're done initializing |
|
31 funcs = {} # hash: address --> (function name + possible file/line) |
|
32 files = {} # hash: filenum (string) --> prettified filename ready to have a line number appended |
|
33 with open(fn) as f: |
|
34 for line in f: |
|
35 line = line.rstrip() |
|
36 # http://code.google.com/p/google-breakpad/wiki/SymbolFiles |
|
37 if line.startswith("FUNC "): |
|
38 # FUNC <address> <size> <stack_param_size> <name> |
|
39 (junk, rva, size, ss, name) = line.split(None, 4) |
|
40 rva = int(rva,16) |
|
41 funcs[rva] = name |
|
42 addrs.append(rva) |
|
43 lastFuncName = name |
|
44 elif line.startswith("PUBLIC "): |
|
45 # PUBLIC <address> <stack_param_size> <name> |
|
46 (junk, rva, ss, name) = line.split(None, 3) |
|
47 rva = int(rva,16) |
|
48 funcs[rva] = name |
|
49 addrs.append(rva) |
|
50 elif line.startswith("FILE "): |
|
51 # FILE <number> <name> |
|
52 (junk, filenum, name) = line.split(None, 2) |
|
53 files[filenum] = prettyFileName(name) |
|
54 elif line[0] in "0123456789abcdef": |
|
55 # This is one of the "line records" corresponding to the last FUNC record |
|
56 # <address> <size> <line> <filenum> |
|
57 (rva, size, line, filenum) = line.split(None) |
|
58 rva = int(rva,16) |
|
59 file = files[filenum] |
|
60 name = lastFuncName + " [" + file + line + "]" |
|
61 funcs[rva] = name |
|
62 addrs.append(rva) |
|
63 # skip everything else |
|
64 #print "Loaded %d functions from symbol file %s" % (len(funcs), os.path.basename(fn)) |
|
65 self.addrs = sorted(addrs) |
|
66 self.funcs = funcs |
|
67 |
|
68 def addrToSymbol(self, address): |
|
69 i = bisect.bisect(self.addrs, address) - 1 |
|
70 if i > 0: |
|
71 #offset = address - self.addrs[i] |
|
72 return self.funcs[self.addrs[i]] |
|
73 else: |
|
74 return "" |
|
75 |
|
76 def guessSymbolFile(fn, symbolsDir): |
|
77 """Guess a symbol file based on an object file's basename, ignoring the path and UUID.""" |
|
78 fn = os.path.basename(fn) |
|
79 d1 = os.path.join(symbolsDir, fn) |
|
80 if not os.path.exists(d1): |
|
81 fn = fn + ".pdb" |
|
82 d1 = os.path.join(symbolsDir, fn) |
|
83 if not os.path.exists(d1): |
|
84 return None |
|
85 uuids = os.listdir(d1) |
|
86 if len(uuids) == 0: |
|
87 raise Exception("Missing symbol file for " + fn) |
|
88 if len(uuids) > 1: |
|
89 raise Exception("Ambiguous symbol file for " + fn) |
|
90 if fn.endswith(".pdb"): |
|
91 fn = fn[:-4] |
|
92 return os.path.join(d1, uuids[0], fn + ".sym") |
|
93 |
|
94 parsedSymbolFiles = {} |
|
95 def getSymbolFile(file, symbolsDir): |
|
96 p = None |
|
97 if not file in parsedSymbolFiles: |
|
98 symfile = guessSymbolFile(file, symbolsDir) |
|
99 if symfile: |
|
100 p = SymbolFile(symfile) |
|
101 else: |
|
102 p = None |
|
103 parsedSymbolFiles[file] = p |
|
104 else: |
|
105 p = parsedSymbolFiles[file] |
|
106 return p |
|
107 |
|
108 def addressToSymbol(file, address, symbolsDir): |
|
109 p = getSymbolFile(file, symbolsDir) |
|
110 if p: |
|
111 return p.addrToSymbol(address) |
|
112 else: |
|
113 return "" |
|
114 |
|
115 line_re = re.compile("^(.*) ?\[([^ ]*) \+(0x[0-9A-F]{1,16})\](.*)$") |
|
116 balance_tree_re = re.compile("^([ \|0-9-]*)") |
|
117 |
|
118 def fixSymbols(line, symbolsDir): |
|
119 result = line_re.match(line) |
|
120 if result is not None: |
|
121 # before allows preservation of balance trees |
|
122 # after allows preservation of counts |
|
123 (before, file, address, after) = result.groups() |
|
124 address = int(address, 16) |
|
125 # throw away the bad symbol, but keep balance tree structure |
|
126 before = balance_tree_re.match(before).groups()[0] |
|
127 symbol = addressToSymbol(file, address, symbolsDir) |
|
128 if not symbol: |
|
129 symbol = "%s + 0x%x" % (os.path.basename(file), address) |
|
130 return before + symbol + after + "\n" |
|
131 else: |
|
132 return line |
|
133 |
|
134 if __name__ == "__main__": |
|
135 symbolsDir = sys.argv[1] |
|
136 for line in iter(sys.stdin.readline, ''): |
|
137 print fixSymbols(line, symbolsDir), |