Add disk-load's cpu_top tool: CPU by systemd unit and container (issue 741)
No tool named which units or processes used CPU outside containers during the novox overload (issue 739). cpu_top reads each cgroup's cpu.stat (no root needed), ranks units or containers busiest first, and says the machine's CPU in use and the busy time no unit accounts for.
This commit is contained in:
@@ -85,6 +85,57 @@ func Tools(m *Machine) []stdio.Tool {
|
||||
return out, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "cpu_top",
|
||||
Description: "What used the most CPU over a few seconds, from each unit's cgroup cpu.stat, which needs no root: the machine's " +
|
||||
"CPU in use (as % of all CPUs and as CPUs busy, from /proc/stat), then by=unit (the default) each systemd unit and " +
|
||||
"container ranked by CPU, busiest first, with the busy time no unit accounts for; by=container only the containers, " +
|
||||
"by name. Replaces top, systemd-cgtop. (r)",
|
||||
Input: map[string]any{
|
||||
"seconds": map[string]any{"type": "integer", "description": "how long to sample, in seconds (default 3, at most 10)"},
|
||||
"limit": map[string]any{"type": "integer", "description": "how many units or containers to name (default 15, at most 50)"},
|
||||
"by": map[string]any{"type": "string", "enum": []string{"unit", "container"}, "description": "what to rank (default unit)"},
|
||||
},
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
n, err := count(args, "seconds", 3, 1, 10)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w := time.Duration(n) * time.Second
|
||||
limit, err := count(args, "limit", 15, 1, 50)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
by := text(args, "by")
|
||||
switch by {
|
||||
case "":
|
||||
by = "unit"
|
||||
case "unit", "container":
|
||||
default:
|
||||
return nil, fmt.Errorf("by is unit or container, not %q", by)
|
||||
}
|
||||
ac, err := parseCPU(m.read("/proc/stat"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a := m.unitCPU()
|
||||
m.Wait(w)
|
||||
bc, err := parseCPU(m.read("/proc/stat"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := m.unitCPU()
|
||||
machine := machineCPUOver(ac, bc)
|
||||
top, unaccounted := m.topCPU(a, b, w, by, limit, machine.CPUsBusy)
|
||||
return map[string]any{
|
||||
"window_s": w.Seconds(),
|
||||
"by": by,
|
||||
"cpu": machine,
|
||||
"top": top,
|
||||
"unaccounted_pct": unaccounted,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "disk_filesystems",
|
||||
Description: "The space of every filesystem that stores data (not proc, tmpfs or overlay layers): size, used and free in GiB, " +
|
||||
|
||||
Reference in New Issue
Block a user