svn commit: r317231 - head/usr.bin/systat

Bruce Evans brde at optusnet.com.au
Fri Apr 21 04:15:31 UTC 2017


On Thu, 20 Apr 2017, Jung-uk Kim wrote:

> Log:
>  Fix systat(1) regression.  It was broken by r317061.

It is more broken than before.  Now it fails when the kernel is older
than the utility instead of vice versa.  When it fails, the failures
are more serious than before.  systat -v actually does complete
checking for errors (some are only errors in itself), but mishandles
them.  It reports the errors on the status line and continues.  The
error message overwrites the previous one.  Continuing is more broken
than before:

Previously, reading 64-bit values into 32-bit variables truncated them,
just like a correct fix would do except for the error messages.  Large
values often never occur, and used to be truncated in the kernel, so
noting would be lost by truncating.  The support for 64-bit types would
just remain intentionally unfinished instead of wrong.  systat has a
lot of modes and fields in which it only reports delta values.  Then
truncation doesn't matter unless it occurs more often than the refresh
interval.

Now, reading 32-bit values into 64-bit variabeles leaves garbage in
the top bits.  I think the garbage is initially 0 and remains 0, since
the variables are statically initializated and short reads by sysctl()
don't change the top bits.  But delta-values are broken if values that
need 64-bit types actually occur.  E.g., after increasing a 32-bit
value of 0xffffffff with overflow to 1, 32-bit subtraction gave the
correct delta of 2, but 64-bit subtraction gives
1 - (2**32 - 1) = 2 - 2**32 = 0xffffffff00000002.

> Modified: head/usr.bin/systat/vmstat.c
> ==============================================================================
> --- head/usr.bin/systat/vmstat.c	Thu Apr 20 21:48:54 2017	(r317230)
> +++ head/usr.bin/systat/vmstat.c	Thu Apr 20 22:30:39 2017	(r317231)
> @@ -70,35 +70,35 @@ static const char sccsid[] = "@(#)vmstat
>
> static struct Info {
> 	long	time[CPUSTATES];
> -	u_int v_swtch;		/* context switches */
> -	u_int v_trap;		/* calls to trap */
> -	u_int v_syscall;	/* calls to syscall() */
> -	u_int v_intr;		/* device interrupts */
> -	u_int v_soft;		/* software interrupts */
> +	uint64_t v_swtch;	/* context switches */
> +	uint64_t v_trap;	/* calls to trap */
> +	uint64_t v_syscall;	/* calls to syscall() */
> +	uint64_t v_intr;	/* device interrupts */
> +	uint64_t v_soft;	/* software interrupts */

systat -v may also have problems printing large values.  It mostly
uses PUTRATE(), and I think that handles 64-bit types not very badly,
though compilers tend to have bugs converting uint64_t to floating
point and systat -v uses float since it doesn't need much precision.
PUTRATE() on a uint64_t calculates the delta in that type and then
scales in double precision and then prints in single precision.  The
overflowed value 0xffffffff00000002 gives a huge rate, say only 1e17
= 100P (P = peta) after scaling down the value from 1.8e19 = 18E (E =
exa).  putfloat() only supports up to small numbers of mega<unit> and
prints larger values as '*'s.

systat -v thus doesn't actually support 64-bit values.  32-bit values
only go up to 4G or 4294M.  With no scaling, 4294M stays large, but
some fields are wide enough to print it so it is not printed as '*'s.
64-bit values go up to 18E.

PUTRATE() already had to be careful with types to get suitable overflow
when calculating the deltas.  It is careful enough.  It assigns to
unsigned integer fields.  This gives modular arithmetic.  Expressions
like (new.fld - old.fld) / (float)rate would give sign extension/overflow
bugs depending on the type of fld in a MD way.

Bruce


More information about the svn-src-all mailing list