Further CPU Experimental Changes
This commit is contained in:
@@ -6,6 +6,7 @@ import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { CollectorPlugin } from './plugin.js'
|
||||
import { registerChart } from '../../../shared/metrics.js'
|
||||
import { readFileBuf } from '../../utils/fd-cache.js'
|
||||
|
||||
export function isBcacheEnabled() {
|
||||
const v = process.env.PEARDATA_BCACHE
|
||||
@@ -15,11 +16,8 @@ export function isBcacheEnabled() {
|
||||
}
|
||||
|
||||
function readNum(p) {
|
||||
try {
|
||||
return Number(fs.readFileSync(p, 'utf8').trim()) || 0
|
||||
} catch {
|
||||
return 0
|
||||
}
|
||||
const raw = readFileBuf(p)
|
||||
return raw ? Number(raw.trim()) || 0 : 0
|
||||
}
|
||||
|
||||
export class BcacheCollector extends CollectorPlugin {
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
} from '../../../shared/metrics.js'
|
||||
import { resolveContainerLabel } from '../../../shared/container-names.js'
|
||||
import { loadContainerNameMap, resolveDockerSocket } from './docker.js'
|
||||
import { readFileBuf } from '../../utils/fd-cache.js'
|
||||
import logger from '../../utils/logger.js'
|
||||
|
||||
const log = logger.child('cgroups')
|
||||
@@ -37,11 +38,7 @@ function maxCgroups() {
|
||||
}
|
||||
|
||||
function readFile(p) {
|
||||
try {
|
||||
return fs.readFileSync(p, 'utf8')
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
return readFileBuf(p)
|
||||
}
|
||||
|
||||
function cgroupRoot() {
|
||||
|
||||
@@ -10,6 +10,7 @@ import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { CollectorPlugin } from './plugin.js'
|
||||
import { registerChart } from '../../../shared/metrics.js'
|
||||
import { readFileBuf } from '../../utils/fd-cache.js'
|
||||
import { execFile } from '../../utils/exec.js'
|
||||
import logger from '../../utils/logger.js'
|
||||
|
||||
@@ -39,11 +40,7 @@ export function isDmcacheEnabled() {
|
||||
}
|
||||
|
||||
function readFile(p) {
|
||||
try {
|
||||
return fs.readFileSync(p, 'utf8')
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
return readFileBuf(p)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@ import net from 'net'
|
||||
import os from 'os'
|
||||
import { EventEmitter } from 'events'
|
||||
import { execFile } from '../../utils/exec.js'
|
||||
import { readFileBuf } from '../../utils/fd-cache.js'
|
||||
import {
|
||||
SAMPLE_INTERVAL_MS,
|
||||
registerChart,
|
||||
@@ -59,11 +60,7 @@ export function resolveDockerSocket(preferred) {
|
||||
}
|
||||
|
||||
function readFile(p) {
|
||||
try {
|
||||
return fs.readFileSync(p, 'utf8')
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
return readFileBuf(p)
|
||||
}
|
||||
|
||||
function bytesToMiB(n) {
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
SAMPLE_INTERVAL_MS,
|
||||
registerChart,
|
||||
} from '../../../shared/metrics.js'
|
||||
import { readFileCached, readFileBuf } from '../../utils/fd-cache.js'
|
||||
import { extractHelper, hasEmbeddedHelper } from '../../native/extract-helper.js'
|
||||
import logger from '../../utils/logger.js'
|
||||
|
||||
@@ -202,13 +203,12 @@ export class EbpfCollector extends EventEmitter {
|
||||
_readVm() {
|
||||
/** @type {Record<string, number>} */
|
||||
const out = {}
|
||||
try {
|
||||
for (const line of fs.readFileSync('/proc/vmstat', 'utf8').split('\n')) {
|
||||
const raw = readFileCached('/proc/vmstat')
|
||||
if (raw) {
|
||||
for (const line of raw.split('\n')) {
|
||||
const [k, v] = line.trim().split(/\s+/)
|
||||
if (k) out[k] = Number(v) || 0
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -228,30 +228,27 @@ export class EbpfCollector extends EventEmitter {
|
||||
const den = dHit + dMiss
|
||||
|
||||
let fileNr = [0, 0]
|
||||
try {
|
||||
const parts = fs.readFileSync('/proc/sys/fs/file-nr', 'utf8').trim().split(/\s+/)
|
||||
const fileNrRaw = readFileCached('/proc/sys/fs/file-nr')
|
||||
if (fileNrRaw) {
|
||||
const parts = fileNrRaw.trim().split(/\s+/)
|
||||
fileNr = [Number(parts[0]) || 0, Number(parts[2]) || 0]
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
let forks = 0
|
||||
let ctxt = 0
|
||||
try {
|
||||
for (const line of fs.readFileSync('/proc/stat', 'utf8').split('\n')) {
|
||||
const statRaw = readFileCached('/proc/stat')
|
||||
if (statRaw) {
|
||||
for (const line of statRaw.split('\n')) {
|
||||
if (line.startsWith('processes ')) forks = Number(line.slice(10)) || 0
|
||||
if (line.startsWith('ctxt ')) ctxt = Number(line.slice(5)) || 0
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
let tcp = 0
|
||||
try {
|
||||
const m = fs.readFileSync('/proc/net/sockstat', 'utf8').match(/TCP:\s+inuse\s+(\d+)/)
|
||||
const sockRaw = readFileCached('/proc/net/sockstat')
|
||||
if (sockRaw) {
|
||||
const m = sockRaw.match(/TCP:\s+inuse\s+(\d+)/)
|
||||
if (m) tcp = Number(m[1]) || 0
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
const batch = [
|
||||
|
||||
@@ -11,6 +11,7 @@ import path from 'path'
|
||||
import os from 'os'
|
||||
import { CollectorPlugin } from './plugin.js'
|
||||
import { registerChart } from '../../../shared/metrics.js'
|
||||
import { readFileBuf } from '../../utils/fd-cache.js'
|
||||
import logger from '../../utils/logger.js'
|
||||
|
||||
const log = logger.child('fs-stats')
|
||||
@@ -23,11 +24,7 @@ export function isFsStatsEnabled() {
|
||||
}
|
||||
|
||||
function readFile(p) {
|
||||
try {
|
||||
return fs.readFileSync(p, 'utf8')
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
return readFileBuf(p)
|
||||
}
|
||||
|
||||
function readNum(p) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import fs from 'fs'
|
||||
import os from 'os'
|
||||
import { EventEmitter } from 'events'
|
||||
import { SAMPLE_INTERVAL_MS, registerChart } from '../../../shared/metrics.js'
|
||||
import { readFileBuf } from '../../utils/fd-cache.js'
|
||||
import logger from '../../utils/logger.js'
|
||||
|
||||
const log = logger.child('mdstat')
|
||||
@@ -31,11 +32,7 @@ const CHART_HEALTH = {
|
||||
}
|
||||
|
||||
function readFile(p) {
|
||||
try {
|
||||
return fs.readFileSync(p, 'utf8')
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
return readFileBuf(p)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@ import path from 'path'
|
||||
import os from 'os'
|
||||
import { EventEmitter } from 'events'
|
||||
import { SAMPLE_INTERVAL_MS, registerChart } from '../../../shared/metrics.js'
|
||||
import { readFileBuf } from '../../utils/fd-cache.js'
|
||||
import logger from '../../utils/logger.js'
|
||||
|
||||
const log = logger.child('processes')
|
||||
@@ -50,6 +51,10 @@ function sanitizeDim(name) {
|
||||
return s || 'unknown'
|
||||
}
|
||||
|
||||
const PROC_EXTRA_CACHE = new Map()
|
||||
const PROC_EXTRA_TICK = 5
|
||||
let procTickCount = 0
|
||||
|
||||
/**
|
||||
* @returns {Array<{ pid: number, name: string, utime: number, stime: number, rssPages: number, threads: number, readBytes: number|null, writeBytes: number|null }>|null}
|
||||
*/
|
||||
@@ -61,47 +66,52 @@ export function listProcStats() {
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
procTickCount = (procTickCount + 1) % PROC_EXTRA_TICK
|
||||
const readExtra = procTickCount === 0
|
||||
|
||||
/** @type {Array<{ pid: number, name: string, utime: number, stime: number, rssPages: number, threads: number, readBytes: number|null, writeBytes: number|null }>} */
|
||||
const out = []
|
||||
for (const ent of dirs) {
|
||||
if (!/^\d+$/.test(ent)) continue
|
||||
const pid = Number(ent)
|
||||
let raw
|
||||
try {
|
||||
raw = fs.readFileSync(path.join('/proc', ent, 'stat'), 'utf8')
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
const raw = readFileBuf(path.join('/proc', ent, 'stat'))
|
||||
if (!raw) continue
|
||||
const open = raw.indexOf('(')
|
||||
const close = raw.lastIndexOf(')')
|
||||
if (open < 0 || close < open) continue
|
||||
const name = raw.slice(open + 1, close)
|
||||
const rest = raw.slice(close + 2).split(/\s+/)
|
||||
// fields after comm: state(0) … utime(11) stime(12) … rss(21)
|
||||
const utime = Number(rest[11])
|
||||
const stime = Number(rest[12])
|
||||
const rssPages = Number(rest[21])
|
||||
if (!Number.isFinite(utime) || !Number.isFinite(stime)) continue
|
||||
|
||||
let threads = 0
|
||||
try {
|
||||
const status = fs.readFileSync(path.join('/proc', ent, 'status'), 'utf8')
|
||||
const m = status.match(/^Threads:\s*(\d+)/m)
|
||||
if (m) threads = Number(m[1]) || 0
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
let readBytes = null
|
||||
let writeBytes = null
|
||||
try {
|
||||
const ioRaw = fs.readFileSync(path.join('/proc', ent, 'io'), 'utf8')
|
||||
|
||||
if (readExtra) {
|
||||
const status = readFileBuf(path.join('/proc', ent, 'status'))
|
||||
if (status) {
|
||||
const m = status.match(/^Threads:\s*(\d+)/m)
|
||||
if (m) threads = Number(m[1]) || 0
|
||||
}
|
||||
const ioRaw = readFileBuf(path.join('/proc', ent, 'io'))
|
||||
if (ioRaw) {
|
||||
const rb = ioRaw.match(/^read_bytes:\s*(\d+)/m)
|
||||
const wb = ioRaw.match(/^write_bytes:\s*(\d+)/m)
|
||||
if (rb) readBytes = Number(rb[1]) || 0
|
||||
if (wb) writeBytes = Number(wb[1]) || 0
|
||||
} catch {
|
||||
// unreadable for this pid
|
||||
}
|
||||
}
|
||||
const cached = PROC_EXTRA_CACHE.get(pid)
|
||||
if (cached) {
|
||||
if (threads === 0) threads = cached.threads
|
||||
if (readBytes === null) readBytes = cached.readBytes
|
||||
if (writeBytes === null) writeBytes = cached.writeBytes
|
||||
}
|
||||
if (readExtra || !cached) {
|
||||
PROC_EXTRA_CACHE.set(pid, { threads, readBytes, writeBytes })
|
||||
}
|
||||
|
||||
out.push({
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
makeSensorTempChart,
|
||||
makeThermalZoneChart,
|
||||
} from '../../../shared/metrics.js'
|
||||
import { readFileBuf } from '../../utils/fd-cache.js'
|
||||
import logger from '../../utils/logger.js'
|
||||
|
||||
const log = logger.child('sensors')
|
||||
@@ -26,11 +27,7 @@ export function isSensorsEnabled() {
|
||||
}
|
||||
|
||||
function readFile(p) {
|
||||
try {
|
||||
return fs.readFileSync(p, 'utf8')
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
return readFileBuf(p)
|
||||
}
|
||||
|
||||
function makeSensorFanChart(chip, label) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import fs from 'fs'
|
||||
import os from 'os'
|
||||
import { EventEmitter } from 'events'
|
||||
import { SAMPLE_INTERVAL_MS, registerChart } from '../../../shared/metrics.js'
|
||||
import { readFileBuf } from '../../utils/fd-cache.js'
|
||||
import logger from '../../utils/logger.js'
|
||||
|
||||
const log = logger.child('sockets')
|
||||
@@ -42,12 +43,8 @@ function countTcp(file) {
|
||||
syn_recv: 0,
|
||||
other: 0,
|
||||
}
|
||||
let raw
|
||||
try {
|
||||
raw = fs.readFileSync(file, 'utf8')
|
||||
} catch {
|
||||
return counts
|
||||
}
|
||||
const raw = readFileBuf(file)
|
||||
if (!raw) return counts
|
||||
for (const line of raw.split('\n').slice(1)) {
|
||||
const parts = line.trim().split(/\s+/)
|
||||
if (parts.length < 4) continue
|
||||
@@ -60,11 +57,9 @@ function countTcp(file) {
|
||||
}
|
||||
|
||||
function countUdp(file) {
|
||||
try {
|
||||
return Math.max(0, fs.readFileSync(file, 'utf8').trim().split('\n').length - 1)
|
||||
} catch {
|
||||
return 0
|
||||
}
|
||||
const raw = readFileBuf(file)
|
||||
if (!raw) return 0
|
||||
return Math.max(0, raw.trim().split('\n').length - 1)
|
||||
}
|
||||
|
||||
export class SocketsCollector extends EventEmitter {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import fs from 'fs'
|
||||
|
||||
const READ_BUF = Buffer.alloc(65536)
|
||||
const SMALL_BUF = Buffer.alloc(8192)
|
||||
const fdCache = new Map()
|
||||
|
||||
export function readFileCached(path) {
|
||||
@@ -24,6 +25,25 @@ export function readFileCached(path) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a file using the shared small buffer, no FD caching.
|
||||
* Good for dynamic paths (per-PID, per-cgroup) where open+close each tick is acceptable
|
||||
* but Buffer allocation is not.
|
||||
*/
|
||||
export function readFileBuf(path) {
|
||||
try {
|
||||
const fd = fs.openSync(path, 'r')
|
||||
try {
|
||||
const bytes = fs.readSync(fd, SMALL_BUF, 0, SMALL_BUF.length, 0)
|
||||
return SMALL_BUF.toString('utf8', 0, bytes)
|
||||
} finally {
|
||||
fs.closeSync(fd)
|
||||
}
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function closeFdCache() {
|
||||
for (const [path, entry] of fdCache) {
|
||||
if (entry) {
|
||||
|
||||
Reference in New Issue
Block a user