@camstack/system 1.2.222 → 1.2.224
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon-runner.js +2 -2
- package/dist/addon-runner.mjs +2 -2
- package/dist/addon-utils.js +1 -1
- package/dist/addon-utils.mjs +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
- package/dist/builtins/device-manager/device-manager.addon.js +2 -2
- package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +30 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{custom-action-registry-DvVMHLfJ.js → custom-action-registry-BTB-LgGN.js} +1 -1
- package/dist/{custom-action-registry-DvtOs-tp.mjs → custom-action-registry-CdLp1P9C.mjs} +1 -1
- package/dist/{dist-0sWJNwme.js → dist-DdRxWA7g.js} +151 -24
- package/dist/{dist-T9GvfhgV.mjs → dist-DmfYrdAn.mjs} +151 -24
- package/dist/index.js +3 -3
- package/dist/index.mjs +5 -5
- package/dist/kernel/heap-watch.d.ts +14 -0
- package/dist/kernel/process-cpu-census.d.ts +74 -0
- package/dist/{manifest-system-deps-YYKKCKSF.js → manifest-system-deps-8x1jgPz_.js} +138 -1
- package/dist/{manifest-system-deps-D4cmEnqQ.mjs → manifest-system-deps-BhYmLVHw.mjs} +139 -2
- package/dist/{retired-settings-keys-DmVZAH9M.mjs → retired-settings-keys-B0JHsynV.mjs} +1 -1
- package/dist/{retired-settings-keys-Dh-XMfFx.js → retired-settings-keys-C-jOWHI0.js} +1 -1
- package/package.json +1 -1
|
@@ -1470,6 +1470,134 @@ function formatGcCensus(window) {
|
|
|
1470
1470
|
const perMin = (n) => window.windowMs <= 0 ? "?" : (n / (window.windowMs / 6e4)).toFixed(1);
|
|
1471
1471
|
return ` gc=maj${window.majors}/min${window.minors}/inc${window.incrementals}@${Math.round(window.windowMs / 1e3)}s gcMajPerMin=${perMin(window.majors)} gcPauseMs=${Math.round(window.pauseMs)}`;
|
|
1472
1472
|
}
|
|
1473
|
+
/**
|
|
1474
|
+
* The census. Always exists — `process.cpuUsage()` is available on every
|
|
1475
|
+
* platform Node runs on, so unlike the GC census there is no runtime in which
|
|
1476
|
+
* this has nothing to report.
|
|
1477
|
+
*/
|
|
1478
|
+
function createProcessCpuCensus(options) {
|
|
1479
|
+
let windowStart = options.startedAt;
|
|
1480
|
+
let cpuAtStart = options.cpuUsageUs();
|
|
1481
|
+
let tasksAtStart = options.readTasks();
|
|
1482
|
+
return { read: (at) => {
|
|
1483
|
+
const windowMs = at - windowStart;
|
|
1484
|
+
if (windowMs < 1e3) return null;
|
|
1485
|
+
const cpuNow = options.cpuUsageUs();
|
|
1486
|
+
const tasksNow = options.readTasks();
|
|
1487
|
+
const cpuUs = cpuNow - cpuAtStart;
|
|
1488
|
+
const percentOfCore = cpuUs <= 0 ? 0 : round1(cpuUs / 1e3 / windowMs * 100);
|
|
1489
|
+
const reading = {
|
|
1490
|
+
percentOfCore,
|
|
1491
|
+
windowMs,
|
|
1492
|
+
v8Workers: shareOfWorkers(tasksAtStart, tasksNow, percentOfCore)
|
|
1493
|
+
};
|
|
1494
|
+
windowStart = at;
|
|
1495
|
+
cpuAtStart = cpuNow;
|
|
1496
|
+
tasksAtStart = tasksNow;
|
|
1497
|
+
return reading;
|
|
1498
|
+
} };
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1501
|
+
* The worker share, or `null`.
|
|
1502
|
+
*
|
|
1503
|
+
* Every way this can fail to be a reading returns `null`: either snapshot
|
|
1504
|
+
* missing, no thread scheduled at all across the window (a share of nothing is
|
|
1505
|
+
* not zero percent — it is no measurement), or a delta that went BACKWARDS,
|
|
1506
|
+
* which means the thread set changed under us and the subtraction describes
|
|
1507
|
+
* nothing real.
|
|
1508
|
+
*/
|
|
1509
|
+
function shareOfWorkers(before, after, percentOfCore) {
|
|
1510
|
+
if (before === null || after === null) return null;
|
|
1511
|
+
const b = sum(before);
|
|
1512
|
+
const a = sum(after);
|
|
1513
|
+
const workerTicks = a.worker - b.worker;
|
|
1514
|
+
const totalTicks = a.total - b.total;
|
|
1515
|
+
if (workerTicks < 0 || totalTicks <= 0 || workerTicks > totalTicks) return null;
|
|
1516
|
+
return {
|
|
1517
|
+
percentOfCore: round1(percentOfCore * (workerTicks / totalTicks)),
|
|
1518
|
+
threads: a.workerThreads
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
var V8_WORKER_COMM = "V8Worker";
|
|
1522
|
+
function sum(tasks) {
|
|
1523
|
+
let worker = 0;
|
|
1524
|
+
let total = 0;
|
|
1525
|
+
let workerThreads = 0;
|
|
1526
|
+
for (const t of tasks) {
|
|
1527
|
+
total += t.ticks;
|
|
1528
|
+
if (t.comm === V8_WORKER_COMM) {
|
|
1529
|
+
worker += t.ticks;
|
|
1530
|
+
workerThreads += 1;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
return {
|
|
1534
|
+
worker,
|
|
1535
|
+
total,
|
|
1536
|
+
workerThreads
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
function round1(n) {
|
|
1540
|
+
return Math.round(n * 10) / 10;
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Per-thread ticks for THIS process, or `null` where that cannot be read.
|
|
1544
|
+
*
|
|
1545
|
+
* `/proc/self/task/<tid>/stat` is `pid (comm) state ppid …`, and `comm` may
|
|
1546
|
+
* itself contain spaces and parentheses — so the split is on the LAST `)`,
|
|
1547
|
+
* never on whitespace. `utime` and `stime` are the 12th and 13th fields after
|
|
1548
|
+
* it. Everything that can fail is inside the try: a non-Linux platform, a
|
|
1549
|
+
* container with `hidepid`, a thread that exits between the readdir and the
|
|
1550
|
+
* read. A thread that vanishes mid-scan is skipped rather than failing the
|
|
1551
|
+
* whole reading — it contributed to neither snapshot's total.
|
|
1552
|
+
*/
|
|
1553
|
+
function readSelfTaskTicks() {
|
|
1554
|
+
try {
|
|
1555
|
+
const tids = (0, node_fs.readdirSync)("/proc/self/task");
|
|
1556
|
+
const out = [];
|
|
1557
|
+
for (const tid of tids) {
|
|
1558
|
+
const parsed = parseTaskStat(tid);
|
|
1559
|
+
if (parsed !== null) out.push(parsed);
|
|
1560
|
+
}
|
|
1561
|
+
return out.length === 0 ? null : out;
|
|
1562
|
+
} catch {
|
|
1563
|
+
return null;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
function parseTaskStat(tid) {
|
|
1567
|
+
try {
|
|
1568
|
+
const raw = (0, node_fs.readFileSync)(`/proc/self/task/${tid}/stat`, "utf8");
|
|
1569
|
+
const close = raw.lastIndexOf(") ");
|
|
1570
|
+
const open = raw.indexOf(" (");
|
|
1571
|
+
if (close < 0 || open < 0 || close <= open) return null;
|
|
1572
|
+
const comm = raw.slice(open + 2, close);
|
|
1573
|
+
const fields = raw.slice(close + 2).split(" ");
|
|
1574
|
+
const utime = Number(fields[11]);
|
|
1575
|
+
const stime = Number(fields[12]);
|
|
1576
|
+
if (!Number.isFinite(utime) || !Number.isFinite(stime)) return null;
|
|
1577
|
+
return {
|
|
1578
|
+
comm,
|
|
1579
|
+
ticks: utime + stime
|
|
1580
|
+
};
|
|
1581
|
+
} catch {
|
|
1582
|
+
return null;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
/**
|
|
1586
|
+
* The two fields, APPENDED LAST on the `[mem]` line — after everything D446,
|
|
1587
|
+
* D456 and D458 appended, so every Loki prefix written since 2026-08-29 still
|
|
1588
|
+
* matches (D446's rule).
|
|
1589
|
+
*
|
|
1590
|
+
* `procCpu` prints whenever there is a window, including at 0.0 %, because an
|
|
1591
|
+
* idle process is a reading and it is the one that says a runner is alive and
|
|
1592
|
+
* doing nothing. `v8WorkerCpu` is CONDITIONAL and its absence is the reading:
|
|
1593
|
+
* the platform could not be asked. It carries its thread count because "36.6 %
|
|
1594
|
+
* over four threads" and "36.6 % on one" are different faults.
|
|
1595
|
+
*/
|
|
1596
|
+
function formatProcessCpu(reading) {
|
|
1597
|
+
if (reading === null || reading === void 0) return "";
|
|
1598
|
+
const workers = reading.v8Workers;
|
|
1599
|
+
return ` procCpu=${reading.percentOfCore.toFixed(1)}%` + (workers === null ? "" : ` v8WorkerCpu=${workers.percentOfCore.toFixed(1)}%/${workers.threads}thr`);
|
|
1600
|
+
}
|
|
1473
1601
|
//#endregion
|
|
1474
1602
|
//#region src/kernel/heap-watch.ts
|
|
1475
1603
|
/**
|
|
@@ -2069,6 +2197,15 @@ function startHeapWatch(label = HUB_MAIN_HEAP_WATCH_LABEL, sink = consoleSink, i
|
|
|
2069
2197
|
const now = reclaimOptions?.now ?? (() => Date.now());
|
|
2070
2198
|
const gcObserver = probes?.gcObserver ?? nodeGcObserver();
|
|
2071
2199
|
const gcCensus = createGcCensus(gcObserver.install, gcObserver.kinds, now());
|
|
2200
|
+
const cpuProbe = probes?.processCpu;
|
|
2201
|
+
const processCpu = createProcessCpuCensus({
|
|
2202
|
+
cpuUsageUs: cpuProbe?.cpuUsageUs ?? (() => {
|
|
2203
|
+
const u = process.cpuUsage();
|
|
2204
|
+
return u.user + u.system;
|
|
2205
|
+
}),
|
|
2206
|
+
readTasks: cpuProbe?.readTasks ?? readSelfTaskTicks,
|
|
2207
|
+
startedAt: now()
|
|
2208
|
+
});
|
|
2072
2209
|
const triggerMb = reclaimOptions?.triggerMb ?? 1024;
|
|
2073
2210
|
const minIntervalMs = reclaimOptions?.minIntervalMs ?? 12e4;
|
|
2074
2211
|
const steadyStateIntervalMs = reclaimOptions?.steadyStateIntervalMs ?? 6e5;
|
|
@@ -2171,7 +2308,7 @@ function startHeapWatch(label = HUB_MAIN_HEAP_WATCH_LABEL, sink = consoleSink, i
|
|
|
2171
2308
|
const due = at - lastLoggedAt >= intervalMs;
|
|
2172
2309
|
if (mode === "escalated" || due) {
|
|
2173
2310
|
lastLoggedAt = at;
|
|
2174
|
-
const line = format(label, sample, loopDelay?.read(), budgetMb, `${formatHeapSpaces(readSpaces())}${formatEventPlane(eventPlane?.read())}` + formatSocketPlane(socketPlane?.read()) + formatMeshQueue(meshQueue?.()) + formatGcCensus(gcCensus?.read(at)));
|
|
2311
|
+
const line = format(label, sample, loopDelay?.read(), budgetMb, `${formatHeapSpaces(readSpaces())}${formatEventPlane(eventPlane?.read())}` + formatSocketPlane(socketPlane?.read()) + formatMeshQueue(meshQueue?.()) + formatGcCensus(gcCensus?.read(at)) + formatProcessCpu(processCpu.read(at)));
|
|
2175
2312
|
if (sample.nearLimit) sink.warn(`${line} — APPROACHING HEAP LIMIT`);
|
|
2176
2313
|
else if (mode === "escalated") sink.warn(`${line} — heap elevated, sampling every ${probeIntervalMs}ms`);
|
|
2177
2314
|
else sink.info(line);
|
|
@@ -6,7 +6,7 @@ import { unlink } from "node:fs/promises";
|
|
|
6
6
|
import * as path$1 from "node:path";
|
|
7
7
|
import { isAbsolute, join } from "node:path";
|
|
8
8
|
import * as fs from "node:fs";
|
|
9
|
-
import { existsSync } from "node:fs";
|
|
9
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
10
10
|
import { execFile } from "node:child_process";
|
|
11
11
|
import * as os from "node:os";
|
|
12
12
|
import { tmpdir } from "node:os";
|
|
@@ -1467,6 +1467,134 @@ function formatGcCensus(window) {
|
|
|
1467
1467
|
const perMin = (n) => window.windowMs <= 0 ? "?" : (n / (window.windowMs / 6e4)).toFixed(1);
|
|
1468
1468
|
return ` gc=maj${window.majors}/min${window.minors}/inc${window.incrementals}@${Math.round(window.windowMs / 1e3)}s gcMajPerMin=${perMin(window.majors)} gcPauseMs=${Math.round(window.pauseMs)}`;
|
|
1469
1469
|
}
|
|
1470
|
+
/**
|
|
1471
|
+
* The census. Always exists — `process.cpuUsage()` is available on every
|
|
1472
|
+
* platform Node runs on, so unlike the GC census there is no runtime in which
|
|
1473
|
+
* this has nothing to report.
|
|
1474
|
+
*/
|
|
1475
|
+
function createProcessCpuCensus(options) {
|
|
1476
|
+
let windowStart = options.startedAt;
|
|
1477
|
+
let cpuAtStart = options.cpuUsageUs();
|
|
1478
|
+
let tasksAtStart = options.readTasks();
|
|
1479
|
+
return { read: (at) => {
|
|
1480
|
+
const windowMs = at - windowStart;
|
|
1481
|
+
if (windowMs < 1e3) return null;
|
|
1482
|
+
const cpuNow = options.cpuUsageUs();
|
|
1483
|
+
const tasksNow = options.readTasks();
|
|
1484
|
+
const cpuUs = cpuNow - cpuAtStart;
|
|
1485
|
+
const percentOfCore = cpuUs <= 0 ? 0 : round1(cpuUs / 1e3 / windowMs * 100);
|
|
1486
|
+
const reading = {
|
|
1487
|
+
percentOfCore,
|
|
1488
|
+
windowMs,
|
|
1489
|
+
v8Workers: shareOfWorkers(tasksAtStart, tasksNow, percentOfCore)
|
|
1490
|
+
};
|
|
1491
|
+
windowStart = at;
|
|
1492
|
+
cpuAtStart = cpuNow;
|
|
1493
|
+
tasksAtStart = tasksNow;
|
|
1494
|
+
return reading;
|
|
1495
|
+
} };
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* The worker share, or `null`.
|
|
1499
|
+
*
|
|
1500
|
+
* Every way this can fail to be a reading returns `null`: either snapshot
|
|
1501
|
+
* missing, no thread scheduled at all across the window (a share of nothing is
|
|
1502
|
+
* not zero percent — it is no measurement), or a delta that went BACKWARDS,
|
|
1503
|
+
* which means the thread set changed under us and the subtraction describes
|
|
1504
|
+
* nothing real.
|
|
1505
|
+
*/
|
|
1506
|
+
function shareOfWorkers(before, after, percentOfCore) {
|
|
1507
|
+
if (before === null || after === null) return null;
|
|
1508
|
+
const b = sum(before);
|
|
1509
|
+
const a = sum(after);
|
|
1510
|
+
const workerTicks = a.worker - b.worker;
|
|
1511
|
+
const totalTicks = a.total - b.total;
|
|
1512
|
+
if (workerTicks < 0 || totalTicks <= 0 || workerTicks > totalTicks) return null;
|
|
1513
|
+
return {
|
|
1514
|
+
percentOfCore: round1(percentOfCore * (workerTicks / totalTicks)),
|
|
1515
|
+
threads: a.workerThreads
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
var V8_WORKER_COMM = "V8Worker";
|
|
1519
|
+
function sum(tasks) {
|
|
1520
|
+
let worker = 0;
|
|
1521
|
+
let total = 0;
|
|
1522
|
+
let workerThreads = 0;
|
|
1523
|
+
for (const t of tasks) {
|
|
1524
|
+
total += t.ticks;
|
|
1525
|
+
if (t.comm === V8_WORKER_COMM) {
|
|
1526
|
+
worker += t.ticks;
|
|
1527
|
+
workerThreads += 1;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
return {
|
|
1531
|
+
worker,
|
|
1532
|
+
total,
|
|
1533
|
+
workerThreads
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
function round1(n) {
|
|
1537
|
+
return Math.round(n * 10) / 10;
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Per-thread ticks for THIS process, or `null` where that cannot be read.
|
|
1541
|
+
*
|
|
1542
|
+
* `/proc/self/task/<tid>/stat` is `pid (comm) state ppid …`, and `comm` may
|
|
1543
|
+
* itself contain spaces and parentheses — so the split is on the LAST `)`,
|
|
1544
|
+
* never on whitespace. `utime` and `stime` are the 12th and 13th fields after
|
|
1545
|
+
* it. Everything that can fail is inside the try: a non-Linux platform, a
|
|
1546
|
+
* container with `hidepid`, a thread that exits between the readdir and the
|
|
1547
|
+
* read. A thread that vanishes mid-scan is skipped rather than failing the
|
|
1548
|
+
* whole reading — it contributed to neither snapshot's total.
|
|
1549
|
+
*/
|
|
1550
|
+
function readSelfTaskTicks() {
|
|
1551
|
+
try {
|
|
1552
|
+
const tids = readdirSync("/proc/self/task");
|
|
1553
|
+
const out = [];
|
|
1554
|
+
for (const tid of tids) {
|
|
1555
|
+
const parsed = parseTaskStat(tid);
|
|
1556
|
+
if (parsed !== null) out.push(parsed);
|
|
1557
|
+
}
|
|
1558
|
+
return out.length === 0 ? null : out;
|
|
1559
|
+
} catch {
|
|
1560
|
+
return null;
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
function parseTaskStat(tid) {
|
|
1564
|
+
try {
|
|
1565
|
+
const raw = readFileSync(`/proc/self/task/${tid}/stat`, "utf8");
|
|
1566
|
+
const close = raw.lastIndexOf(") ");
|
|
1567
|
+
const open = raw.indexOf(" (");
|
|
1568
|
+
if (close < 0 || open < 0 || close <= open) return null;
|
|
1569
|
+
const comm = raw.slice(open + 2, close);
|
|
1570
|
+
const fields = raw.slice(close + 2).split(" ");
|
|
1571
|
+
const utime = Number(fields[11]);
|
|
1572
|
+
const stime = Number(fields[12]);
|
|
1573
|
+
if (!Number.isFinite(utime) || !Number.isFinite(stime)) return null;
|
|
1574
|
+
return {
|
|
1575
|
+
comm,
|
|
1576
|
+
ticks: utime + stime
|
|
1577
|
+
};
|
|
1578
|
+
} catch {
|
|
1579
|
+
return null;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
/**
|
|
1583
|
+
* The two fields, APPENDED LAST on the `[mem]` line — after everything D446,
|
|
1584
|
+
* D456 and D458 appended, so every Loki prefix written since 2026-08-29 still
|
|
1585
|
+
* matches (D446's rule).
|
|
1586
|
+
*
|
|
1587
|
+
* `procCpu` prints whenever there is a window, including at 0.0 %, because an
|
|
1588
|
+
* idle process is a reading and it is the one that says a runner is alive and
|
|
1589
|
+
* doing nothing. `v8WorkerCpu` is CONDITIONAL and its absence is the reading:
|
|
1590
|
+
* the platform could not be asked. It carries its thread count because "36.6 %
|
|
1591
|
+
* over four threads" and "36.6 % on one" are different faults.
|
|
1592
|
+
*/
|
|
1593
|
+
function formatProcessCpu(reading) {
|
|
1594
|
+
if (reading === null || reading === void 0) return "";
|
|
1595
|
+
const workers = reading.v8Workers;
|
|
1596
|
+
return ` procCpu=${reading.percentOfCore.toFixed(1)}%` + (workers === null ? "" : ` v8WorkerCpu=${workers.percentOfCore.toFixed(1)}%/${workers.threads}thr`);
|
|
1597
|
+
}
|
|
1470
1598
|
//#endregion
|
|
1471
1599
|
//#region src/kernel/heap-watch.ts
|
|
1472
1600
|
/**
|
|
@@ -2066,6 +2194,15 @@ function startHeapWatch(label = HUB_MAIN_HEAP_WATCH_LABEL, sink = consoleSink, i
|
|
|
2066
2194
|
const now = reclaimOptions?.now ?? (() => Date.now());
|
|
2067
2195
|
const gcObserver = probes?.gcObserver ?? nodeGcObserver();
|
|
2068
2196
|
const gcCensus = createGcCensus(gcObserver.install, gcObserver.kinds, now());
|
|
2197
|
+
const cpuProbe = probes?.processCpu;
|
|
2198
|
+
const processCpu = createProcessCpuCensus({
|
|
2199
|
+
cpuUsageUs: cpuProbe?.cpuUsageUs ?? (() => {
|
|
2200
|
+
const u = process.cpuUsage();
|
|
2201
|
+
return u.user + u.system;
|
|
2202
|
+
}),
|
|
2203
|
+
readTasks: cpuProbe?.readTasks ?? readSelfTaskTicks,
|
|
2204
|
+
startedAt: now()
|
|
2205
|
+
});
|
|
2069
2206
|
const triggerMb = reclaimOptions?.triggerMb ?? 1024;
|
|
2070
2207
|
const minIntervalMs = reclaimOptions?.minIntervalMs ?? 12e4;
|
|
2071
2208
|
const steadyStateIntervalMs = reclaimOptions?.steadyStateIntervalMs ?? 6e5;
|
|
@@ -2168,7 +2305,7 @@ function startHeapWatch(label = HUB_MAIN_HEAP_WATCH_LABEL, sink = consoleSink, i
|
|
|
2168
2305
|
const due = at - lastLoggedAt >= intervalMs;
|
|
2169
2306
|
if (mode === "escalated" || due) {
|
|
2170
2307
|
lastLoggedAt = at;
|
|
2171
|
-
const line = format(label, sample, loopDelay?.read(), budgetMb, `${formatHeapSpaces(readSpaces())}${formatEventPlane(eventPlane?.read())}` + formatSocketPlane(socketPlane?.read()) + formatMeshQueue(meshQueue?.()) + formatGcCensus(gcCensus?.read(at)));
|
|
2308
|
+
const line = format(label, sample, loopDelay?.read(), budgetMb, `${formatHeapSpaces(readSpaces())}${formatEventPlane(eventPlane?.read())}` + formatSocketPlane(socketPlane?.read()) + formatMeshQueue(meshQueue?.()) + formatGcCensus(gcCensus?.read(at)) + formatProcessCpu(processCpu.read(at)));
|
|
2172
2309
|
if (sample.nearLimit) sink.warn(`${line} — APPROACHING HEAP LIMIT`);
|
|
2173
2310
|
else if (mode === "escalated") sink.warn(`${line} — heap elevated, sampling every ${probeIntervalMs}ms`);
|
|
2174
2311
|
else sink.info(line);
|