Subject: bin/3585: csh "time" bug
To: None <gnats-bugs@gnats.netbsd.org>
From: Erik E. Fair <fair@atomic.clock.org>
List: netbsd-bugs
Date: 05/08/1997 02:05:39
>Number:         3585
>Category:       bin
>Synopsis:       csh "time" prints negative CPU percentage for jobs with more tahn 6 hours of CPU (int overflow)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu May  8 02:20:01 1997
>Last-Modified:
>Originator:     Erik E. Fair
>Organization:
	International Organization of Internet Clock Watchers
>Release:        NetBSD-current of May 7, 1997
>Environment:

System: NetBSD atomic.clock.org 1.2D NetBSD 1.2D (GENERIC) #42: Wed Apr 23 08:06:27 PDT 1997 root@atomic.clock.org:/usr/src/sys/arch/sparc/compile/GENERIC sparc


>Description:
	The percentage calculation in /usr/src/bin/csh/time.c (prusage)
	overflows a 32-bit int if total CPU time is more than 21475 seconds.

>How-To-Repeat:
	% time job-that-takes-long-enough
	21541.6u 5628.2s 8:13:33.08 -53.-2% 0+0k 54448+184212io 342848pf+0w

>Fix:

*** time.c.orig	Tue Jan 14 04:06:02 1997
--- time.c	Thu May  8 01:53:00 1997
***************
*** 56,61 ****
--- 56,63 ----
   */
  static void	pdeltat __P((struct timeval *, struct timeval *));
  
+ extern char * strpct __P((u_long num, u_long denom, u_int digits));
+ 
  void
  settimes()
  {
***************
*** 173,181 ****
  
  	    case 'P':		/* percent time spent running */
  		/* check if it did not run at all */
! 		i = (ms == 0) ? 0 : (t * 1000 / ms);
! 		/* nn.n% */
! 		(void) fprintf(cshout, "%ld.%01ld%%", i / 10, i % 10);
  		break;
  
  	    case 'W':		/* number of swaps */
--- 175,185 ----
  
  	    case 'P':		/* percent time spent running */
  		/* check if it did not run at all */
! 		if (ms == 0) {
! 			(void) fputs("0.0%", cshout);
! 		} else {
! 			(void) fputs(strpct((ulong)t, (ulong)ms, 1), cshout);
! 		}
  		break;
  
  	    case 'W':		/* number of swaps */



## New File: strpct.c (suggested for C library) ##############################

#include <stdio.h>
#include <machine/limits.h>
#include <sys/types.h>

/*
** Calculate a percentage without resorting to floating point
** and return a pointer to a string
**
** "digits" is the number of digits past the decimal place you want
** (zero being the straight percentage with no decimals)
**
** Erik E. Fair <fair@clock.org>, May 8, 1997
*/

extern char * strpct(u_long num, u_long denom, u_int digits);

char *
strpct(numerator, denominator, digits)
u_long	numerator, denominator;
u_int	digits;
{
	register int i;
	u_long result, factor = 100L;
	static char	percent[32];

	/* I should check for digit overflow here, too XXX */
	for(i = 0; i < digits; i++) {
		factor *= 10;
	}

	/* watch out for overflow! */
	if (numerator < (ULONG_MAX / factor)) {
		numerator *= factor;
	} else {
		/* toss some of the bits of lesser significance */
		denominator /= factor;
	}

	/* divide by zero is just plain bad */
	if (denominator == 0L) {
		denominator = 1L;
	}

	result = numerator / denominator;

	if (digits == 0) {
		(void) snprintf(percent, sizeof(percent), "%lu%%", result);
	} else {
		char	fmt[32];

		/* indirection to produce the right output format */
		(void) snprintf(fmt, sizeof(fmt), "%%lu.%%0%ulu%%%%", digits);

		factor /= 100L;		/* undo initialization */

		(void) snprintf(percent, sizeof(percent),
			fmt, result / factor, result % factor);
	}	

	return(percent);
}
>Audit-Trail:
>Unformatted: