diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /js/src/gc/GenerateStatsPhases.py | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | js/src/gc/GenerateStatsPhases.py | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/js/src/gc/GenerateStatsPhases.py b/js/src/gc/GenerateStatsPhases.py new file mode 100644 index 0000000000..6396d409b0 --- /dev/null +++ b/js/src/gc/GenerateStatsPhases.py @@ -0,0 +1,407 @@ +# 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/. + +# flake8: noqa: F821 + +# Generate graph structures for GC statistics recording. +# +# Stats phases are nested and form a directed acyclic graph starting +# from a set of root phases. Importantly, a phase may appear under more +# than one parent phase. +# +# For example, the following arrangement is possible: +# +# +---+ +# | A | +# +---+ +# | +# +-------+-------+ +# | | | +# v v v +# +---+ +---+ +---+ +# | B | | C | | D | +# +---+ +---+ +---+ +# | | +# +---+---+ +# | +# v +# +---+ +# | E | +# +---+ +# +# This graph is expanded into a tree (or really a forest) and phases +# with multiple parents are duplicated. +# +# For example, the input example above would be expanded to: +# +# +---+ +# | A | +# +---+ +# | +# +-------+-------+ +# | | | +# v v v +# +---+ +---+ +---+ +# | B | | C | | D | +# +---+ +---+ +---+ +# | | +# v v +# +---+ +---+ +# | E | | E'| +# +---+ +---+ + +# NOTE: If you add new phases here the current next phase kind number can be +# found at the end of js/src/gc/StatsPhasesGenerated.inc + +import collections +import re + + +class PhaseKind: + def __init__(self, name, descr, bucket, children=[]): + self.name = name + self.descr = descr + # For telemetry + self.bucket = bucket + self.children = children + + +AllPhaseKinds = [] +PhaseKindsByName = dict() + + +def addPhaseKind(name, descr, bucket, children=[]): + assert name not in PhaseKindsByName + phaseKind = PhaseKind(name, descr, bucket, children) + AllPhaseKinds.append(phaseKind) + PhaseKindsByName[name] = phaseKind + return phaseKind + + +def getPhaseKind(name): + return PhaseKindsByName[name] + + +PhaseKindGraphRoots = [ + addPhaseKind("MUTATOR", "Mutator Running", 0), + addPhaseKind("GC_BEGIN", "Begin Callback", 1), + addPhaseKind( + "EVICT_NURSERY_FOR_MAJOR_GC", + "Evict Nursery For Major GC", + 70, + [ + addPhaseKind( + "MARK_ROOTS", + "Mark Roots", + 48, + [ + addPhaseKind("MARK_CCWS", "Mark Cross Compartment Wrappers", 50), + addPhaseKind("MARK_STACK", "Mark C and JS stacks", 51), + addPhaseKind("MARK_RUNTIME_DATA", "Mark Runtime-wide Data", 52), + addPhaseKind("MARK_EMBEDDING", "Mark Embedding", 53), + ], + ) + ], + ), + addPhaseKind("WAIT_BACKGROUND_THREAD", "Wait Background Thread", 2), + addPhaseKind( + "PREPARE", + "Prepare For Collection", + 69, + [ + addPhaseKind("UNMARK", "Unmark", 7), + addPhaseKind("UNMARK_WEAKMAPS", "Unmark WeakMaps", 76), + addPhaseKind("BUFFER_GRAY_ROOTS", "Buffer Gray Roots", 49), + addPhaseKind("MARK_DISCARD_CODE", "Mark Discard Code", 3), + addPhaseKind("RELAZIFY_FUNCTIONS", "Relazify Functions", 4), + addPhaseKind("PURGE", "Purge", 5), + addPhaseKind("PURGE_PROP_MAP_TABLES", "Purge PropMapTables", 60), + addPhaseKind("PURGE_SOURCE_URLS", "Purge Source URLs", 73), + addPhaseKind("JOIN_PARALLEL_TASKS", "Join Parallel Tasks", 67), + ], + ), + addPhaseKind( + "MARK", + "Mark", + 6, + [ + getPhaseKind("MARK_ROOTS"), + addPhaseKind("MARK_DELAYED", "Mark Delayed", 8), + getPhaseKind("JOIN_PARALLEL_TASKS"), + ], + ), + addPhaseKind( + "SWEEP", + "Sweep", + 9, + [ + addPhaseKind( + "SWEEP_MARK", + "Mark During Sweeping", + 10, + [ + getPhaseKind("MARK_DELAYED"), + addPhaseKind( + "SWEEP_MARK_WEAK", + "Mark Weak", + 13, + [ + getPhaseKind("MARK_DELAYED"), + addPhaseKind( + "SWEEP_MARK_GRAY_WEAK", "Mark Gray and Weak", 16 + ), + ], + ), + addPhaseKind( + "SWEEP_MARK_INCOMING_GRAY", "Mark Incoming Gray Pointers", 14 + ), + addPhaseKind("SWEEP_MARK_GRAY", "Mark Gray", 15), + getPhaseKind("JOIN_PARALLEL_TASKS"), + ], + ), + addPhaseKind( + "FINALIZE_START", + "Finalize Start Callbacks", + 17, + [ + addPhaseKind("WEAK_ZONES_CALLBACK", "Per-Slice Weak Callback", 57), + addPhaseKind( + "WEAK_COMPARTMENT_CALLBACK", "Per-Compartment Weak Callback", 58 + ), + ], + ), + addPhaseKind("UPDATE_ATOMS_BITMAP", "Sweep Atoms Bitmap", 68), + addPhaseKind("SWEEP_ATOMS_TABLE", "Sweep Atoms Table", 18), + addPhaseKind( + "SWEEP_COMPARTMENTS", + "Sweep Compartments", + 20, + [ + addPhaseKind("SWEEP_DISCARD_CODE", "Sweep Discard Code", 21), + addPhaseKind("SWEEP_INNER_VIEWS", "Sweep Inner Views", 22), + addPhaseKind( + "SWEEP_CC_WRAPPER", "Sweep Cross Compartment Wrappers", 23 + ), + addPhaseKind("SWEEP_BASE_SHAPE", "Sweep Base Shapes", 24), + addPhaseKind("SWEEP_INITIAL_SHAPE", "Sweep Initial Shapes", 25), + addPhaseKind("SWEEP_REGEXP", "Sweep Regexps", 28), + addPhaseKind("SWEEP_COMPRESSION", "Sweep Compression Tasks", 62), + addPhaseKind("SWEEP_WEAKMAPS", "Sweep WeakMaps", 63), + addPhaseKind("SWEEP_UNIQUEIDS", "Sweep Unique IDs", 64), + addPhaseKind( + "SWEEP_FINALIZATION_OBSERVERS", + "Sweep FinalizationRegistries and WeakRefs", + 74, + ), + addPhaseKind("SWEEP_JIT_DATA", "Sweep JIT Data", 65), + addPhaseKind("SWEEP_WEAK_CACHES", "Sweep Weak Caches", 66), + addPhaseKind("SWEEP_MISC", "Sweep Miscellaneous", 29), + getPhaseKind("JOIN_PARALLEL_TASKS"), + ], + ), + addPhaseKind("FINALIZE_OBJECT", "Finalize Objects", 33), + addPhaseKind("FINALIZE_NON_OBJECT", "Finalize Non-objects", 34), + addPhaseKind("SWEEP_PROP_MAP", "Sweep PropMap Tree", 77), + addPhaseKind("FINALIZE_END", "Finalize End Callback", 38), + addPhaseKind("DESTROY", "Deallocate", 39), + getPhaseKind("JOIN_PARALLEL_TASKS"), + addPhaseKind("FIND_DEAD_COMPARTMENTS", "Find Dead Compartments", 54), + ], + ), + addPhaseKind( + "COMPACT", + "Compact", + 40, + [ + addPhaseKind("COMPACT_MOVE", "Compact Move", 41), + addPhaseKind( + "COMPACT_UPDATE", + "Compact Update", + 42, + [ + getPhaseKind("MARK_ROOTS"), + addPhaseKind("COMPACT_UPDATE_CELLS", "Compact Update Cells", 43), + getPhaseKind("JOIN_PARALLEL_TASKS"), + ], + ), + ], + ), + addPhaseKind("DECOMMIT", "Decommit", 72), + addPhaseKind("GC_END", "End Callback", 44), + addPhaseKind( + "MINOR_GC", + "All Minor GCs", + 45, + [ + getPhaseKind("MARK_ROOTS"), + ], + ), + addPhaseKind( + "EVICT_NURSERY", + "Minor GCs to Evict Nursery", + 46, + [ + getPhaseKind("MARK_ROOTS"), + ], + ), + addPhaseKind( + "TRACE_HEAP", + "Trace Heap", + 47, + [ + getPhaseKind("MARK_ROOTS"), + ], + ), +] + + +class Phase: + # Expand the DAG into a tree, duplicating phases which have more than + # one parent. + def __init__(self, phaseKind, parent): + self.phaseKind = phaseKind + self.parent = parent + self.depth = parent.depth + 1 if parent else 0 + self.children = [] + self.nextSibling = None + self.nextInPhaseKind = None + + self.path = re.sub(r"\W+", "_", phaseKind.name.lower()) + if parent is not None: + self.path = parent.path + "." + self.path + + +def expandPhases(): + phases = [] + phasesForKind = collections.defaultdict(list) + + def traverse(phaseKind, parent): + ep = Phase(phaseKind, parent) + phases.append(ep) + + # Update list of expanded phases for this phase kind. + if phasesForKind[phaseKind]: + phasesForKind[phaseKind][-1].nextInPhaseKind = ep + phasesForKind[phaseKind].append(ep) + + # Recurse over children. + for child in phaseKind.children: + child_ep = traverse(child, ep) + if ep.children: + ep.children[-1].nextSibling = child_ep + ep.children.append(child_ep) + return ep + + for phaseKind in PhaseKindGraphRoots: + traverse(phaseKind, None) + + return phases, phasesForKind + + +AllPhases, PhasesForPhaseKind = expandPhases() + +# Name phases based on phase kind name and index if there are multiple phases +# corresponding to a single phase kind. + +for phaseKind in AllPhaseKinds: + phases = PhasesForPhaseKind[phaseKind] + if len(phases) == 1: + phases[0].name = "%s" % phaseKind.name + else: + for index, phase in enumerate(phases): + phase.name = "%s_%d" % (phaseKind.name, index + 1) + +# Find the maximum phase nesting. +MaxPhaseNesting = max(phase.depth for phase in AllPhases) + 1 + +# And the maximum bucket number. +MaxBucket = max(kind.bucket for kind in AllPhaseKinds) + +# Generate code. + + +def writeList(out, items): + if items: + out.write(",\n".join(" " + item for item in items) + "\n") + + +def writeEnumClass(out, name, type, items, extraItems): + items = ["FIRST"] + list(items) + ["LIMIT"] + list(extraItems) + items[1] += " = " + items[0] + out.write("enum class %s : %s {\n" % (name, type)) + writeList(out, items) + out.write("};\n") + + +def generateHeader(out): + # + # Generate PhaseKind enum. + # + phaseKindNames = map(lambda phaseKind: phaseKind.name, AllPhaseKinds) + extraPhaseKinds = [ + "NONE = LIMIT", + "EXPLICIT_SUSPENSION = LIMIT", + "IMPLICIT_SUSPENSION", + ] + writeEnumClass(out, "PhaseKind", "uint8_t", phaseKindNames, extraPhaseKinds) + out.write("\n") + + # + # Generate Phase enum. + # + phaseNames = map(lambda phase: phase.name, AllPhases) + extraPhases = ["NONE = LIMIT", "EXPLICIT_SUSPENSION = LIMIT", "IMPLICIT_SUSPENSION"] + writeEnumClass(out, "Phase", "uint8_t", phaseNames, extraPhases) + out.write("\n") + + # + # Generate MAX_PHASE_NESTING constant. + # + out.write("static const size_t MAX_PHASE_NESTING = %d;\n" % MaxPhaseNesting) + + +def generateCpp(out): + # + # Generate the PhaseKindInfo table. + # + out.write("static constexpr PhaseKindTable phaseKinds = {\n") + for phaseKind in AllPhaseKinds: + phase = PhasesForPhaseKind[phaseKind][0] + out.write( + ' /* PhaseKind::%s */ PhaseKindInfo { Phase::%s, %d, "%s" },\n' + % (phaseKind.name, phase.name, phaseKind.bucket, phaseKind.name) + ) + out.write("};\n") + out.write("\n") + + # + # Generate the PhaseInfo tree. + # + def name(phase): + return "Phase::" + phase.name if phase else "Phase::NONE" + + out.write("static constexpr PhaseTable phases = {\n") + for phase in AllPhases: + firstChild = phase.children[0] if phase.children else None + phaseKind = phase.phaseKind + out.write( + ' /* %s */ PhaseInfo { %s, %s, %s, %s, PhaseKind::%s, %d, "%s", "%s" },\n' + % ( # NOQA: E501 + name(phase), + name(phase.parent), + name(firstChild), + name(phase.nextSibling), + name(phase.nextInPhaseKind), + phaseKind.name, + phase.depth, + phaseKind.descr, + phase.path, + ) + ) + out.write("};\n") + + # + # Print in a comment the next available phase kind number. + # + out.write("// The next available phase kind number is: %d\n" % (MaxBucket + 1)) |