#2131·gopsutil

[cpu][openbsd]: cpu-total is the per-CPU average, so TestTimes fails on hosts with 4+ CPUs

Author: neilpangCreated Aug 3, 2026Updated Aug 3, 2026

Describe the bug

cpu.TestTimes fails on OpenBSD on any host with 4 or more online CPUs:

--- FAIL: TestTimes (0.00s)
    cpu_test.go:51: {"cpu":"cpu-total","user":22.4,"system":17.2,"idle":10.7,...}
    cpu_test.go:57: Relative error is too high: 2 (expected) < 3.001336302895323 (actual)
    cpu_test.go:60: Relative error is too high: 2 (expected) < 3 (actual)
    cpu_test.go:63: Relative error is too high: 2 (expected) < 3.000000000000001 (actual)

The relative error is exactly ncpu - 1. hw.ncpu was 4 on that host, and all three fields came out at 3.0.

To Reproduce

go
// on OpenBSD, on a host with 4 or more online CPUs
total, _ := cpu.Times(false)  // KERN_CPTIME
per, _ := cpu.Times(true)     // KERN_CPUSTATS, one per CPU
// sum(per) == ncpu * total[0]

Expected behavior

The test asserts that the per-CPU times sum to roughly cpu-total, with margin = 2.0. On OpenBSD they do not, because the two paths are not on the same scale: KERN_CPTIME is averaged over the online CPUs. From sys/kern/kern_sysctl.c (lines 653-675):

c
case KERN_CPTIME:
	CPU_INFO_FOREACH(cii, ci) {
		if (!cpu_is_online(ci))
			continue;
		n++;
		sysctl_ci_cp_time(ci, ci_cp_time);
		for (i = 0; i < CPUSTATES; i++)
			cp_time[i] += ci_cp_time[i];
	}

	for (i = 0; i < CPUSTATES; i++)
		cp_time[i] /= n;

https://github.com/openbsd/src/blob/master/sys/kern/kern_sysctl.c

So sum(perCPU) == ncpu * cpuTotal, and margin = 2.0 only holds while ncpu <= 3.

This may be more than a test tolerance. On Linux, cpu-total comes from the cpu line of /proc/stat, which is the sum across CPUs. If OpenBSD returns the average, cpu.Times(false) means something different there. It cancels out in a percentage computed over two samples, so it may not matter in practice -- your call.

If you do want a sum, KERN_CPTIME2 returns un-averaged per-CPU times, and cpu_openbsd.go already reads hw.ncpuonline elsewhere, which is the same divisor the kernel uses.

Environment (please complete the following information):

  • OpenBSD: OpenBSD 7.9 amd64, go1.26.2, gopsutil master, hw.ncpu=4

Additional context

Found while running the suite on OpenBSD for the first time: https://github.com/neilpang/gopsutil/actions/runs/30751019667/job/91504958976

Filed separately from the Process.Times() panic, since they are unrelated.