Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 1 Jun 2015 19:48:45 +1000 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        Dimitry Andric <dim@freebsd.org>
Cc:        src-committers@freebsd.org, svn-src-all@freebsd.org,  svn-src-head@freebsd.org
Subject:   Re: svn commit: r283870 - head/sys/amd64/amd64
Message-ID:  <20150601185112.V1517@besplex.bde.org>
In-Reply-To: <201506010650.t516oeNY080402@svn.freebsd.org>
References:  <201506010650.t516oeNY080402@svn.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Mon, 1 Jun 2015, Dimitry Andric wrote:

> Log:
>  Remove unneeded NULL checks in amd64's trap_fatal().
>
>  Since td_name is an array member of struct thread, it can never be NULL,
>  so the check can be removed.  In addition, curproc can never be NULL,
>  so remove the if statement, and splice the two printfs() together.

I fixed the first part of this in my version (for p_comm) for i386 long ago.

>  While here, remove the u_long cast, and use the correct printf format
>  specifier curproc->p_pid.

This was correct, modulo a harmless sign error.  Now it assumes that pid_t
is int.

Careful code elsewhere, and in my i386 version here, casts pid_t to signed
long for printing.  More careful code would cast pid_t to intmax_t, but
that would be excessive.  The less careful code here was copied from i386.

amd64 and i386 are MD, so can hard-code assumptions about type sizes, but
this is still fragile.  In FreeBSD-1 (and presumably Net/2), pid_t was
signed short and PID_MAX was 30000.  Signed short is just barely enough
to hold 30000, and 30000 is just small enough to fit in signed short.
In 4.4BSD-Lite1, pid_t was expanded to signed long, although PID_MAX
was not expanded.  pid_t was apparently expanded for portability.  If
PID_MAX is expanded above 32767 then it is too large for a portable
signed short, and also too large for a portable signed int, so before
<stdint.h> existed, then only portable type for pid_t was signed long
and 4.4BSD used this.  But in practice, signed long is the most
unportable type, since is usually actually longer than int on 64-bit
systems, so using it mainly gives binary compatibility problems.  These
problem for pid_t and most of the other long types were fixed in NetBSD
and merged into 4.4BSD-Lite2, or vice versa, and merged into FreeBSD a
few years later (1997).  So pid_t now has type int32_t in FreeBSD.

Code that was careful about printing pid_t's always cast to long for
printing and never noticed the 2 previous type changes and wouldn't
notice any further ones, except C99 broke the promise that long is the
longest signed integer type, so the careful code might be broken by
future expansion of pid_t.

> Modified: head/sys/amd64/amd64/trap.c
> ==============================================================================
> --- head/sys/amd64/amd64/trap.c	Mon Jun  1 06:14:17 2015	(r283869)
> +++ head/sys/amd64/amd64/trap.c	Mon Jun  1 06:50:39 2015	(r283870)
> @@ -840,14 +840,8 @@ trap_fatal(frame, eva)
> 	if (frame->tf_rflags & PSL_RF)
> 		printf("resume, ");
> 	printf("IOPL = %ld\n", (frame->tf_rflags & PSL_IOPL) >> 12);
> -	printf("current process		= ");
> -	if (curproc) {
> -		printf("%lu (%s)\n",
> -		    (u_long)curproc->p_pid, curthread->td_name ?
> -		    curthread->td_name : "");
> -	} else {
> -		printf("Idle\n");
> -	}
> +	printf("current process		= %d (%s)\n",
> +	    curproc->p_pid, curthread->td_name);

Technically, it is still an error to print int32_t's using %d format.
This assumes that ints are at least 32 bits.  The amd64 and i386 can
safely assume this.  Also, POSIX started requiring at least 32-bit ints
in about 2007.  But it is easier not to assume this.

In FreeBSD-1, the i386 code correspoding to the above was:

X 	if (curproc) {
X 		printf("%d (%s)\n",
X 		    curproc->p_pid, curproc->p_comm ?
X 		    curproc->p_comm : "");
X 	} else {
X 		printf("Idle\n");
X 	}

In this, the curproc check was correct (curproc was sometimes NULL),
the %d format was correct (pid_t was signed short), and the p_comm
check was garbage as now (p_comm was an array, and even if it was
a null pointer it is feature to let printf() print it as "(null)"
since it is unexpected for it to be either empty or null).

The %d format broke in FreeBSD-2 when FreeBSD was forced to merge with
4.4BSD-Lite-1, and the (wrong but adequate) fix for this was to cast
to unsigned long and change the format to %lu.  The sign mismatches
from this are harmless, but it is easier to not have any mismatches
than to verify that mismatches are harmless.  Casting to int and keeping
the %d format would also have worked, but again it was easier to not
change the type, or to upcast the type (casting to long would have become
an upcast when pid_t was reduced to int32_t a few years later).  C99's
breakage of long, and impending problems with intmax_t, made it not so
clear that upcasting is right or what the upcast should be to.
Upcasting to intmax_t is now only to 64 bits, but even that is excessive
for pid_t on 32-bit arches.  128-bit integers will require intmax_t to
be 128 bits, and that will be excessive for pid_t on on 64 bit arches.
C99 also has the PRI* formats, but those are worse than what they fix.
They are harder to use than upcasts, and just fix runtime space+time
bloat from larger than necessary types.

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20150601185112.V1517>