forked from mirrors/gecko-dev
This includes both the vanilla sources we haven't forked and the client sources that we have. Client patches were applied manually up to version 69c2c51dd89965d234eec16e3a9353634831916b. The following changes were not included as they break merging segments corresponding to libxul.so in the module list: 8915f7be39448d9257b6da3ad0233944d1d9a92a 17ad0c18b179c135fc5a3d2bba199c3fa4276035 94b6309aecaddfcf11672f6cfad9575d68ad3b40 With these changes applied two entries for libxul.so are generated, the second one is bogus and prevents symbolication from working correctly. The build system and some of the tools relying on breakpad were also updated to work with the new version. --HG-- extra : source : fe4d49307f8890a0c430c257c96f74a9552eeb31 extra : histedit_source : bc84861445bd93856cd0d0c864fd15ad7d9ccc12%2C1efd65797da46e33481afa61a302098780b0f107
177 lines
6.4 KiB
Python
Executable file
177 lines
6.4 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
# This script uses breakpad symbols to post-process the entries produced by
|
|
# NS_FormatCodeAddress(), which on TBPL builds often lack a file name and a
|
|
# line number (and on Linux even the symbol is often bad).
|
|
|
|
from __future__ import with_statement
|
|
|
|
import sys
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import bisect
|
|
|
|
here = os.path.dirname(__file__)
|
|
|
|
|
|
def prettyFileName(name):
|
|
if name.startswith("../") or name.startswith("..\\"):
|
|
# dom_quickstubs.cpp and many .h files show up with relative paths that are useless
|
|
# and/or don't correspond to the layout of the source tree.
|
|
return os.path.basename(name) + ":"
|
|
elif name.startswith("hg:"):
|
|
bits = name.split(":")
|
|
if len(bits) == 4:
|
|
(junk, repo, path, rev) = bits
|
|
# We could construct an hgweb URL with /file/ or /annotate/, like this:
|
|
# return "http://%s/annotate/%s/%s#l" % (repo, rev, path)
|
|
return path + ":"
|
|
return name + ":"
|
|
|
|
|
|
class SymbolFile:
|
|
def __init__(self, fn):
|
|
addrs = [] # list of addresses, which will be sorted once we're done initializing
|
|
funcs = {} # hash: address --> (function name + possible file/line)
|
|
# hash: filenum (string) --> prettified filename ready to have a line number appended
|
|
files = {}
|
|
with open(fn) as f:
|
|
for line in f:
|
|
line = line.rstrip()
|
|
# https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
|
|
if line.startswith("FUNC "):
|
|
# FUNC [<multiple>] <address> <size> <stack_param_size> <name>
|
|
line = line.replace("FUNC m ", "FUNC ") # Ignore the multiple marker
|
|
bits = line.split(None, 4)
|
|
if len(bits) < 5:
|
|
bits.append('unnamed_function')
|
|
(junk, rva, size, ss, name) = bits
|
|
rva = int(rva, 16)
|
|
funcs[rva] = name
|
|
addrs.append(rva)
|
|
lastFuncName = name
|
|
elif line.startswith("PUBLIC "):
|
|
# PUBLIC [<multiple>] <address> <stack_param_size> <name>
|
|
line = line.replace("PUBLIC m ", "PUBLIC ") # Ignore the multiple marker
|
|
(junk, rva, ss, name) = line.split(None, 3)
|
|
rva = int(rva, 16)
|
|
funcs[rva] = name
|
|
addrs.append(rva)
|
|
elif line.startswith("FILE "):
|
|
# FILE <number> <name>
|
|
(junk, filenum, name) = line.split(None, 2)
|
|
files[filenum] = prettyFileName(name)
|
|
elif line[0] in "0123456789abcdef":
|
|
# This is one of the "line records" corresponding to the last FUNC record
|
|
# <address> <size> <line> <filenum>
|
|
(rva, size, line, filenum) = line.split(None)
|
|
rva = int(rva, 16)
|
|
file = files[filenum]
|
|
name = lastFuncName + " [" + file + line + "]"
|
|
funcs[rva] = name
|
|
addrs.append(rva)
|
|
# skip everything else
|
|
# print "Loaded %d functions from symbol file %s" % (len(funcs), os.path.basename(fn))
|
|
self.addrs = sorted(addrs)
|
|
self.funcs = funcs
|
|
|
|
def addrToSymbol(self, address):
|
|
i = bisect.bisect(self.addrs, address) - 1
|
|
if i > 0:
|
|
# offset = address - self.addrs[i]
|
|
return self.funcs[self.addrs[i]]
|
|
else:
|
|
return ""
|
|
|
|
|
|
def findIdForPath(path):
|
|
"""Finds the breakpad id for the object file at the given path."""
|
|
# We should always be packaged with a "fileid" executable.
|
|
fileid_exe = os.path.join(here, 'fileid')
|
|
if not os.path.isfile(fileid_exe):
|
|
fileid_exe = fileid_exe + '.exe'
|
|
if not os.path.isfile(fileid_exe):
|
|
raise Exception("Could not find fileid executable in %s" % here)
|
|
|
|
if not os.path.isfile(path):
|
|
for suffix in ('.exe', '.dll'):
|
|
if os.path.isfile(path + suffix):
|
|
path = path + suffix
|
|
try:
|
|
return subprocess.check_output([fileid_exe, path]).rstrip()
|
|
except subprocess.CalledProcessError as e:
|
|
raise Exception("Error getting fileid for %s: %s" %
|
|
(path, e.output))
|
|
|
|
|
|
def guessSymbolFile(full_path, symbolsDir):
|
|
"""Guess a symbol file based on an object file's basename, ignoring the path and UUID."""
|
|
fn = os.path.basename(full_path)
|
|
d1 = os.path.join(symbolsDir, fn)
|
|
root, _ = os.path.splitext(fn)
|
|
if os.path.exists(os.path.join(symbolsDir, root) + '.pdb'):
|
|
d1 = os.path.join(symbolsDir, root) + '.pdb'
|
|
fn = root
|
|
if not os.path.exists(d1):
|
|
return None
|
|
uuids = os.listdir(d1)
|
|
if len(uuids) == 0:
|
|
raise Exception("Missing symbol file for " + fn)
|
|
if len(uuids) > 1:
|
|
uuid = findIdForPath(full_path)
|
|
else:
|
|
uuid = uuids[0]
|
|
return os.path.join(d1, uuid, fn + ".sym")
|
|
|
|
|
|
parsedSymbolFiles = {}
|
|
|
|
|
|
def getSymbolFile(file, symbolsDir):
|
|
p = None
|
|
if file not in parsedSymbolFiles:
|
|
symfile = guessSymbolFile(file, symbolsDir)
|
|
if symfile:
|
|
p = SymbolFile(symfile)
|
|
else:
|
|
p = None
|
|
parsedSymbolFiles[file] = p
|
|
else:
|
|
p = parsedSymbolFiles[file]
|
|
return p
|
|
|
|
|
|
def addressToSymbol(file, address, symbolsDir):
|
|
p = getSymbolFile(file, symbolsDir)
|
|
if p:
|
|
return p.addrToSymbol(address)
|
|
else:
|
|
return ""
|
|
|
|
|
|
# Matches lines produced by NS_FormatCodeAddress().
|
|
line_re = re.compile("^(.*#\d+: )(.+)\[(.+) \+(0x[0-9A-Fa-f]+)\](.*)$")
|
|
|
|
|
|
def fixSymbols(line, symbolsDir):
|
|
result = line_re.match(line)
|
|
if result is not None:
|
|
(before, fn, file, address, after) = result.groups()
|
|
address = int(address, 16)
|
|
symbol = addressToSymbol(file, address, symbolsDir)
|
|
if not symbol:
|
|
symbol = "%s + 0x%x" % (os.path.basename(file), address)
|
|
return before + symbol + after + "\n"
|
|
else:
|
|
return line
|
|
|
|
|
|
if __name__ == "__main__":
|
|
symbolsDir = sys.argv[1]
|
|
for line in iter(sys.stdin.readline, ''):
|
|
print fixSymbols(line, symbolsDir),
|