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("#\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 # bootstrap_search_path_order has a dependency on developer_options, which
39 # is not defined in init.configure. Its value doesn't matter for us, though.
40 sandbox
["developer_options"] = sandbox
["always"]
41 sandbox
.include_file(os
.path
.join(moz_configure
, "bootstrap.configure"))
42 # Expand the `bootstrap_path` template for "fix-stacks", and execute the
43 # expanded function via `_value_for`, which will trigger autobootstrap.
44 sandbox
._value
_for
(sandbox
["bootstrap_path"]("fix-stacks"))
47 def initFixStacks(jsonMode
, slowWarning
, breakpadSymsDir
, hide_errors
):
48 # Look in MOZ_FETCHES_DIR (for automation), then in MOZBUILD_STATE_PATH
49 # (for a local build where the user has that set), then in ~/.mozbuild
50 # (for a local build with default settings).
51 base
= os
.environ
.get(
53 os
.environ
.get("MOZBUILD_STATE_PATH", os
.path
.expanduser("~/.mozbuild")),
55 fix_stacks_exe
= base
+ "/fix-stacks/fix-stacks"
56 if platform
.system() == "Windows":
57 fix_stacks_exe
= fix_stacks_exe
+ ".exe"
59 if not (os
.path
.isfile(fix_stacks_exe
) and os
.access(fix_stacks_exe
, os
.X_OK
)):
63 # We're out-of-tree (e.g. tests tasks on CI) and can't autobootstrap
64 # (we shouldn't anyways).
67 if not (os
.path
.isfile(fix_stacks_exe
) and os
.access(fix_stacks_exe
, os
.X_OK
)):
68 raise Exception("cannot find `fix-stacks`; please run `./mach bootstrap`")
70 args
= [fix_stacks_exe
]
75 args
.append(breakpadSymsDir
)
77 # Sometimes we need to prevent errors from going to stderr.
78 stderr
= open(os
.devnull
) if hide_errors
else None
82 args
, stdin
=PIPE
, stdout
=PIPE
, stderr
=stderr
, universal_newlines
=True
85 # Shut down the fix_stacks process on exit. We use `terminate()`
86 # because it is more forceful than `wait()`, and the Python docs warn
87 # about possible deadlocks with `wait()`.
88 def cleanup(fix_stacks
):
89 for fn
in [fix_stacks
.stdin
.close
, fix_stacks
.terminate
]:
95 atexit
.register(cleanup
, fix_stacks
)
99 "Initializing stack-fixing for the first stack frame, this may take a while..."
104 line
, jsonMode
=False, slowWarning
=False, breakpadSymsDir
=None, hide_errors
=False
106 is_bytes
= isinstance(line
, bytes
)
107 line_str
= line
.decode("utf-8") if is_bytes
else line
108 if line_re
.search(line_str
) is None:
112 initFixStacks(jsonMode
, slowWarning
, breakpadSymsDir
, hide_errors
)
114 # Sometimes `line` is lacking a trailing newline. If we pass such a `line`
115 # to `fix-stacks` it will wait until it receives a newline, causing this
116 # script to hang. So we add a newline if one is missing and then remove it
118 is_missing_newline
= not line_str
.endswith("\n")
119 if is_missing_newline
:
120 line_str
= line_str
+ "\n"
121 fix_stacks
.stdin
.write(line_str
)
122 fix_stacks
.stdin
.flush()
123 out
= fix_stacks
.stdout
.readline()
124 if is_missing_newline
:
127 if is_bytes
and not isinstance(out
, bytes
):
128 out
= out
.encode("utf-8")
132 if __name__
== "__main__":
133 bpsyms
= os
.environ
.get("BREAKPAD_SYMBOLS_PATH", None)
134 for line
in sys
.stdin
:
135 sys
.stdout
.write(fixSymbols(line
, breakpadSymsDir
=bpsyms
))