Files
workinf_Blender_Wasm/my-tools/wbv2_process_metrics.py
mes123456 f3e21c6b98
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
chore: checkpoint web Blender migration source, tools and docs
Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer,
web workflow engine and protocol, tooling, docs and task records.
Large generated evidence, user-testing archives, build snapshots and
imported test assets remain untracked.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-10 11:18:01 +08:00

67 lines
2.8 KiB
Python

"""Best-effort Linux process-tree observations without reading command payloads."""
from collections import Counter
from pathlib import Path
import os
import resource
import time
class ProcessMetrics:
def __init__(self, pid):
self.pid = pid
self.started = time.monotonic()
self.initial = resource.getrusage(resource.RUSAGE_CHILDREN)
self.peak_rss = self.peak_threads = self.peak_processes = self.peak_fds = 0
self.samples = 0
self.wait_channels = Counter()
self.names = set()
self.sample()
def sample(self):
pending, seen = [self.pid], set()
rss = threads = fds = processes = 0
while pending:
pid = pending.pop()
if pid in seen:
continue
seen.add(pid)
directory = Path("/proc") / str(pid)
try:
fields = (directory / "stat").read_text().rsplit(")", 1)[1].split()
children = (directory / "task" / str(pid) / "children").read_text().split()
pending.extend(map(int, children))
rss += int(fields[21]) * os.sysconf("SC_PAGE_SIZE")
threads += int(fields[17])
fds += sum(1 for _ in (directory / "fd").iterdir())
processes += 1
self.names.add((directory / "comm").read_text().strip())
wait = (directory / "wchan").read_text().strip()
if wait and wait != "0":
self.wait_channels[wait] += 1
except (OSError, ValueError, IndexError):
continue
self.samples += 1
self.peak_rss = max(self.peak_rss, rss)
self.peak_threads = max(self.peak_threads, threads)
self.peak_processes = max(self.peak_processes, processes)
self.peak_fds = max(self.peak_fds, fds)
def finish(self):
usage = resource.getrusage(resource.RUSAGE_CHILDREN)
cpu = max(0, usage.ru_utime + usage.ru_stime - self.initial.ru_utime - self.initial.ru_stime)
wall = time.monotonic() - self.started
return {
"ownerProcessGroup": self.pid,
"wallSeconds": round(wall, 6),
"reapedChildCpuSeconds": round(cpu, 6),
"cpuPercent": round(100 * cpu / wall, 2) if wall else 0,
"peakSampledRssBytes": self.peak_rss,
"peakSampledThreads": self.peak_threads,
"peakSampledProcesses": self.peak_processes,
"peakSampledFileDescriptors": self.peak_fds,
"samples": self.samples,
"processNames": sorted(self.names),
"waitChannels": dict(self.wait_channels.most_common(8)),
"limits": "Sampled peaks may miss short-lived children; child CPU is wait4 accounting, not GIL or event-loop timing.",
}