fix provider process memory accounting

This commit is contained in:
Codex
2026-05-17 16:43:11 +00:00
parent bdaae79834
commit 3ed8c102c1
10 changed files with 82 additions and 16 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@unidesk/provider-gateway",
"version": "0.2.22",
"version": "0.2.23",
"private": true,
"type": "module",
"scripts": {
+48 -2
View File
@@ -914,6 +914,41 @@ function readProcessIo(pid: number): { readBytes: number; writeBytes: number } {
}
}
function readProcessMemory(pid: number, rssBytes: number): {
memoryBytes: number;
memoryMode: string;
pssBytes?: number;
privateBytes?: number;
sharedBytes?: number;
swapPssBytes?: number;
} {
try {
const values = new Map<string, number>();
for (const line of readFileSync(`/proc/${pid}/smaps_rollup`, "utf8").split("\n")) {
const match = line.match(/^([A-Za-z_]+):\s+(\d+)\s+kB/);
if (!match) continue;
values.set(match[1], Number(match[2]) * 1024);
}
const pssBytes = values.get("Pss");
if (typeof pssBytes !== "number" || !Number.isFinite(pssBytes) || pssBytes < 0) {
throw new Error("missing Pss");
}
const privateBytes = (values.get("Private_Clean") ?? 0) + (values.get("Private_Dirty") ?? 0);
const sharedBytes = (values.get("Shared_Clean") ?? 0) + (values.get("Shared_Dirty") ?? 0);
const swapPssBytes = values.get("SwapPss") ?? 0;
return {
memoryBytes: pssBytes,
memoryMode: "pss_smaps_rollup",
pssBytes,
privateBytes,
sharedBytes,
swapPssBytes,
};
} catch {
return { memoryBytes: rssBytes, memoryMode: "rss_fallback" };
}
}
function readUptimeSeconds(): number {
const uptime = Number(readFileSync("/proc/uptime", "utf8").trim().split(/\s+/)[0]);
return Number.isFinite(uptime) ? uptime : 0;
@@ -989,6 +1024,7 @@ async function collectProcessResources(totalMemoryBytes: number, cpuCores: numbe
const readBytesPerSecond = previous && ioSeconds > 0 ? Math.max(0, io.readBytes - previous.readBytes) / ioSeconds : 0;
const writeBytesPerSecond = previous && ioSeconds > 0 ? Math.max(0, io.writeBytes - previous.writeBytes) / ioSeconds : 0;
const rssBytes = Math.max(0, stat.rssPages * pageSize);
const processMemory = readProcessMemory(pid, rssBytes);
const name = status.name || stat.name;
const uid = status.uid >= 0 ? status.uid : 0;
rows.push({
@@ -1000,8 +1036,14 @@ async function collectProcessResources(totalMemoryBytes: number, cpuCores: numbe
command: readProcessCommand(pid, name),
state: stat.state,
cpuPercent: roundMetric(Math.min(cpuPercent, Math.max(1, cpuCores) * 100)),
memoryPercent: totalMemoryBytes > 0 ? clampPercent((rssBytes / totalMemoryBytes) * 100) : 0,
memoryPercent: totalMemoryBytes > 0 ? clampPercent((processMemory.memoryBytes / totalMemoryBytes) * 100) : 0,
memoryBytes: processMemory.memoryBytes,
memoryMode: processMemory.memoryMode,
rssBytes,
pssBytes: processMemory.pssBytes,
privateBytes: processMemory.privateBytes,
sharedBytes: processMemory.sharedBytes,
swapPssBytes: processMemory.swapPssBytes,
vmsBytes: Math.max(0, stat.vmsBytes),
threads: status.threads || stat.threads,
readBytes: io.readBytes,
@@ -1024,7 +1066,8 @@ async function collectProcessResources(totalMemoryBytes: number, cpuCores: numbe
}
previousProcessSamples = new Map([...previousProcessSamples].filter(([pid]) => seen.has(pid)));
rows.sort((a, b) => b.rssBytes - a.rssBytes || b.cpuPercent - a.cpuPercent || a.pid - b.pid);
rows.sort((a, b) => b.memoryBytes - a.memoryBytes || b.cpuPercent - a.cpuPercent || a.pid - b.pid);
const pssRows = rows.filter((row) => row.memoryMode === "pss_smaps_rollup").length;
return {
processes: rows.slice(0, 120),
summary: {
@@ -1033,6 +1076,9 @@ async function collectProcessResources(totalMemoryBytes: number, cpuCores: numbe
skipped,
defaultSort: "memory_desc",
scope: "provider_pid_namespace",
memoryMode: pssRows > 0 ? "pss_smaps_rollup" : "rss_fallback",
pssRows,
rssFallbackRows: rows.length - pssRows,
cpuPercentMode: hasPreviousProcessSample ? "delta_ticks_per_sample" : "lifetime_average_first_sample",
diskIoMode: "proc_pid_io_delta_bytes_per_second",
},