1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
#!/usr/bin/env python3
# vim:sw=4:ts=4:et:
# 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 `fix-stacks` to post-process the entries produced by
# MozFormatCodeAddress().
import atexit
import os
import platform
import re
import sys
from subprocess import PIPE, Popen
# Matches lines produced by MozFormatCodeAddress(), e.g.
# `#01: ???[tests/example +0x43a0]`.
line_re = re.compile(r"#\d+: .+\[.+ \+0x[0-9A-Fa-f]+\]")
fix_stacks = None
def autobootstrap():
import buildconfig
from mozbuild.configure import ConfigureSandbox
sandbox = ConfigureSandbox(
{},
argv=[
"configure",
"--help",
"--host={}".format(buildconfig.substs["HOST_ALIAS"]),
],
)
moz_configure = os.path.join(buildconfig.topsrcdir, "build", "moz.configure")
sandbox.include_file(os.path.join(moz_configure, "init.configure"))
sandbox.include_file(os.path.join(moz_configure, "bootstrap.configure"))
# Expand the `bootstrap_path` template for "fix-stacks", and execute the
# expanded function via `_value_for`, which will trigger autobootstrap.
sandbox._value_for(sandbox["bootstrap_path"]("fix-stacks"))
def initFixStacks(jsonMode, slowWarning, breakpadSymsDir, hide_errors):
# Look in MOZ_FETCHES_DIR (for automation), then in MOZBUILD_STATE_PATH
# (for a local build where the user has that set), then in ~/.mozbuild
# (for a local build with default settings).
base = os.environ.get(
"MOZ_FETCHES_DIR",
os.environ.get("MOZBUILD_STATE_PATH", os.path.expanduser("~/.mozbuild")),
)
fix_stacks_exe = base + "/fix-stacks/fix-stacks"
if platform.system() == "Windows":
fix_stacks_exe = fix_stacks_exe + ".exe"
if not (os.path.isfile(fix_stacks_exe) and os.access(fix_stacks_exe, os.X_OK)):
try:
autobootstrap()
except ImportError:
# We're out-of-tree (e.g. tests tasks on CI) and can't autobootstrap
# (we shouldn't anyways).
pass
if not (os.path.isfile(fix_stacks_exe) and os.access(fix_stacks_exe, os.X_OK)):
raise Exception("cannot find `fix-stacks`; please run `./mach bootstrap`")
args = [fix_stacks_exe]
if jsonMode:
args.append("-j")
if breakpadSymsDir:
args.append("-b")
args.append(breakpadSymsDir)
# Sometimes we need to prevent errors from going to stderr.
stderr = open(os.devnull) if hide_errors else None
global fix_stacks
fix_stacks = Popen(
args, stdin=PIPE, stdout=PIPE, stderr=stderr, universal_newlines=True
)
# Shut down the fix_stacks process on exit. We use `terminate()`
# because it is more forceful than `wait()`, and the Python docs warn
# about possible deadlocks with `wait()`.
def cleanup(fix_stacks):
for fn in [fix_stacks.stdin.close, fix_stacks.terminate]:
try:
fn()
except OSError:
pass
atexit.register(cleanup, fix_stacks)
if slowWarning:
print(
"Initializing stack-fixing for the first stack frame, this may take a while..."
)
def fixSymbols(
line, jsonMode=False, slowWarning=False, breakpadSymsDir=None, hide_errors=False
):
is_bytes = isinstance(line, bytes)
line_str = line.decode("utf-8") if is_bytes else line
if line_re.search(line_str) is None:
return line
if not fix_stacks:
initFixStacks(jsonMode, slowWarning, breakpadSymsDir, hide_errors)
# Sometimes `line` is lacking a trailing newline. If we pass such a `line`
# to `fix-stacks` it will wait until it receives a newline, causing this
# script to hang. So we add a newline if one is missing and then remove it
# from the output.
is_missing_newline = not line_str.endswith("\n")
if is_missing_newline:
line_str = line_str + "\n"
fix_stacks.stdin.write(line_str)
fix_stacks.stdin.flush()
out = fix_stacks.stdout.readline()
if is_missing_newline:
out = out[:-1]
if is_bytes and not isinstance(out, bytes):
out = out.encode("utf-8")
return out
if __name__ == "__main__":
bpsyms = os.environ.get("BREAKPAD_SYMBOLS_PATH", None)
for line in sys.stdin:
sys.stdout.write(fixSymbols(line, breakpadSymsDir=bpsyms))
|