Subject: bin/18235: increase resolution of times in top
To: None <gnats-bugs@gnats.netbsd.org>
From: None <dsl@l8s.co.uk>
List: netbsd-bugs
Date: 09/08/2002 19:26:59
>Number:         18235
>Category:       bin
>Synopsis:       increase resolution of times in top
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sun Sep 08 11:24:01 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator:     David Laight
>Release:        NetBSD 1.6H
>Organization:
no
>Environment:
System: NetBSD snowdrop 1.6H NetBSD 1.6H (GENERIC) #164: Sat Sep 7 14:19:53 BST 2002
dsl@snowdrop:/oldroot/usr/bsd-current/src/sys/arch/i386/compile/GENERIC i386
Architecture: i386
Machine: i386
>Description:
	The 'TIME' field reported by top is 0:00 for most processes because
	fractions of a second are not displayed.
	Additionally when the list is sorted by 'time' only whole seconds are used.
	(Actually I do wonder if child time should be included, as this would
	give a true idea of why the system is busy.)
>How-To-Repeat:
	inspection
>Fix:
	Apply following patch:
	output then looks like:

  PID USERNAME PRI NICE   SIZE   RES STATE      TIME   WCPU    CPU COMMAND
14141 dsl       69    4    12K   80K RUN        5h39 88.04% 88.04% x
  346 root       2    0    13M   29M select    87.34  7.47%  7.47% XFree86
  562 dsl        2    0    22M   46M select     1:57  2.93%  2.93% mozilla-bin
    8 root      18    0     0K   29M RUN       43.72  0.05%  0.05% [ioflush]


Index: m_netbsd15.c
===================================================================
RCS file: /cvsroot/basesrc/usr.bin/top/machine/m_netbsd15.c,v
retrieving revision 1.16
diff -u -r1.16 m_netbsd15.c
--- m_netbsd15.c	2002/03/23 01:28:11	1.16
+++ m_netbsd15.c	2002/09/08 12:17:24
@@ -64,8 +64,8 @@
 #include "loadavg.h"
 
 void percentages64 __P((int, int *, u_int64_t *, u_int64_t *, u_int64_t *));
+static char *time_format(ulong, ulong);
 
-
 /* get_process_info passes back a handle.  This is what it looks like: */
 
 struct handle {
@@ -459,7 +459,6 @@
 	char *(*get_userid) __P((int));
 {
 	struct kinfo_proc2 *pp;
-	long cputime;
 	double pct;
 	struct handle *hp;
 	const char *statep;
@@ -497,13 +496,6 @@
 		comm[COMSIZ - 1] = '\0';
 	}
 
-#if 0
-	/* This does not produce the correct results */
-	cputime = pp->p_uticks + pp->p_sticks + pp->p_iticks;
-#else
-	cputime = pp->p_rtime_sec;	/* This does not count interrupts */
-#endif
-
 	/* calculate the base for cpu percentages */
 	pct = pctdouble(pp->p_pctcpu);
 
@@ -537,7 +529,7 @@
 	    format_k(pagetok(PROCSIZE(pp))),
 	    format_k(pagetok(pp->p_vm_rssize)),
 	    statep,
-	    format_time(cputime),
+	    time_format(pp->p_rtime_sec, pp->p_rtime_usec),
 	    100.0 * weighted_cpu(pct, pp),
 	    100.0 * pct,
 	    printable(pp->p_comm));
@@ -571,9 +563,8 @@
 	    (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
 
 #define ORDERKEY_CPTICKS \
-	if (lresult = (pctcpu)(p2)->p_rtime_sec \
-		    - (pctcpu)(p1)->p_rtime_sec,\
-	    (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
+	if ((result = (p2)->p_rtime_sec - (p1)->p_rtime_sec) == 0) \
+	if ((result = (p2)->p_rtime_usec - (p1)->p_rtime_usec) == 0)
 
 #define ORDERKEY_STATE \
 	if ((result = sorted_state[(int)(p2)->p_stat] - \
@@ -835,4 +827,81 @@
 	half_total = total_change / 2;
 	for (i = 0; i < cnt; i++)
 		*out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
+}
+
+/* Explanation:
+   This is a rewrite of the version in utils.c to give greater precision.
+   We want to keep the output within 6 characters.
+   for values below 100 seconds nn.nn		(seconds and fractions thereof)
+   for values that exceed 100 seconds nn:nn	(minutes and seconds)
+   for values that exceed 100 minutes nnhnn	(hours and minutes)
+   for values that exceed 100 hours nndnn	(days and hours)
+   for values that exceed 100 days nnnnnd	(days)
+   for values that exceed 99999 days "???"
+ */
+
+struct time_info {
+	uint	lim;
+	uint	base;
+	uint	round;
+	char	*fmt;
+};
+
+static char *
+time_format(ulong units, ulong fraction)
+{
+	static char result[10];
+	ulong new;
+
+	static struct time_info time_info[] = {
+		{ 99, 60, 50, "%d.%.2d" },
+		{ 99, 60, 30, "%d:%.2d" },
+		{ 99, 24, 30, "%dh%.2d" },
+		{ 999, 1, 12, "%dd%.2d" },
+		{ 99999, 1, 0, "%dd" },
+		{  0,  0,  0, "???" },
+	};
+	struct time_info *ti;
+
+	/* We could output n.nnnnn for small values, but it isn't really
+	   needed, and stops this nice loop" */
+
+	/* Convert usecs to hundredths */
+	fraction = (fraction + 5000) / 10000;
+	if (fraction >= 100) {
+		fraction -= 100;
+		units++;
+	}
+
+	for (ti = time_info; ti->lim; ti++) {
+		if (units <= ti->lim)
+			break;
+		if (fraction > ti->round)
+			units++;
+		/* Divide by constant is lots faster than by variable,
+		   especially on systems without hw divide.
+		   (and without a rather foul optimisation). */
+		switch (ti->base) {
+		case 1:
+			new = units;
+			fraction = 0;
+			break;
+		case 60:
+			new = units / 60;
+			fraction = units - new * 60;
+			break;
+		case 24:
+			new = units / 24;
+			fraction = units - new * 24;
+			break;
+		default:
+			new = units / ti->base;
+			fraction = units - new * ti->base;
+			break;
+		}
+		units = new;
+	}
+
+	snprintf( result, sizeof result, ti->fmt, units, fraction );
+	return result;
 }
>Release-Note:
>Audit-Trail:
>Unformatted: