vmstat: add guest time to stat

Since 2.6.24/33 the kernel knows about guest and guest nice time. That
is the time that is spend in KVM guests. To handle userspace programs
that do not know about this the guest time is also added to user.

Let us provide a guest time column in vmstat that collects both guest
and guest nice into a gu value.

We also subtract that value from the user time as we are now aware of
the guest value.

This commit is different to !113 in several ways:
 * newlib already knows about these to values
 * vmstat summary already shows these values
 * non-wide vmstat squishes the values

So its around the wide vmstat output.

References:
 procps-ng/procps!113
Signed-off-by: Craig Small <csmall@dropbear.xyz>
This commit is contained in:
Christian Borntraeger 2021-04-28 18:51:34 +10:00 committed by Craig Small
parent 2dcbe71f3b
commit 2461bb5bc1
2 changed files with 33 additions and 10 deletions

View File

@ -139,6 +139,7 @@ sy: Time spent running kernel code. (system time)
id: Time spent idle. Prior to Linux 2.5.41, this includes IO\-wait time. id: Time spent idle. Prior to Linux 2.5.41, this includes IO\-wait time.
wa: Time spent waiting for IO. Prior to Linux 2.5.41, included in idle. wa: Time spent waiting for IO. Prior to Linux 2.5.41, included in idle.
st: Time stolen from a virtual machine. Prior to Linux 2.6.11, unknown. st: Time stolen from a virtual machine. Prior to Linux 2.6.11, unknown.
gu: Time spent running KVM guest code (guest time, including guest nice).
.fi .fi
.PP .PP
.SH "FIELD DESCRIPTION FOR DISK MODE" .SH "FIELD DESCRIPTION FOR DISK MODE"

View File

@ -102,7 +102,9 @@ static enum stat_item First_stat_items[] = {
STAT_TIC_SOFTIRQ, STAT_TIC_SOFTIRQ,
STAT_TIC_IDLE, STAT_TIC_IDLE,
STAT_TIC_IOWAIT, STAT_TIC_IOWAIT,
STAT_TIC_STOLEN STAT_TIC_STOLEN,
STAT_TIC_GUEST,
STAT_TIC_GUEST_NICE
}; };
static enum stat_item Loop_stat_items[] = { static enum stat_item Loop_stat_items[] = {
STAT_SYS_PROC_RUNNING, STAT_SYS_PROC_RUNNING,
@ -116,12 +118,15 @@ static enum stat_item Loop_stat_items[] = {
STAT_TIC_DELTA_SOFTIRQ, STAT_TIC_DELTA_SOFTIRQ,
STAT_TIC_DELTA_IDLE, STAT_TIC_DELTA_IDLE,
STAT_TIC_DELTA_IOWAIT, STAT_TIC_DELTA_IOWAIT,
STAT_TIC_DELTA_STOLEN STAT_TIC_DELTA_STOLEN,
STAT_TIC_DELTA_GUEST,
STAT_TIC_DELTA_GUEST_NICE
}; };
enum Rel_statitems { enum Rel_statitems {
stat_PRU, stat_PBL, stat_INT, stat_CTX, stat_PRU, stat_PBL, stat_INT, stat_CTX,
stat_USR, stat_NIC, stat_SYS, stat_IRQ, stat_SRQ, stat_USR, stat_NIC, stat_SYS, stat_IRQ, stat_SRQ,
stat_IDL, stat_IOW, stat_STO, MAX_stat stat_IDL, stat_IOW, stat_STO, stat_GST, stat_GNI,
MAX_stat
}; };
static enum meminfo_item Mem_items[] = { static enum meminfo_item Mem_items[] = {
@ -247,13 +252,13 @@ static void new_header(void)
const char *header = const char *header =
_("procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----"); _("procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----");
const char *wide_header = const char *wide_header =
_("--procs-- -----------------------memory---------------------- ---swap-- -----io---- -system-- --------cpu--------"); _("--procs-- -----------------------memory---------------------- ---swap-- -----io---- -system-- ----------cpu----------");
const char *timestamp_header = _(" -----timestamp-----"); const char *timestamp_header = _(" -----timestamp-----");
const char format[] = const char format[] =
"%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s %2s"; "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s %2s";
const char wide_format[] = const char wide_format[] =
"%2s %2s %12s %12s %12s %12s %4s %4s %5s %5s %4s %4s %3s %3s %3s %3s %3s"; "%4s %4s %12s %12s %12s %12s %4s %4s %5s %5s %4s %4s %3s %3s %3s %3s %3s %3s";
printf("%s", w_option ? wide_header : header); printf("%s", w_option ? wide_header : header);
@ -303,7 +308,9 @@ static void new_header(void)
/* Translation Hint: max 2 chars */ /* Translation Hint: max 2 chars */
_("wa"), _("wa"),
/* Translation Hint: max 2 chars */ /* Translation Hint: max 2 chars */
_("st")); _("st"),
/* Translation Hint: max 2 chars */
_("gu"));
if (t_option) { if (t_option) {
(void) time( &the_time ); (void) time( &the_time );
@ -340,12 +347,12 @@ static void new_format(void)
const char format[] = const char format[] =
"%2lu %2lu %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u"; "%2lu %2lu %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u";
const char wide_format[] = const char wide_format[] =
"%4lu %4lu %12lu %12lu %12lu %12lu %4u %4u %5u %5u %4u %4u %3u %3u %3u %3u %3u"; "%4lu %4lu %12lu %12lu %12lu %12lu %4u %4u %5u %5u %4u %4u %3u %3u %3u %3u %3u %3u";
unsigned int tog = 0; /* toggle switch for cleaner code */ unsigned int tog = 0; /* toggle switch for cleaner code */
unsigned int i; unsigned int i;
long hz; long hz;
long long cpu_use, cpu_sys, cpu_idl, cpu_iow, cpu_sto; long long cpu_use, cpu_sys, cpu_idl, cpu_iow, cpu_sto, cpu_gue;
long long Div, divo2; long long Div, divo2;
unsigned long pgpgin[2], pgpgout[2], pswpin[2] = {0,0}, pswpout[2]; unsigned long pgpgin[2], pgpgout[2], pswpin[2] = {0,0}, pswpout[2];
unsigned int sleep_half; unsigned int sleep_half;
@ -388,6 +395,7 @@ static void new_format(void)
cpu_idl = TICv(stat_IDL); cpu_idl = TICv(stat_IDL);
cpu_iow = TICv(stat_IOW); cpu_iow = TICv(stat_IOW);
cpu_sto = TICv(stat_STO); cpu_sto = TICv(stat_STO);
cpu_gue = TICv(stat_GST) + TICv(stat_GNI);
pgpgin[tog] = VMSTAT_GET(vm_info, VMSTAT_PGPGIN, ul_int); pgpgin[tog] = VMSTAT_GET(vm_info, VMSTAT_PGPGIN, ul_int);
pgpgout[tog] = VMSTAT_GET(vm_info, VMSTAT_PGPGOUT, ul_int); pgpgout[tog] = VMSTAT_GET(vm_info, VMSTAT_PGPGOUT, ul_int);
@ -403,6 +411,7 @@ static void new_format(void)
cpu_idl = 1; cpu_idl = 1;
} }
divo2 = Div / 2UL; divo2 = Div / 2UL;
cpu_use = (cpu_use >= cpu_gue)? cpu_use - cpu_gue : 0;
printf(w_option ? wide_format : format, printf(w_option ? wide_format : format,
SYSv(stat_PRU), SYSv(stat_PRU),
@ -421,7 +430,8 @@ static void new_format(void)
(unsigned)( (100*cpu_sys + divo2) / Div ), (unsigned)( (100*cpu_sys + divo2) / Div ),
(unsigned)( (100*cpu_idl + divo2) / Div ), (unsigned)( (100*cpu_idl + divo2) / Div ),
(unsigned)( (100*cpu_iow + divo2) / Div ), (unsigned)( (100*cpu_iow + divo2) / Div ),
(unsigned)( (100*cpu_sto + divo2) / Div ) (unsigned)( (100*cpu_sto + divo2) / Div ),
(unsigned)( (100*cpu_gue + divo2) / Div )
); );
if (t_option) { if (t_option) {
@ -445,6 +455,7 @@ static void new_format(void)
cpu_idl = DTICv(stat_IDL); cpu_idl = DTICv(stat_IDL);
cpu_iow = DTICv(stat_IOW); cpu_iow = DTICv(stat_IOW);
cpu_sto = DTICv(stat_STO); cpu_sto = DTICv(stat_STO);
cpu_gue = TICv(stat_GST) + TICv(stat_GNI);
pgpgin[tog] = VMSTAT_GET(vm_info, VMSTAT_PGPGIN, ul_int); pgpgin[tog] = VMSTAT_GET(vm_info, VMSTAT_PGPGIN, ul_int);
pgpgout[tog] = VMSTAT_GET(vm_info, VMSTAT_PGPGOUT, ul_int); pgpgout[tog] = VMSTAT_GET(vm_info, VMSTAT_PGPGOUT, ul_int);
pswpin[tog] = VMSTAT_GET(vm_info, VMSTAT_PSWPIN, ul_int); pswpin[tog] = VMSTAT_GET(vm_info, VMSTAT_PSWPIN, ul_int);
@ -473,6 +484,15 @@ static void new_format(void)
Div = cpu_use + cpu_sys + cpu_idl + cpu_iow + cpu_sto; Div = cpu_use + cpu_sys + cpu_idl + cpu_iow + cpu_sto;
if (!Div) Div = 1, cpu_idl = 1; if (!Div) Div = 1, cpu_idl = 1;
divo2 = Div / 2UL; divo2 = Div / 2UL;
/* guest time is also in user time, we need to subtract. Due to timing
* effects guest could be larger than user. We use 0 that case */
if (cpu_use >= cpu_gue) {
cpu_use -= cpu_gue;
} else {
cpu_use = 0;
}
printf(w_option ? wide_format : format, printf(w_option ? wide_format : format,
SYSv(stat_PRU), SYSv(stat_PRU),
SYSv(stat_PBL), SYSv(stat_PBL),
@ -501,7 +521,9 @@ static void new_format(void)
/* wa */ /* wa */
(unsigned)( (100*cpu_iow+divo2)/Div ), (unsigned)( (100*cpu_iow+divo2)/Div ),
/* st */ /* st */
(unsigned)( (100*cpu_sto+divo2)/Div ) (unsigned)( (100*cpu_sto+divo2)/Div ),
/* gu */
(unsigned)( (100*cpu_gue+divo2)/Div )
); );
if (t_option) { if (t_option) {