IOS LLE: checkpoint NAND conformance and diagnostic tooling
Preserve formatted NAND boot and Wii Remote pairing, add DMA/SRAM conformance tests and a Wii comparison probe. Keep P1 profiling tools and native update A/B evidence; reject the unhelpful targeted SRAM shortcut.
This commit is contained in:
1 parent
b931671fde
commit
c230971f07
64 files changed
+6488
-238
No files matched your search
@@ -0,0 +1,100 @@
|
||||
"""Summarize CPU leaf samples per thread from an already-aligned P1 capture.
|
||||
|
||||
These are sample counts, not exact CPU times or a critical-path attribution.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from collections import Counter, defaultdict
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from analyze_p1 import percentile
|
||||
|
||||
|
||||
def summarize(path, pid):
|
||||
by_thread = defaultdict(Counter)
|
||||
processors = defaultdict(Counter)
|
||||
sample_begin = None
|
||||
sample_end = None
|
||||
with path.open(encoding="utf-8") as source:
|
||||
for line in source:
|
||||
event = json.loads(line)
|
||||
if event["native"] or event["pid"] != pid or event["event"] != "SampledProfile":
|
||||
continue
|
||||
data = event["data"]
|
||||
tid = int(data["ThreadId"])
|
||||
weight = int(data.get("Count", 1))
|
||||
by_thread[tid][data.get("Symbol", "") or "<unresolved>"] += weight
|
||||
processors[tid][int(data["Processor"])] += weight
|
||||
sample_begin = event["qpc"] if sample_begin is None else min(sample_begin, event["qpc"])
|
||||
sample_end = event["qpc"] if sample_end is None else max(sample_end, event["qpc"])
|
||||
if sample_begin is None:
|
||||
raise ValueError("No target CPU samples in the selected window")
|
||||
frequency = None
|
||||
previous_frame = {}
|
||||
frame_deltas = defaultdict(list)
|
||||
snapshots = {}
|
||||
with path.open(encoding="utf-8") as source:
|
||||
for line in source:
|
||||
event = json.loads(line)
|
||||
if not event["native"] or event["pid"] != pid:
|
||||
continue
|
||||
qpc = event["qpc"]
|
||||
if not sample_begin <= qpc <= sample_end:
|
||||
continue
|
||||
data = event["data"]
|
||||
if event["event"] == "Marker" and data["Name"] == "PerformanceMetricsReset":
|
||||
previous_frame.clear()
|
||||
elif event["event"] == "Frame":
|
||||
if frequency is None:
|
||||
frequency = int(data["QPCFrequency"])
|
||||
elif frequency != int(data["QPCFrequency"]):
|
||||
raise ValueError("Inconsistent frame QPC frequency")
|
||||
kind = data["Kind"]
|
||||
if kind in previous_frame:
|
||||
frame_deltas[kind].append(1000 * (qpc - previous_frame[kind]) / frequency)
|
||||
previous_frame[kind] = qpc
|
||||
elif event["event"] == "Snapshot":
|
||||
core = int(data["CoreId"])
|
||||
if core not in snapshots:
|
||||
snapshots[core] = [data, data]
|
||||
else:
|
||||
snapshots[core][1] = data
|
||||
frames = {kind: {"intervals": len(deltas), "p50_ms": percentile(deltas, .5),
|
||||
"p95_ms": percentile(deltas, .95), "p99_ms": percentile(deltas, .99)}
|
||||
for kind, deltas in frame_deltas.items()}
|
||||
return {
|
||||
"pid": pid,
|
||||
"sample_count": sum(sum(symbols.values()) for symbols in by_thread.values()),
|
||||
"sample_window_qpc": {"begin": sample_begin, "end": sample_end,
|
||||
"duration_seconds": (sample_end - sample_begin) / frequency
|
||||
if frequency else None},
|
||||
"frame_intervals_in_sample_window": frames,
|
||||
"snapshot_deltas_in_sample_window": {
|
||||
str(core): {field: last[field] - first[field]
|
||||
for field in ("Instructions", "NativeInstructions", "SlowReads",
|
||||
"SlowWrites", "Compiles", "Fallbacks")}
|
||||
for core, (first, last) in snapshots.items()
|
||||
},
|
||||
"note": "Leaf sample counts and host frame intervals within the same selected window; no exact CPU duration or critical-path claim.",
|
||||
"threads": [
|
||||
{"tid": tid, "samples": sum(symbols.values()),
|
||||
"processors": dict(sorted(processors[tid].items())),
|
||||
"top_symbols": [{"symbol": symbol, "samples": count}
|
||||
for symbol, count in symbols.most_common(12)]}
|
||||
for tid, symbols in sorted(by_thread.items(),
|
||||
key=lambda item: -sum(item[1].values()))
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("events", type=Path)
|
||||
parser.add_argument("--pid", required=True, type=int)
|
||||
parser.add_argument("--output", required=True, type=Path)
|
||||
args = parser.parse_args()
|
||||
result = summarize(args.events, args.pid)
|
||||
with args.output.open("x", encoding="utf-8") as target:
|
||||
json.dump(result, target, indent=2)
|
||||
print(f"{result['sample_count']} CPU samples from {len(result['threads'])} threads")
|
||||
Reference in new issue
Block a user