| 88 | } |
| 89 | |
| 90 | export class CgroupV2Source implements MachineStatsSource { |
| 91 | public readonly name = 'cgroup-v2'; |
| 92 | protected log = new Logger('hardware'); |
| 93 | protected lastSample: Sample | null = null; |
| 94 | protected loggedFailure: Set<string> = new Set(); |
| 95 | |
| 96 | protected now: () => number; |
| 97 | protected readFile: ReadFileFn; |
| 98 | |
| 99 | constructor(opts: CgroupSourceOpts = {}) { |
| 100 | this.now = opts.now ?? Date.now; |
| 101 | this.readFile = opts.readFile ?? defaultReadFile; |
| 102 | } |
| 103 | |
| 104 | public async read(): Promise<IResourceLoad> { |
| 105 | const [cpu, memory] = await Promise.all([ |
| 106 | this.readCpu(), |
| 107 | this.readMemory(), |
| 108 | ]); |
| 109 | return { cpu, memory }; |
| 110 | } |
| 111 | |
| 112 | protected async readCpu(): Promise<number | null> { |
| 113 | let usageContent: string; |
| 114 | let maxContent: string; |
| 115 | try { |
| 116 | [usageContent, maxContent] = await Promise.all([ |
| 117 | readWithTimeout('/sys/fs/cgroup/cpu.stat', this.readFile), |
| 118 | readWithTimeout('/sys/fs/cgroup/cpu.max', this.readFile), |
| 119 | ]); |
| 120 | } catch (err) { |
| 121 | this.logOnce('cpu-read', err); |
| 122 | return null; |
| 123 | } |
| 124 | |
| 125 | const usageUsec = parseCpuStatUsageUsec(usageContent); |
| 126 | const cores = parseCpuMax(maxContent); |
| 127 | if (usageUsec === null || cores === null || cores <= 0) { |
| 128 | this.logOnce('cpu-parse', new Error('cgroup v2 cpu parse failed')); |
| 129 | return null; |
| 130 | } |
| 131 | |
| 132 | const timestamp = this.now(); |
| 133 | const previous = this.lastSample; |
| 134 | this.lastSample = { timestamp, usageUsec }; |
| 135 | |
| 136 | if (!previous) return null; |
| 137 | |
| 138 | const dWallMs = timestamp - previous.timestamp; |
| 139 | if (dWallMs <= 0) return null; |
| 140 | const dUsageUsec = usageUsec - previous.usageUsec; |
| 141 | if (dUsageUsec < 0) return null; |
| 142 | |
| 143 | return dUsageUsec / (dWallMs * cores * 1000); |
| 144 | } |
| 145 | |
| 146 | protected async readMemory(): Promise<number | null> { |
| 147 | let currentContent: string; |
nothing calls this directly
no outgoing calls
no test coverage detected