svn commit: r266906 - head/usr.bin/top

John Baldwin jhb at FreeBSD.org
Fri May 30 21:18:53 UTC 2014


Author: jhb
Date: Fri May 30 21:18:53 2014
New Revision: 266906
URL: http://svnweb.freebsd.org/changeset/base/266906

Log:
  Rework the notion of CPU used in top.  In particular, for subsequent
  displays after a pause, use the difference in runtime divided by the
  length of the pause as the percentage of CPU used instead of the value
  calculated by the kernel.  In addition, when determing if a process or
  thread is idle or not, treat any process or thread that has used any
  runtime or performed any context switches during the interval as busy.
  
  Note that the percent CPU is calculated as a double and stored in an
  array to avoid recalculating the value multiple times in the comparison
  method used to sort processes in the CPU display.
  
  Tested by:	Jamie Landeg-Jones <jamie at dyslexicfish.net>
  Reviewed by:	emaste (earlier version)
  MFC after:	1 week

Modified:
  head/usr.bin/top/machine.c

Modified: head/usr.bin/top/machine.c
==============================================================================
--- head/usr.bin/top/machine.c	Fri May 30 20:58:32 2014	(r266905)
+++ head/usr.bin/top/machine.c	Fri May 30 21:18:53 2014	(r266906)
@@ -96,6 +96,7 @@ struct handle {
 #define RUTOT(pp) \
 	(RU(pp)->ru_inblock + RU(pp)->ru_oublock + RU(pp)->ru_majflt)
 
+#define	PCTCPU(pp) (pcpu[pp - pbase])
 
 /* definitions for indices in the nlist array */
 
@@ -203,7 +204,14 @@ static struct kinfo_proc *previous_procs
 static struct kinfo_proc **previous_pref;
 static int previous_proc_count = 0;
 static int previous_proc_count_max = 0;
-static int arc_enabled;
+static int previous_thread;
+
+/* data used for recalculating pctcpu */
+static double *pcpu;
+static struct timespec proc_uptime;
+static struct timeval proc_wall_time;
+static struct timeval previous_wall_time;
+static uint64_t previous_interval = 0;
 
 /* total number of io operations */
 static long total_inblock;
@@ -212,6 +220,7 @@ static long total_majflt;
 
 /* these are for getting the memory statistics */
 
+static int arc_enabled;
 static int pageshift;		/* log base 2 of the pagesize */
 
 /* define pagetok in terms of pageshift */
@@ -329,6 +338,7 @@ machine_init(struct statics *statics, ch
 
 	pbase = NULL;
 	pref = NULL;
+	pcpu = NULL;
 	nproc = 0;
 	onproc = -1;
 
@@ -650,6 +660,52 @@ get_io_stats(struct kinfo_proc *pp, long
 }
 
 /*
+ * If there was a previous update, use the delta in ki_runtime over
+ * the previous interval to calculate pctcpu.  Otherwise, fall back
+ * to using the kernel's ki_pctcpu.
+ */
+static double
+proc_calc_pctcpu(struct kinfo_proc *pp)
+{
+	const struct kinfo_proc *oldp;
+
+	if (previous_interval != 0) {
+		oldp = get_old_proc(pp);
+		if (oldp != NULL)
+			return ((double)(pp->ki_runtime - oldp->ki_runtime)
+			    / previous_interval);
+
+		/*
+		 * If this process/thread was created during the previous
+		 * interval, charge it's total runtime to the previous
+		 * interval.
+		 */
+		else if (pp->ki_start.tv_sec > previous_wall_time.tv_sec ||
+		    (pp->ki_start.tv_sec == previous_wall_time.tv_sec &&
+		    pp->ki_start.tv_usec >= previous_wall_time.tv_usec))
+			return ((double)pp->ki_runtime / previous_interval);
+	}
+	return (pctdouble(pp->ki_pctcpu));
+}
+
+/*
+ * Return true if this process has used any CPU time since the
+ * previous update.
+ */
+static int
+proc_used_cpu(struct kinfo_proc *pp)
+{
+	const struct kinfo_proc *oldp;
+
+	oldp = get_old_proc(pp);
+	if (oldp == NULL)
+		return (PCTCPU(pp) != 0);
+	return (pp->ki_runtime != oldp->ki_runtime ||
+	    RU(pp)->ru_nvcsw != RU(oldp)->ru_nvcsw ||
+	    RU(pp)->ru_nivcsw != RU(oldp)->ru_nivcsw);
+}
+
+/*
  * Return the total number of block in/out and faults by a process.
  */
 long
@@ -670,9 +726,11 @@ get_process_info(struct system_info *si,
 	int total_procs;
 	long p_io;
 	long p_inblock, p_oublock, p_majflt, p_vcsw, p_ivcsw;
+	long nsec;
 	int active_procs;
 	struct kinfo_proc **prefp;
 	struct kinfo_proc *pp;
+	struct timespec previous_proc_uptime;
 
 	/* these are copied out of sel for speed */
 	int show_idle;
@@ -684,6 +742,13 @@ get_process_info(struct system_info *si,
 	int show_kidle;
 
 	/*
+	 * If thread state was toggled, don't cache the previous processes.
+	 */
+	if (previous_thread != sel->thread)
+		nproc = 0;
+	previous_thread = sel->thread;
+
+	/*
 	 * Save the previous process info.
 	 */
 	if (previous_proc_count_max < nproc) {
@@ -705,12 +770,32 @@ get_process_info(struct system_info *si,
 		    ps.thread ? compare_tid : compare_pid);
 	}
 	previous_proc_count = nproc;
+	previous_proc_uptime = proc_uptime;
+	previous_wall_time = proc_wall_time;
+	previous_interval = 0;
 
 	pbase = kvm_getprocs(kd, sel->thread ? KERN_PROC_ALL : KERN_PROC_PROC,
 	    0, &nproc);
-	if (nproc > onproc)
-		pref = realloc(pref, sizeof(*pref) * (onproc = nproc));
-	if (pref == NULL || pbase == NULL) {
+	(void)gettimeofday(&proc_wall_time, NULL);
+	if (clock_gettime(CLOCK_UPTIME, &proc_uptime) != 0)
+		memset(&proc_uptime, 0, sizeof(proc_uptime));
+	else if (previous_proc_uptime.tv_sec != 0 &&
+	    previous_proc_uptime.tv_nsec != 0) {
+		previous_interval = (proc_uptime.tv_sec -
+		    previous_proc_uptime.tv_sec) * 1000000;
+		nsec = proc_uptime.tv_nsec - previous_proc_uptime.tv_nsec;
+		if (nsec < 0) {
+			previous_interval -= 1000000;
+			nsec += 1000000000;
+		}
+		previous_interval += nsec / 1000;
+	}
+	if (nproc > onproc) {
+		pref = realloc(pref, sizeof(*pref) * nproc);
+		pcpu = realloc(pcpu, sizeof(*pcpu) * nproc);
+		onproc = nproc;
+	}
+	if (pref == NULL || pbase == NULL || pcpu == NULL) {
 		(void) fprintf(stderr, "top: Out of memory.\n");
 		quit(23);
 	}
@@ -763,9 +848,10 @@ get_process_info(struct system_info *si,
 		if (!show_kidle && pp->ki_tdflags & TDF_IDLETD)
 			/* skip kernel idle process */
 			continue;
-		    
+
+		PCTCPU(pp) = proc_calc_pctcpu(pp);
 		if (displaymode == DISP_CPU && !show_idle &&
-		    (pp->ki_pctcpu == 0 ||
+		    (!proc_used_cpu(pp) ||
 		     pp->ki_stat == SSTOP || pp->ki_stat == SIDL))
 			/* skip idle or non-running processes */
 			continue;
@@ -848,7 +934,7 @@ format_next_process(caddr_t handle, char
 	cputime = (pp->ki_runtime + 500000) / 1000000;
 
 	/* calculate the base for cpu percentages */
-	pct = pctdouble(pp->ki_pctcpu);
+	pct = PCTCPU(pp);
 
 	/* generate "STATE" field */
 	switch (state = pp->ki_stat) {
@@ -1169,14 +1255,12 @@ static int sorted_state[] = {
 
 
 #define ORDERKEY_PCTCPU(a, b) do { \
-	long diff; \
+	double diff; \
 	if (ps.wcpu) \
-		diff = floor(1.0E6 * weighted_cpu(pctdouble((b)->ki_pctcpu), \
-		    (b))) - \
-		    floor(1.0E6 * weighted_cpu(pctdouble((a)->ki_pctcpu), \
-		    (a))); \
+		diff = weighted_cpu(PCTCPU((b)), (b)) - \
+		    weighted_cpu(PCTCPU((a)), (a)); \
 	else \
-		diff = (long)(b)->ki_pctcpu - (long)(a)->ki_pctcpu; \
+		diff = PCTCPU((b)) - PCTCPU((a)); \
 	if (diff != 0) \
 		return (diff > 0 ? 1 : -1); \
 } while (0)


More information about the svn-src-all mailing list