mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-12 14:19:00 +01:00
Record local journal bottleneck and retain diagnostic evidence
This commit is contained in:
1 parent
e9b909015a
commit
75dbd64974
23 files changed
+1625
-4
No files matched your search
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Summarize diagnostic app samples, excluding them from performance acceptance."""
|
||||
import gzip
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
raise SystemExit("Pass one .ndjson or .ndjson.gz diagnostic app metrics file")
|
||||
path = pathlib.Path(sys.argv[1])
|
||||
raw = gzip.decompress(path.read_bytes()) if path.suffix == ".gz" else path.read_bytes()
|
||||
rows = [json.loads(line) for line in raw.splitlines() if line.strip()]
|
||||
samples = [row for row in rows if row.get("kind") == "app-sample"]
|
||||
if not samples:
|
||||
raise SystemExit("No app samples found")
|
||||
print(json.dumps({
|
||||
"scope": "Instrumented failed historical local-process baseline replay; no acceptance comparison",
|
||||
"sampleCount": len(samples),
|
||||
"firstAtMs": samples[0]["atMs"],
|
||||
"lastAtMs": samples[-1]["atMs"],
|
||||
"sampleSpanMs": samples[-1]["atMs"] - samples[0]["atMs"],
|
||||
"maxObservedNormalizedQueueDepth": max(row["actualNormalizedQueueDepth"] for row in samples),
|
||||
"maxObservedRssBytes": max(row["rssBytes"] for row in samples),
|
||||
"lastSample": samples[-1],
|
||||
"limits": [
|
||||
"Queue counts the normalized upstream queue, excluding collector/pending-read/in-flight/transport queues",
|
||||
"Observed peaks are sample based",
|
||||
"Cumulative filesystem elapsed times overlap across async operations and include scheduling; do not sum or interpret async sync elapsed as blocked CPU",
|
||||
"Serialization elapsed ends before byte-counting probe; sampled wrapper residence includes byte counting",
|
||||
"Diagnostic instrumentation and its observer work are excluded from acceptance",
|
||||
],
|
||||
}, indent=2))
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Describe sealed V8 profiles; sampled stack time is not process CPU usage."""
|
||||
import collections
|
||||
import gzip
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
raise SystemExit("Pass one or more .cpuprofile or .cpuprofile.gz paths")
|
||||
|
||||
reports = []
|
||||
for argument in sys.argv[1:]:
|
||||
path = pathlib.Path(argument)
|
||||
raw = path.read_bytes()
|
||||
if path.suffix == ".gz":
|
||||
raw = gzip.decompress(raw)
|
||||
profile = json.loads(raw)
|
||||
nodes = {node["id"]: node for node in profile["nodes"]}
|
||||
parents = {child: node["id"] for node in nodes.values() for child in node.get("children", [])}
|
||||
assert len(profile["samples"]) == len(profile["timeDeltas"])
|
||||
own, inclusive = collections.Counter(), collections.Counter()
|
||||
|
||||
def frame(node_id):
|
||||
value = nodes[node_id]["callFrame"]
|
||||
return (value["functionName"] or "<anonymous>", value["url"], value["lineNumber"] + 1)
|
||||
|
||||
for node_id, duration in zip(profile["samples"], profile["timeDeltas"]):
|
||||
assert duration >= 0
|
||||
own[frame(node_id)] += duration
|
||||
seen = set()
|
||||
while node_id in nodes:
|
||||
key = frame(node_id)
|
||||
if key not in seen:
|
||||
inclusive[key] += duration
|
||||
seen.add(key)
|
||||
if node_id not in parents:
|
||||
break
|
||||
node_id = parents[node_id]
|
||||
total = sum(profile["timeDeltas"])
|
||||
assert total > 0
|
||||
|
||||
def ranked(counter, limit):
|
||||
return [{"function": key[0], "url": key[1], "line": key[2],
|
||||
"weightedMillis": duration / 1000, "sampledSharePercent": duration * 100 / total}
|
||||
for key, duration in counter.most_common(limit)]
|
||||
|
||||
application = collections.Counter({key: value for key, value in inclusive.items()
|
||||
if key[1].startswith("file:") and key[0] != "<anonymous>"})
|
||||
reports.append({"file": path.name.removesuffix(".gz"), "decompressedSha256": hashlib.sha256(raw).hexdigest(),
|
||||
"samples": len(profile["samples"]), "weightedMillis": total / 1000,
|
||||
"self": ranked(own, 20), "inclusiveFileFrames": ranked(application, 30)})
|
||||
|
||||
print(json.dumps({"scope": "Diagnostic only; no performance acceptance claim",
|
||||
"method": "Weight each sampled frame by its corresponding V8 timeDelta; inclusive frames deduplicate recursion",
|
||||
"limits": ["Includes idle and native blocking time; this is not process CPU usage",
|
||||
"Inclusive frames overlap and must not be summed",
|
||||
"Instrumented wrapper residence can include byte counting or other probe work",
|
||||
"Instrumentation and failed-run phases must remain identified"],
|
||||
"profiles": reports}, indent=2))
|
||||
Reference in new issue
Block a user