mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
82 lines
3.9 KiB
Python
82 lines
3.9 KiB
Python
# MobileGL - tools/device_bench/disagg/offwait.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
|
|
|
|
# offwait.py <trace.txt> <tid> [present_marker]
|
|
# Off-CPU accounting for one thread from an atrace/ftrace text dump:
|
|
# - sleep (S/D) and runnable (preempted) time per frame
|
|
# - sleep time keyed by the atrace slice stack open at the switch-out and the waker's comm
|
|
# - GPU busy from adreno_cmdbatch_retired (start/retire are GPU ticks: 19.2 MHz)
|
|
import re, sys
|
|
from collections import Counter, defaultdict
|
|
path, tid = sys.argv[1], sys.argv[2]
|
|
present = sys.argv[3] if len(sys.argv) > 3 else "QueuePresentKHR|eglSwapBuffers"
|
|
pres_re = re.compile(present)
|
|
line_re = re.compile(r'^\s*(.+?)-(\d+)\s+\(\s*([\d-]+)\)\s+\[(\d+)\]\s+\S+\s+([\d.]+):\s+(\w+):\s*(.*)$')
|
|
stack = []
|
|
state = None; t_state = None # 'run' | 'sleep' | 'runq'
|
|
sleep_by = Counter(); runq_total = 0.0; sleep_total = 0.0; run_total = 0.0
|
|
pending_sleep = None # (t0, stackkey, prev_state)
|
|
last_waker = None
|
|
presents = []
|
|
gpu = [] # (start_tick, retire_tick, ctx)
|
|
t_first = t_last = None
|
|
slice_time = Counter(); slice_open = []
|
|
for line in open(path, errors="replace"):
|
|
m = line_re.match(line)
|
|
if not m: continue
|
|
comm, pid, tgid, cpu, ts, ev, rest = m.groups(); ts = float(ts)
|
|
if t_first is None: t_first = ts
|
|
t_last = ts
|
|
if ev == "tracing_mark_write" and pid == tid:
|
|
if rest.startswith("B|"):
|
|
name = rest.split("|", 2)[2]
|
|
stack.append((name, ts))
|
|
if pres_re.search(name): presents.append(ts)
|
|
elif rest.startswith("E|"):
|
|
if stack:
|
|
name, t0 = stack.pop(); slice_time[name[:60]] += ts - t0
|
|
elif ev == "sched_waking":
|
|
mm = re.search(r'pid=(\d+)', rest)
|
|
if mm and mm.group(1) == tid: last_waker = f"{comm}"
|
|
elif ev == "sched_switch":
|
|
mp = re.search(r'prev_pid=(\d+) prev_prio=\d+ prev_state=(\S+) ==> next_comm=.* next_pid=(\d+)', rest)
|
|
if not mp: continue
|
|
ppid, pst, npid = mp.groups()
|
|
if ppid == tid:
|
|
key = " > ".join(n[:40] for n, _ in stack[-3:]) or "(no slice)"
|
|
pending_sleep = (ts, key, pst)
|
|
last_waker = None
|
|
if npid == tid and pending_sleep:
|
|
t0, key, pst = pending_sleep
|
|
d = ts - t0
|
|
if pst.startswith("R"): runq_total += d; sleep_by[("RUNNABLE(preempted)", key, "-")] += d
|
|
else: sleep_total += d; sleep_by[("sleep " + pst, key, last_waker or "?")] += d
|
|
pending_sleep = None
|
|
elif ev == "adreno_cmdbatch_retired":
|
|
ms = re.search(r'ctx=(\d+).*start=(\d+) retire=(\d+)', rest)
|
|
if ms: gpu.append((int(ms.group(2)), int(ms.group(3)), int(ms.group(1))))
|
|
win = t_last - t_first
|
|
# only steady presents
|
|
nfr = max(1, len(presents) - 1); span = presents[-1] - presents[0] if len(presents) > 1 else win
|
|
print(f"window {win:.3f}s, presents {len(presents)} -> {len(presents)/win:.1f} fps, frame {1e3*span/nfr:.3f} ms")
|
|
print(f"per frame: sleep {1e3*sleep_total/nfr:.3f} ms, runnable(preempted) {1e3*runq_total/nfr:.3f} ms")
|
|
print("top off-CPU (ms/frame): state | open slices at switch-out | waker")
|
|
for (st, key, wk), d in sleep_by.most_common(18):
|
|
print(f" {1e3*d/nfr:6.3f} {st:<22} | {key[:110]:<110} | {wk}")
|
|
print("top slices on thread (ms/frame, wall incl. children):")
|
|
for k, v in slice_time.most_common(14): print(f" {1e3*v/nfr:6.3f} {k}")
|
|
if gpu:
|
|
gpu.sort(); busy = 0; cs, ce = gpu[0][0], gpu[0][1]
|
|
for s, e, c in gpu[1:]:
|
|
if s > ce: busy += ce - cs; cs, ce = s, e
|
|
else: ce = max(ce, e)
|
|
busy += ce - cs
|
|
spanticks = gpu[-1][1] - gpu[0][0]
|
|
ctxs = Counter(c for _, _, c in gpu)
|
|
print(f"GPU: {len(gpu)} cmdbatches, busy {100*busy/spanticks:.1f}% of {spanticks/19.2e6:.2f}s, {1e3*busy/19.2e6/nfr:.3f} ms/frame; ctxs {ctxs.most_common(5)}")
|