1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
'use strict';
import { readfile } from 'fs';
import * as uloop from 'uloop';
const STEP = 5000, SLOTS = 60;
let ring = [], seq = 0, timer;
function now() {
const c = clock(true);
return c[0] * 1000 + int(c[1] / 1000000);
}
// Tells the instances apart, as the sequence starts over with rpcd.
const id = trim(readfile('/proc/sys/kernel/random/uuid'));
// Counters as the kernel has them. Whoever draws them takes the differences,
// over the time between two samples on the clock they were read by.
function sample() {
const t = now(), cpus = {}, net = {};
for (let line in split(readfile('/proc/stat') ?? '', '\n')) {
if (substr(line, 0, 3) != 'cpu')
break;
const fields = split(line, /\s+/);
cpus[fields[0]] = map(slice(fields, 1, 9), v => +v);
}
const mem = match(readfile('/proc/meminfo', 256) ?? '', /MemAvailable:\s+(\d+)/);
for (let line in slice(split(readfile('/proc/net/dev') ?? '', '\n'), 2)) {
const fields = split(trim(line), /[: ]+/);
if (length(fields) > 9 && fields[0] != 'lo')
net[fields[0]] = [ +fields[1], +fields[9] ];
}
push(ring, [ ++seq, t, cpus, mem ? +mem[1] * 1024 : null, net ]);
while (length(ring) > SLOTS)
shift(ring);
}
// Sampling never stops, so the charts are full whenever somebody looks.
function tick() {
timer.set(STEP);
// An exception here would end rpcd's uloop, not just this timer.
try {
sample();
}
catch (e) {
warn(`luci.dashboard: ${e}\n`);
}
}
timer = uloop.timer(0, tick);
const methods = {
history: {
args: { since: 0, id: 'id' },
call: function(request) {
const since = (request.args?.id == id) ? +(request.args?.since ?? 0) : 0;
return {
id,
now: now(),
step: STEP,
slots: SLOTS,
samples: filter(ring, entry => entry[0] > since)
};
}
}
};
return { 'luci.dashboard': methods };
|