From 434f79cbfa00c7370724dc25091b5a42bdf8d9e1 Mon Sep 17 00:00:00 2001 From: Jeremie Koenig Date: Mon, 23 Aug 2010 19:04:48 +0000 Subject: Fix the global idle time * rootdir.c: Replace INIT_PID by KERNEL_PID, for boot time and idle time purposes. (get_idletime): New function, queries the kernel's idle thread. (rootdir_gc_uptime, rootdir_gc_stat): Use the new function and provide the real idle time. --- rootdir.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 13 deletions(-) diff --git a/rootdir.c b/rootdir.c index dcee9a5..728008e 100644 --- a/rootdir.c +++ b/rootdir.c @@ -16,15 +16,16 @@ using them would require locking and as a consequence it would be more complicated, not simpler. */ -#define INIT_PID 1 +#define KERNEL_PID 2 +/* We get the boot time by using that of the kernel process. */ static error_t get_boottime (struct ps_context *pc, struct timeval *tv) { struct proc_stat *ps; error_t err; - err = _proc_stat_create (INIT_PID, pc, &ps); + err = _proc_stat_create (KERNEL_PID, pc, &ps); if (err) return err; @@ -43,6 +44,63 @@ get_boottime (struct ps_context *pc, struct timeval *tv) return err; } +/* We get the idle time by querying the kernel's idle thread. */ +static error_t +get_idletime (struct ps_context *pc, struct timeval *tv) +{ + struct proc_stat *ps, *pst; + thread_basic_info_t tbi; + error_t err; + int i; + + err = _proc_stat_create (KERNEL_PID, pc, &ps); + if (err) + return err; + + pst = NULL, tbi = NULL; + + err = proc_stat_set_flags (ps, PSTAT_NUM_THREADS); + if (err || !(proc_stat_flags (ps) & PSTAT_NUM_THREADS)) + { + err = EIO; + goto out; + } + + /* Look for the idle thread */ + for (i=0; !tbi || !(tbi->flags & TH_FLAGS_IDLE); i++) + { + if (pst) + _proc_stat_free (pst); + + pst = NULL, tbi = NULL; + if (i >= proc_stat_num_threads (ps)) + { + err = ESRCH; + goto out; + } + + err = proc_stat_thread_create (ps, i, &pst); + if (err) + continue; + + err = proc_stat_set_flags (pst, PSTAT_THREAD_BASIC); + if (err || ! (proc_stat_flags (pst) & PSTAT_THREAD_BASIC)) + continue; + + tbi = proc_stat_thread_basic_info (pst); + } + + /* We found it! */ + tv->tv_sec = tbi->system_time.seconds; + tv->tv_usec = tbi->system_time.microseconds; + err = 0; + +out: + if (pst) _proc_stat_free (pst); + _proc_stat_free (ps); + return err; +} + static error_t rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) { @@ -65,8 +123,8 @@ rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) static error_t rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) { - struct timeval time, boottime; - double up_secs; + struct timeval time, boottime, idletime; + double up_secs, idle_secs; error_t err; err = gettimeofday (&time, NULL); @@ -77,15 +135,20 @@ rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) if (err) return err; + err = get_idletime (hook, &idletime); + if (err) + return err; + timersub (&time, &boottime, &time); up_secs = time.tv_sec + time.tv_usec / 1000000.; + idle_secs = idletime.tv_sec + idletime.tv_usec / 1000000.; /* The second field is the total idle time. As far as I know we don't keep track of it. However, procps uses it to compute "USER_HZ", and proc(5) specifies that it should be equal to USER_HZ times the idle value in ticks from /proc/stat. So we assume a completely idle system both here and there to make that work. */ - *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, up_secs); + *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, idle_secs); return 0; } @@ -93,9 +156,9 @@ rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) static error_t rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) { - struct timeval boottime, time; + struct timeval boottime, time, idletime; struct vm_statistics vmstats; - unsigned long up_ticks; + unsigned long up_ticks, idle_ticks; error_t err; err = gettimeofday (&time, NULL); @@ -106,23 +169,26 @@ rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) if (err) return err; + err = get_idletime (hook, &idletime); + if (err) + return err; + err = vm_statistics (mach_task_self (), &vmstats); if (err) return EIO; timersub (&time, &boottime, &time); up_ticks = opt_clk_tck * (time.tv_sec + time.tv_usec / 1000000.); + idle_ticks = opt_clk_tck * (idletime.tv_sec + idletime.tv_usec / 1000000.); *contents_len = asprintf (contents, - /* Does Mach keeps track of any of this? */ - "cpu 0 0 0 %lu 0 0 0 0 0\n" - "cpu0 0 0 0 %lu 0 0 0 0 0\n" + "cpu %lu 0 0 %lu 0 0 0 0 0\n" + "cpu0 %lu 0 0 %lu 0 0 0 0 0\n" "intr 0\n" - /* This we know. */ "page %d %d\n" "btime %lu\n", - up_ticks, - up_ticks, + up_ticks - idle_ticks, idle_ticks, + up_ticks - idle_ticks, idle_ticks, vmstats.pageins, vmstats.pageouts, boottime.tv_sec); -- cgit v1.2.3