Source code

Revision control

Copy as Markdown

Other Tools

# 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 https://mozilla.org/MPL/2.0/.
"""Canonical hang-signature keys, shared by the BHR aggregation jobs.
A hang signature is identified by a content-based key derived from its
reconstructed stack: the ordered list of (funcName, libName) frames, leaf->root.
funcTable indices are per-file and cannot be used.
The key has to stay byte-for-byte identical everywhere it is derived, including
the dashboard outside this repo, because that is what joins a displayed hang to
its rolled-up numbers. It lives here so there is a single implementation to keep
in step rather than one per job.
"""
# Frame and stack separators for the canonical key. Control characters that
# cannot appear in a symbol or library name, so the join is unambiguous.
FIELD_SEP = "\x1f"
FRAME_SEP = "\x1e"
def canonical_key(frames):
"""Stable cross-day signature key for a leaf->root list of [name, lib]."""
return FRAME_SEP.join(f"{name}{FIELD_SEP}{lib}" for name, lib in frames)
def reconstruct_stack(thread, sample_index):
"""Return a sample's stack as [funcName, libName] pairs, leaf->root.
Uses the library's stripped ``name`` (not ``debugName``) so the derived key
matches the frontend.
"""
frames, _ = reconstruct_stack_indexed(thread, sample_index)
return frames
def reconstruct_stack_indexed(thread, sample_index):
"""Return a sample's stack as (frames, func_indices), both leaf->root.
Same walk as reconstruct_stack, additionally handing back each frame's
funcTable index. Callers that emit a stack into the artifact use the
indices, since the profile already interns those strings and repeating them
per frame is what made leafGroups dominate the file.
"""
string_array = thread["stringArray"]
libs = thread["libs"]
func_name = thread["funcTable"]["name"]
func_lib = thread["funcTable"]["lib"]
prefix = thread["stackTable"]["prefix"]
func = thread["stackTable"]["func"]
frames = []
func_indices = []
stack = thread["sampleTable"]["stack"][sample_index]
while stack:
func_index = func[stack]
name = string_array[func_name[func_index]]
lib_index = func_lib[func_index]
lib = "" if lib_index is None else libs[lib_index]["name"]
frames.append([name, lib])
func_indices.append(func_index)
stack = prefix[stack]
return frames, func_indices