basic/procfs-util: stop counting guest time twice
The kernel reports guest usage in guest/guest-nice and also includes it in user/nice. By adding the guest and guest-nice values, we were counting those ticks twice. C.f. https://gitlab.com/procps-ng/procps/-/blob/master/library/stat.c?ref_type=heads#L612-615 > note: we exclude guest tics from xtot since ... > 'user' already includes 'guest' > 'nice' already includes 'gnice' > (see linux sources: ./kernel/sched/cputime.c, kcpustat_cpu_fetch) Rewrite procfs_cpu_get_usage() to use procfs_cpu_get_ticks() and fix the accounting error.
This commit is contained in:
+12
-31
@@ -128,46 +128,27 @@ static uint64_t calc_gcd64(uint64_t a, uint64_t b) {
|
||||
}
|
||||
|
||||
int procfs_cpu_get_usage(nsec_t *ret) {
|
||||
_cleanup_free_ char *first_line = NULL;
|
||||
unsigned long user_ticks, nice_ticks, system_ticks, irq_ticks, softirq_ticks,
|
||||
guest_ticks = 0, guest_nice_ticks = 0;
|
||||
uint64_t sum, gcd, a, b;
|
||||
const char *p;
|
||||
ProcfsCpuTicks ticks;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = read_one_line_file("/proc/stat", &first_line);
|
||||
r = procfs_cpu_get_ticks(&ticks);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p = first_word(first_line, "cpu");
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
uint64_t ticks_per_second = sysconf_clock_ticks_cached(),
|
||||
sum = ticks.user + ticks.nice + ticks.system + ticks.irq + ticks.softirq,
|
||||
gcd = calc_gcd64(NSEC_PER_SEC, ticks_per_second);
|
||||
|
||||
if (sscanf(p, "%lu %lu %lu %*u %*u %lu %lu %*u %lu %lu",
|
||||
&user_ticks,
|
||||
&nice_ticks,
|
||||
&system_ticks,
|
||||
&irq_ticks,
|
||||
&softirq_ticks,
|
||||
&guest_ticks,
|
||||
&guest_nice_ticks) < 5) /* we only insist on the first five fields */
|
||||
return -EINVAL;
|
||||
/* Reduce NSEC_PER_SEC/ticks_per_second first to avoid overflow of ticks * NSEC_PER_SEC, and split
|
||||
* the multiplication into whole and remainder parts, so that the result stays exact.
|
||||
*
|
||||
* Note that in most cases b == 1 and the second term is trivial. */
|
||||
uint64_t a = NSEC_PER_SEC / gcd,
|
||||
b = ticks_per_second / gcd;
|
||||
|
||||
uint64_t ticks_per_second = sysconf_clock_ticks_cached();
|
||||
|
||||
sum = (uint64_t) user_ticks + (uint64_t) nice_ticks + (uint64_t) system_ticks +
|
||||
(uint64_t) irq_ticks + (uint64_t) softirq_ticks +
|
||||
(uint64_t) guest_ticks + (uint64_t) guest_nice_ticks;
|
||||
|
||||
/* Let's reduce this fraction before we apply it to avoid overflows when converting this to μsec */
|
||||
gcd = calc_gcd64(NSEC_PER_SEC, ticks_per_second);
|
||||
|
||||
a = NSEC_PER_SEC / gcd;
|
||||
b = ticks_per_second / gcd;
|
||||
|
||||
*ret = DIV_ROUND_UP((nsec_t) sum * (nsec_t) a, (nsec_t) b);
|
||||
*ret = (nsec_t) (sum / b * a + DIV_ROUND_UP(sum % b * a, b));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user