From owner-svn-src-head@FreeBSD.ORG Fri May 30 21:18:53 2014 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id BD2133A3; Fri, 30 May 2014 21:18:53 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id AA55F214C; Fri, 30 May 2014 21:18:53 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.8/8.14.8) with ESMTP id s4ULIrFQ084310; Fri, 30 May 2014 21:18:53 GMT (envelope-from jhb@svn.freebsd.org) Received: (from jhb@localhost) by svn.freebsd.org (8.14.8/8.14.8/Submit) id s4ULIrAa084309; Fri, 30 May 2014 21:18:53 GMT (envelope-from jhb@svn.freebsd.org) Message-Id: <201405302118.s4ULIrAa084309@svn.freebsd.org> From: John Baldwin Date: Fri, 30 May 2014 21:18:53 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r266906 - head/usr.bin/top X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 30 May 2014 21:18:53 -0000 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 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)