mirror of https://github.com/openclaw/openclaw.git
test: summarize diagnostic report memory growth
This commit is contained in:
parent
f9785c63e7
commit
c6f95a0c37
|
|
@ -1,4 +1,6 @@
|
|||
import { spawnSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const ESCAPE = String.fromCodePoint(27);
|
||||
const BELL = String.fromCodePoint(7);
|
||||
|
|
@ -196,3 +198,86 @@ export function sampleProcessTreeRssKb(rootPid) {
|
|||
|
||||
return { rssKb, processCount };
|
||||
}
|
||||
|
||||
const REPORT_FILE_PATTERN =
|
||||
/^report\.(?<date>\d+)\.(?<time>\d+)\.(?<pid>\d+)\.0\.(?<sequence>\d+)\.json$/u;
|
||||
|
||||
function readDiagnosticReport(reportPath) {
|
||||
try {
|
||||
const raw = fs.readFileSync(reportPath, "utf8");
|
||||
const parsed = JSON.parse(raw);
|
||||
const rssBytes = parsed?.resourceUsage?.rss;
|
||||
const usedHeapBytes = parsed?.javascriptHeap?.usedMemory;
|
||||
const externalBytes = parsed?.javascriptHeap?.externalMemory;
|
||||
if (
|
||||
!Number.isFinite(rssBytes) ||
|
||||
!Number.isFinite(usedHeapBytes) ||
|
||||
!Number.isFinite(externalBytes)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
rssKb: Math.round(rssBytes / 1024),
|
||||
usedHeapKb: Math.round(usedHeapBytes / 1024),
|
||||
externalKb: Math.round(externalBytes / 1024),
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function summarizeDiagnosticReports(reportDir) {
|
||||
if (typeof reportDir !== "string" || reportDir.trim() === "") {
|
||||
return [];
|
||||
}
|
||||
let entries;
|
||||
try {
|
||||
entries = fs.readdirSync(reportDir, { withFileTypes: true });
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
|
||||
const reportsByPid = new Map();
|
||||
for (const entry of entries) {
|
||||
if (!entry.isFile()) {
|
||||
continue;
|
||||
}
|
||||
const match = entry.name.match(REPORT_FILE_PATTERN);
|
||||
if (!match?.groups) {
|
||||
continue;
|
||||
}
|
||||
const pid = Number.parseInt(match.groups.pid, 10);
|
||||
const sequence = Number.parseInt(match.groups.sequence, 10);
|
||||
if (!Number.isInteger(pid) || !Number.isInteger(sequence)) {
|
||||
continue;
|
||||
}
|
||||
const reportPath = path.join(reportDir, entry.name);
|
||||
const report = readDiagnosticReport(reportPath);
|
||||
if (!report) {
|
||||
continue;
|
||||
}
|
||||
const bucket = reportsByPid.get(pid) ?? [];
|
||||
bucket.push({ pid, sequence, ...report });
|
||||
reportsByPid.set(pid, bucket);
|
||||
}
|
||||
|
||||
return [...reportsByPid.entries()]
|
||||
.map(([pid, reports]) => {
|
||||
const ordered = reports.toSorted((left, right) => left.sequence - right.sequence);
|
||||
const first = ordered[0];
|
||||
const last = ordered.at(-1);
|
||||
if (!first || !last) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
pid,
|
||||
first,
|
||||
last,
|
||||
rssDeltaKb: last.rssKb - first.rssKb,
|
||||
usedHeapDeltaKb: last.usedHeapKb - first.usedHeapKb,
|
||||
externalDeltaKb: last.externalKb - first.externalKb,
|
||||
};
|
||||
})
|
||||
.filter((entry) => entry !== null)
|
||||
.toSorted((left, right) => right.rssDeltaKb - left.rssDeltaKb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
getProcessTreeRecords,
|
||||
parseCompletedTestFileLines,
|
||||
sampleProcessTreeRssKb,
|
||||
summarizeDiagnosticReports,
|
||||
} from "../test-parallel-memory.mjs";
|
||||
import {
|
||||
appendCapturedOutput,
|
||||
|
|
@ -650,12 +651,24 @@ export async function executePlan(plan, options = {}) {
|
|||
.toSorted((left, right) => right.deltaKb - left.deltaKb)
|
||||
.slice(0, memoryTraceTopCount)
|
||||
.map((record) => `${record.file}:${formatMemoryDeltaKb(record.deltaKb)}`);
|
||||
const reportGrowth =
|
||||
reportOnSignalEnabled && heapSnapshotDir
|
||||
? summarizeDiagnosticReports(heapSnapshotDir)
|
||||
.filter((record) => record.rssDeltaKb > 0 || record.usedHeapDeltaKb > 0)
|
||||
.slice(0, memoryTraceTopCount)
|
||||
.map(
|
||||
(record) =>
|
||||
`pid=${String(record.pid)} rss=${formatMemoryDeltaKb(record.rssDeltaKb)} heap=${formatMemoryDeltaKb(record.usedHeapDeltaKb)} external=${formatMemoryDeltaKb(record.externalDeltaKb)}`,
|
||||
)
|
||||
: [];
|
||||
console.log(
|
||||
`[test-parallel][mem] summary ${unit.id} files=${memoryFileRecords.length} peak=${formatMemoryKb(
|
||||
peakTreeSample?.rssKb ?? 0,
|
||||
)} totalDelta=${formatMemoryDeltaKb(totalDeltaKb)} peakAt=${
|
||||
peakTreeSample?.reason ?? "n/a"
|
||||
} top=${topGrowthFiles.length > 0 ? topGrowthFiles.join(", ") : "none"}`,
|
||||
} top=${topGrowthFiles.length > 0 ? topGrowthFiles.join(", ") : "none"} reports=${
|
||||
reportGrowth.length > 0 ? reportGrowth.join(", ") : "none"
|
||||
}`,
|
||||
);
|
||||
};
|
||||
const clearChildTimers = () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue