mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
98 lines
4.0 KiB
Python
98 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
# MobileGL - tools/device_bench/disagg/components.py
|
|
# Copyright (c) 2025-2026 MobileGL-Dev
|
|
# Licensed under the GNU Lesser General Public License v3.0:
|
|
# https://www.gnu.org/licenses/gpl-3.0.txt
|
|
# https://www.gnu.org/licenses/lgpl-3.0.txt
|
|
# SPDX-License-Identifier: LGPL-3.0-only
|
|
# End of Source File Header
|
|
|
|
"""Inclusive share of fixed components on one thread of a simpleperf profile, converted to
|
|
milliseconds per frame with the thread's measured CPU ms/frame.
|
|
|
|
python components.py <perf.data> <binary_cache> <thread comm> [cpu_ms_per_frame] [--tail-ms=N]
|
|
|
|
A sample counts toward a component when any frame of its call chain matches the component's
|
|
pattern (first match in COMPONENTS order wins for the 'exclusive' column, so the rows of that
|
|
column add up to 100%).
|
|
"""
|
|
import os
|
|
import sys
|
|
from collections import Counter
|
|
|
|
sys.path.insert(0, os.environ.get("SIMPLEPERF_DIR", ""))
|
|
from simpleperf_report_lib import ReportLib # noqa: E402
|
|
|
|
|
|
def tail_start(data, symfs, tail_ms):
|
|
"""Timestamp (ns) where the last `tail_ms` of the recording begins, or 0 for everything."""
|
|
if not tail_ms:
|
|
return 0
|
|
lib = ReportLib()
|
|
lib.SetRecordFile(data)
|
|
lib.SetSymfs(symfs)
|
|
# The end is the replay's last retraced call, not the recording's last sample: the app
|
|
# tears its context down after the benchmark ends, and that must not land in the window.
|
|
last = 0
|
|
while True:
|
|
sample = lib.GetNextSample()
|
|
if sample is None:
|
|
break
|
|
chain = lib.GetCallChainOfCurrentSample()
|
|
if any("retrace::retraceCall" in chain.entries[i].symbol.symbol_name for i in range(chain.nr)):
|
|
last = max(last, sample.time)
|
|
return last - int(tail_ms * 1e6)
|
|
|
|
COMPONENTS = [
|
|
("trace parse (harness)", ("trace::Parser::", "BrotliFile", "trace::Call::~Call", "trace::File::")),
|
|
("validate point", ("MGPipeValidateForVerb",)),
|
|
("backend draw setup", ("SetupWireDraw", "VulkanRenderer::SetupDraw", "DirectGLES::PrepareForDraw")),
|
|
("backend draw other", ("DirectVulkan::DrawElements", "DirectGLES::DrawElements", "VulkanRenderer::DrawElements")),
|
|
("verb port / record sink", ("EmitDrawRecord", "VerbChannel::Port", "RecordVerbSink::")),
|
|
("wire encode (client)", ("ClientSession::Emit", "PipeWireEncoder::")),
|
|
("apply decode", ("PipeWireDecoder::", "PipeApplier::ApplyOne")),
|
|
("apply loop idle/poll", ("ServerLoop::ApplyThreadMain", "ServerLoop::DrainRing")),
|
|
("other GL entry points", ("retrace::retraceCall",)),
|
|
]
|
|
|
|
|
|
def main():
|
|
args = [a for a in sys.argv[1:] if not a.startswith("--tail-ms=")]
|
|
tail = next((float(a.split("=", 1)[1]) for a in sys.argv[1:] if a.startswith("--tail-ms=")), 0.0)
|
|
data, symfs, comm = args[0], args[1], args[2]
|
|
cpu = float(args[3]) if len(args) > 3 else None
|
|
start = tail_start(data, symfs, tail)
|
|
lib = ReportLib()
|
|
lib.SetRecordFile(data)
|
|
lib.SetSymfs(symfs)
|
|
total = 0
|
|
inclusive = Counter()
|
|
exclusive = Counter()
|
|
while True:
|
|
sample = lib.GetNextSample()
|
|
if sample is None:
|
|
break
|
|
if comm not in sample.thread_comm or sample.time < start:
|
|
continue
|
|
total += 1
|
|
frames = [lib.GetSymbolOfCurrentSample().symbol_name]
|
|
chain = lib.GetCallChainOfCurrentSample()
|
|
frames += [chain.entries[i].symbol.symbol_name for i in range(chain.nr)]
|
|
first = None
|
|
for name, patterns in COMPONENTS:
|
|
if any(p in f for f in frames for p in patterns):
|
|
inclusive[name] += 1
|
|
if first is None:
|
|
first = name
|
|
exclusive[first or "rest"] += 1
|
|
print(f"{comm}: {total} samples" + (f", {cpu:.2f} ms/frame" if cpu else ""))
|
|
for name, _ in COMPONENTS + [("rest", ())]:
|
|
inc = 100.0 * inclusive[name] / max(1, total)
|
|
exc = 100.0 * exclusive[name] / max(1, total)
|
|
ms = f" {cpu * exc / 100.0:6.2f} ms" if cpu else ""
|
|
print(f" {name:28} incl {inc:5.1f}% excl {exc:5.1f}%{ms}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|