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 # This script uses `fix-stacks` to post-process the entries produced by
8 # MozFormatCodeAddress().
15 from subprocess
import PIPE
, Popen
17 # Matches lines produced by MozFormatCodeAddress(), e.g.
18 # `#01: ???[tests/example +0x43a0]`.
19 line_re
= re
.compile(r
"#\d+: .+\[.+ \+0x[0-9A-Fa-f]+\]")
26 from mozbuild
.configure
import ConfigureSandbox
28 sandbox
= ConfigureSandbox(
33 "--host={}".format(buildconfig
.substs
["HOST_ALIAS"]),
36 moz_configure
= os
.path
.join(buildconfig
.topsrcdir
, "build", "moz.configure")
37 sandbox
.include_file(os
.path
.join(moz_configure
, "init.configure"))
38 sandbox
.include_file(os
.path
.join(moz_configure
, "bootstrap.configure"))
39 # Expand the `bootstrap_path` template for "fix-stacks", and execute the
40 # expanded function via `_value_for`, which will trigger autobootstrap.
41 sandbox
._value
_for
(sandbox
["bootstrap_path"]("fix-stacks"))
44 def initFixStacks(jsonMode
, slowWarning
, breakpadSymsDir
, hide_errors
):
45 # Look in MOZ_FETCHES_DIR (for automation), then in MOZBUILD_STATE_PATH
46 # (for a local build where the user has that set), then in ~/.mozbuild
47 # (for a local build with default settings).
48 base
= os
.environ
.get(
50 os
.environ
.get("MOZBUILD_STATE_PATH", os
.path
.expanduser("~/.mozbuild")),
52 fix_stacks_exe
= base
+ "/fix-stacks/fix-stacks"
53 if platform
.system() == "Windows":
54 fix_stacks_exe
= fix_stacks_exe
+ ".exe"
56 if not (os
.path
.isfile(fix_stacks_exe
) and os
.access(fix_stacks_exe
, os
.X_OK
)):
60 # We're out-of-tree (e.g. tests tasks on CI) and can't autobootstrap
61 # (we shouldn't anyways).
64 if not (os
.path
.isfile(fix_stacks_exe
) and os
.access(fix_stacks_exe
, os
.X_OK
)):
65 raise Exception("cannot find `fix-stacks`; please run `./mach bootstrap`")
67 args
= [fix_stacks_exe
]
72 args
.append(breakpadSymsDir
)
74 # Sometimes we need to prevent errors from going to stderr.
75 stderr
= open(os
.devnull
) if hide_errors
else None
79 args
, stdin
=PIPE
, stdout
=PIPE
, stderr
=stderr
, universal_newlines
=True
82 # Shut down the fix_stacks process on exit. We use `terminate()`
83 # because it is more forceful than `wait()`, and the Python docs warn
84 # about possible deadlocks with `wait()`.
85 def cleanup(fix_stacks
):
86 for fn
in [fix_stacks
.stdin
.close
, fix_stacks
.terminate
]:
92 atexit
.register(cleanup
, fix_stacks
)
96 "Initializing stack-fixing for the first stack frame, this may take a while..."
101 line
, jsonMode
=False, slowWarning
=False, breakpadSymsDir
=None, hide_errors
=False
103 is_bytes
= isinstance(line
, bytes
)
104 line_str
= line
.decode("utf-8") if is_bytes
else line
105 if line_re
.search(line_str
) is None:
109 initFixStacks(jsonMode
, slowWarning
, breakpadSymsDir
, hide_errors
)
111 # Sometimes `line` is lacking a trailing newline. If we pass such a `line`
112 # to `fix-stacks` it will wait until it receives a newline, causing this
113 # script to hang. So we add a newline if one is missing and then remove it
115 is_missing_newline
= not line_str
.endswith("\n")
116 if is_missing_newline
:
117 line_str
= line_str
+ "\n"
118 fix_stacks
.stdin
.write(line_str
)
119 fix_stacks
.stdin
.flush()
120 out
= fix_stacks
.stdout
.readline()
121 if is_missing_newline
:
124 if is_bytes
and not isinstance(out
, bytes
):
125 out
= out
.encode("utf-8")
129 if __name__
== "__main__":
130 bpsyms
= os
.environ
.get("BREAKPAD_SYMBOLS_PATH", None)
131 for line
in sys
.stdin
:
132 sys
.stdout
.write(fixSymbols(line
, breakpadSymsDir
=bpsyms
))