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/.
7 from __future__
import with_statement
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(":")
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)
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
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)
44 elif line
.startswith("PUBLIC "):
45 # PUBLIC <address> <stack_param_size> <name>
46 (junk
, rva
, ss
, name
) = line
.split(None, 3)
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)
60 name
= lastFuncName
+ " [" + file + line
+ "]"
63 # skip everything else
64 #print "Loaded %d functions from symbol file %s" % (len(funcs), os.path.basename(fn))
65 self
.addrs
= sorted(addrs
)
68 def addrToSymbol(self
, address
):
69 i
= bisect
.bisect(self
.addrs
, address
) - 1
71 #offset = address - self.addrs[i]
72 return self
.funcs
[self
.addrs
[i
]]
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
):
82 d1
= os
.path
.join(symbolsDir
, fn
)
83 if not os
.path
.exists(d1
):
85 uuids
= os
.listdir(d1
)
87 raise Exception("Missing symbol file for " + fn
)
89 raise Exception("Ambiguous symbol file for " + fn
)
90 if fn
.endswith(".pdb"):
92 return os
.path
.join(d1
, uuids
[0], fn
+ ".sym")
94 parsedSymbolFiles
= {}
95 def getSymbolFile(file, symbolsDir
):
97 if not file in parsedSymbolFiles
:
98 symfile
= guessSymbolFile(file, symbolsDir
)
100 p
= SymbolFile(symfile
)
103 parsedSymbolFiles
[file] = p
105 p
= parsedSymbolFiles
[file]
108 def addressToSymbol(file, address
, symbolsDir
):
109 p
= getSymbolFile(file, symbolsDir
)
111 return p
.addrToSymbol(address
)
115 line_re
= re
.compile("^(.*) ?\[([^ ]*) \+(0x[0-9A-F]{1,16})\](.*)$")
116 balance_tree_re
= re
.compile("^([ \|0-9-]*)")
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
)
129 symbol
= "%s + 0x%x" % (os
.path
.basename(file), address
)
130 return before
+ symbol
+ after
+ "\n"
134 if __name__
== "__main__":
135 symbolsDir
= sys
.argv
[1]
136 for line
in iter(sys
.stdin
.readline
, ''):
137 print fixSymbols(line
, symbolsDir
),