update
This commit is contained in:
117
ts/smartmetrics.pidusage.ts
Normal file
117
ts/smartmetrics.pidusage.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import * as fs from 'fs';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
// Cached system constants
|
||||
let clkTck: number | null = null;
|
||||
let pageSize: number | null = null;
|
||||
|
||||
function getClkTck(): number {
|
||||
if (clkTck === null) {
|
||||
try {
|
||||
clkTck = parseInt(execSync('getconf CLK_TCK', { encoding: 'utf8' }).trim(), 10);
|
||||
} catch {
|
||||
clkTck = 100; // standard Linux default
|
||||
}
|
||||
}
|
||||
return clkTck;
|
||||
}
|
||||
|
||||
function getPageSize(): number {
|
||||
if (pageSize === null) {
|
||||
try {
|
||||
pageSize = parseInt(execSync('getconf PAGESIZE', { encoding: 'utf8' }).trim(), 10);
|
||||
} catch {
|
||||
pageSize = 4096; // standard Linux default
|
||||
}
|
||||
}
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
// History for CPU delta tracking
|
||||
interface ISnapshot {
|
||||
utime: number;
|
||||
stime: number;
|
||||
timestamp: number; // hrtime in seconds
|
||||
}
|
||||
|
||||
const history = new Map<number, ISnapshot>();
|
||||
|
||||
function readProcStat(pid: number): { utime: number; stime: number; rss: number } | null {
|
||||
try {
|
||||
const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf8');
|
||||
// Format: pid (comm) state ppid ... fields
|
||||
// utime is field 14, stime is field 15, rss is field 24 (1-indexed)
|
||||
const closeParenIdx = stat.lastIndexOf(')');
|
||||
if (closeParenIdx === -1) return null;
|
||||
const afterComm = stat.slice(closeParenIdx + 2);
|
||||
const fields = afterComm.split(' ');
|
||||
// fields[0] = state (field 3), so utime = fields[11] (field 14), stime = fields[12] (field 15), rss = fields[21] (field 24)
|
||||
const utime = parseInt(fields[11], 10);
|
||||
const stime = parseInt(fields[12], 10);
|
||||
const rss = parseInt(fields[21], 10);
|
||||
return { utime, stime, rss };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function hrtimeSeconds(): number {
|
||||
const [sec, nsec] = process.hrtime();
|
||||
return sec + nsec / 1e9;
|
||||
}
|
||||
|
||||
export interface IPidUsageResult {
|
||||
cpu: number;
|
||||
memory: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CPU percentage and memory usage for the given PIDs.
|
||||
* CPU% is calculated as a delta between successive calls.
|
||||
*/
|
||||
export async function getPidUsage(
|
||||
pids: number[]
|
||||
): Promise<Record<number, IPidUsageResult>> {
|
||||
const tck = getClkTck();
|
||||
const ps = getPageSize();
|
||||
const result: Record<number, IPidUsageResult> = {};
|
||||
|
||||
for (const pid of pids) {
|
||||
const stat = readProcStat(pid);
|
||||
if (!stat) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const now = hrtimeSeconds();
|
||||
const totalTicks = stat.utime + stat.stime;
|
||||
const memoryBytes = stat.rss * ps;
|
||||
|
||||
const prev = history.get(pid);
|
||||
if (prev) {
|
||||
const elapsedSeconds = now - prev.timestamp;
|
||||
const ticksDelta = totalTicks - (prev.utime + prev.stime);
|
||||
const cpuSeconds = ticksDelta / tck;
|
||||
const cpuPercent = elapsedSeconds > 0 ? (cpuSeconds / elapsedSeconds) * 100 : 0;
|
||||
|
||||
result[pid] = {
|
||||
cpu: cpuPercent,
|
||||
memory: memoryBytes,
|
||||
};
|
||||
} else {
|
||||
// First call for this PID — no delta available, report 0% cpu
|
||||
result[pid] = {
|
||||
cpu: 0,
|
||||
memory: memoryBytes,
|
||||
};
|
||||
}
|
||||
|
||||
// Update history
|
||||
history.set(pid, {
|
||||
utime: stat.utime,
|
||||
stime: stat.stime,
|
||||
timestamp: now,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user