]> Zhao Yanbai Git Server - minix.git/commitdiff
Add 'getidle' CPU utilization measurement infrastructure
authorDavid van Moolenbroek <david@minix3.org>
Wed, 2 Dec 2009 11:52:26 +0000 (11:52 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Wed, 2 Dec 2009 11:52:26 +0000 (11:52 +0000)
22 files changed:
include/minix/com.h
include/minix/sysinfo.h
include/minix/syslib.h
include/minix/sysutil.h
kernel/arch/i386/exception.c
kernel/arch/i386/memory.c
kernel/arch/i386/proto.h
kernel/arch/i386/system.c
kernel/clock.c
kernel/const.h
kernel/glo.h
kernel/interrupt.c
kernel/kernel.h
kernel/main.c
kernel/proc.c
kernel/proto.h
kernel/system/do_getinfo.c
kernel/utility.c
lib/sysutil/Makefile.in
lib/sysutil/getidle.c [new file with mode: 0644]
lib/sysutil/profile.c
servers/pm/misc.c

index 76918419474a0857752d2c3661f7f1b56eb00b6d..0b95b687aac6412ff044fd6466b591a4d7f65db4 100644 (file)
 #   define GET_HZ        18    /* get HZ value */
 #   define GET_WHOAMI    19    /* get own name and endpoint */
 #   define GET_RANDOMNESS_BIN 20 /* get one randomness bin */
+#   define GET_IDLETSC   21    /* get cumulative idle time stamp counter */
 #define I_ENDPT      m7_i4     /* calling process */
 #define I_VAL_PTR      m7_p1   /* virtual address at caller */ 
 #define I_VAL_LEN      m7_i1   /* max length of value */
index 072c4eb4e9e64e0fc38c2b207e04602edfb2fac8..c0d7ed3a0936ac6f37ed6e2c896c18c2dbc6af7a 100644 (file)
@@ -11,6 +11,7 @@ _PROTOTYPE( ssize_t getsysinfo_up, (endpoint_t who, int what, size_t size,
 
 #define SIU_LOADINFO   1       /* retrieve load info data */
 #define SIU_SYSTEMHZ   2       /* retrieve system clock frequency */
+#define SIU_IDLETSC    3       /* retrieve cumulative idle timestamp count */
 
 /* Exported system parameters. */
 
index 93aeaf3c03c9715708edf557fdfb043e56ffcaa2..02c03bed52c2515682f19867d9356c6549f78c90 100644 (file)
@@ -179,6 +179,7 @@ _PROTOTYPE(int sys_segctl, (int *index, u16_t *seg, vir_bytes *off,
 #define sys_getschedinfo(v1,v2)        sys_getinfo(GET_SCHEDINFO, v1,0, v2,0)
 #define sys_getlocktimings(dst)        sys_getinfo(GET_LOCKTIMING, dst, 0,0,0)
 #define sys_getprivid(nr)      sys_getinfo(GET_PRIVID, 0, 0,0, nr)
+#define sys_getidletsc(dst)    sys_getinfo(GET_IDLETSC, dst, 0,0,0)
 _PROTOTYPE(int sys_getinfo, (int request, void *val_ptr, int val_len,
                                 void *val_ptr2, int val_len2)          );
 _PROTOTYPE(int sys_whoami, (endpoint_t *ep, char *name, int namelen));
@@ -242,9 +243,5 @@ _PROTOTYPE( int sys_cprof, (int action, int size, endpoint_t endpt,
                                        void *ctl_ptr, void *mem_ptr)   );
 _PROTOTYPE( int sys_profbuf, (void *ctl_ptr, void *mem_ptr)            );
 
-/* read_tsc() and friends. */
-_PROTOTYPE( void read_tsc_64, (u64_t *t)                               );
-_PROTOTYPE( void read_tsc, (u32_t *hi, u32_t *lo)                      );
-
 #endif /* _SYSLIB_H */
 
index 7318bd94dc9d76b562d068008f0bb8fd0cb9db6a..487c6b864921ad28f1fda6ce44be2f29ac3f3eac 100644 (file)
@@ -51,6 +51,7 @@ _PROTOTYPE( int getuptime2, (clock_t *ticks, time_t *boottime));
 _PROTOTYPE( int tickdelay, (clock_t ticks));
 _PROTOTYPE( int micro_delay_calibrate, (void));
 _PROTOTYPE( u32_t sys_hz, (void));
+_PROTOTYPE( double getidle, (void));
 _PROTOTYPE( void util_stacktrace, (void));
 _PROTOTYPE( void util_nstrcat, (char *str, unsigned long n) );
 _PROTOTYPE( void util_stacktrace_strcat, (char *));
@@ -81,5 +82,9 @@ struct util_timingdata {
 
 typedef struct util_timingdata util_timingdata_t;
 
+/* read_tsc() and friends. */
+_PROTOTYPE( void read_tsc_64, (u64_t *t)                               );
+_PROTOTYPE( void read_tsc, (u32_t *hi, u32_t *lo)                      );
+
 #endif /* _MINIX_SYSUTIL_H */
 
index 7fc6e2cb741a0e1a569ea8e6948a6b1be8267f09..98d244005426af71f78ba24276d2e9eaa8491d00 100644 (file)
@@ -8,7 +8,6 @@
 #include <signal.h>
 #include <stdio.h>
 #include <string.h>
-#include <minix/sysutil.h>
 #include "../../proc.h"
 #include "../../proto.h"
 #include "../../vm.h"
index 312d6abc9566a4bf322845d3552595a803daa812..53c2ce4c72d95fa65d169b9294dec59533ceb2d1 100644 (file)
@@ -6,7 +6,6 @@
 
 #include <minix/type.h>
 #include <minix/syslib.h>
-#include <minix/sysutil.h>
 #include <minix/cpufeature.h>
 #include <string.h>
 
index 08828a337b3f0e24d2e11a6a6c71144ce8f34e30..4a0c429782b42f3e41f6e0151777b304737b930b 100644 (file)
@@ -119,7 +119,6 @@ struct tss_s {
 
 EXTERN struct tss_s tss;
 
-_PROTOTYPE( void prot_init, (void)                                             );
 _PROTOTYPE( void idt_init, (void)                                      );
 _PROTOTYPE( void init_codeseg, (struct segdesc_s *segdp, phys_bytes base,
                 vir_bytes size, int privilege)                          );
index 426c53dc6ea8258efd931c15a135d5ef636ce501..f929604f2cec1aae7b990f1b4143cf34c4d64a2c 100644 (file)
@@ -9,7 +9,6 @@
 #include <ibm/bios.h>
 #include <minix/portio.h>
 #include <minix/u64.h>
-#include <minix/sysutil.h>
 #include <a.out.h>
 
 #include "proto.h"
index db4be94b38c4f816e5bd275226882ad8498ee035..488b443b3e70ae6cd41aa2a2055c544232f35b39 100644 (file)
@@ -118,6 +118,8 @@ PUBLIC int bsp_timer_int_handler(void)
 {
        unsigned ticks;
 
+       IDLE_STOP;
+
        if(minix_panicing)
                return 0;
 
@@ -228,6 +230,8 @@ PUBLIC int ap_timer_int_handler(void)
        int expired = 0;
        struct proc * p, * billp;
 
+       IDLE_STOP;
+
        /* Update user and system accounting times. Charge the current process
         * for user time. If the current process is not billable, that is, if a
         * non-user process is running, charge the billable process for system
index c3f1ea00831692154dcf1467c3e7f50cad0ebf89..9f41136f10d8a2d6b30b24e3da0dfad9bf119670 100644 (file)
 #define lock      reallock
 #define unlock    realunlock
 
+#ifdef CONFIG_IDLE_TSC
+#define IDLE_STOP if(idle_active) { read_tsc_64(&idle_stop); idle_active = 0; }
+#else
+#define IDLE_STOP
+#endif
+
 /* args to intr_init() */
 #define INTS_ORIG      0       /* restore interrupts */
 #define INTS_MINIX     1       /* initialize interrupts for minix */
index d639f7f793585266b74a2b6cadbe8a6a048436e4..41a2560abdc36c94061e47b29da6e50515390da0 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef GLO_H
 #define GLO_H
 
-#include <minix/sysutil.h>
-
 /* Global variables used in the kernel. This file contains the declarations;
  * storage space for the variables is allocated in table.c, because EXTERN is
  * defined as extern unless the _TABLE definition is seen. We rely on the 
@@ -66,6 +64,12 @@ EXTERN int verboseflags;
 EXTERN int config_no_apic; /* optionaly turn off apic */
 #endif
 
+#ifdef CONFIG_IDLE_TSC
+EXTERN u64_t idle_tsc;
+EXTERN u64_t idle_stop;
+EXTERN int idle_active;
+#endif
+
 /* VM */
 EXTERN int vm_running;
 EXTERN int catch_pagefaults;
index c4376482089ee3907c5a95da3392fd85192ad21b..536f49a013eb97c1404f3d1b57768fa3f13c2ffc 100644 (file)
@@ -112,6 +112,8 @@ PUBLIC void irq_handle(int irq)
 {
   irq_hook_t * hook;
 
+  IDLE_STOP;
+
   /* here we need not to get this IRQ until all the handlers had a say */
   hw_intr_mask(irq);
   hook = irq_handlers[irq];
index 4090733cb58341c16e95ef3f14e2ba0283045ee9..f3971a4b917a8a9267e24048ccc40d86d74e2ed9 100644 (file)
@@ -5,6 +5,8 @@
 #define CONFIG_APIC
 /* boot verbose */
 #define CONFIG_BOOT_VERBOSE
+/* measure cumulative idle timestamp counter ticks */
+#undef CONFIG_IDLE_TSC
 
 /* This is the master header for the kernel.  It includes some other files
  * and defines the principal constants.
@@ -20,6 +22,7 @@
 #include <minix/const.h>       /* MINIX specific constants */
 #include <minix/type.h>                /* MINIX specific types, e.g. message */
 #include <minix/ipc.h>         /* MINIX run-time system */
+#include <minix/sysutil.h>     /* MINIX utility library functions */
 #include <timers.h>            /* watchdog timer management */
 #include <errno.h>             /* return codes and error numbers */
 
index 777e12d4dfb753c5587b7e95515e1851b6a45ab0..30c040585e4d76cf9bee0c4373bd020cb358414b 100644 (file)
@@ -16,6 +16,7 @@
 #include <minix/callnr.h>
 #include <minix/com.h>
 #include <minix/endpoint.h>
+#include <minix/u64.h>
 #include "proc.h"
 #include "debug.h"
 #include "clock.h"
@@ -189,6 +190,10 @@ PUBLIC void main()
 #endif /* SPROFILE */
   cprof_procs_no = 0;  /* init nr of hash table slots used */
 
+#ifdef CONFIG_IDLE_TSC
+  idle_tsc = cvu64(0);
+#endif
+
   vm_running = 0;
   krandom.random_sources = RANDOM_SOURCES;
   krandom.random_elements = RANDOM_ELEMENTS;
index 2f1f9d15c9129e33e7661efe62a2a737beb17524..c39470937a74053d078abf4cf9c536ac4e51ae03 100644 (file)
@@ -40,6 +40,7 @@
 #include <signal.h>
 #include <minix/portio.h>
 #include <minix/u64.h>
+#include <minix/syslib.h>
 
 #include "debug.h"
 #include "kernel.h"
@@ -50,6 +51,7 @@
  * other parts of the kernel through lock_...(). The lock temporarily disables 
  * interrupts to prevent race conditions. 
  */
+FORWARD _PROTOTYPE( void idle, (void));
 FORWARD _PROTOTYPE( int mini_send, (struct proc *caller_ptr, int dst_e,
                message *m_ptr, int flags));
 FORWARD _PROTOTYPE( int mini_receive, (struct proc *caller_ptr, int src,
@@ -123,6 +125,35 @@ PRIVATE int QueueMess(endpoint_t ep, vir_bytes msg_lin, struct proc *dst)
        NOREC_RETURN(queuemess, OK);
 }
 
+/*===========================================================================*
+ *                             idle                                         * 
+ *===========================================================================*/
+PRIVATE void idle()
+{
+       /* This function is called whenever there is no work to do.
+        * Halt the CPU, and measure how many timestamp counter ticks are
+        * spent not doing anything. This allows test setups to measure
+        * the CPU utiliziation of certain workloads with high precision.
+        */
+#ifdef CONFIG_IDLE_TSC
+       u64_t idle_start;
+
+       read_tsc_64(&idle_start);
+       idle_active = 1;
+#endif
+
+       halt_cpu();
+
+#ifdef CONFIG_IDLE_TSC
+       if (idle_active) {
+               IDLE_STOP;
+               printf("Kernel: idle active after resuming CPU\n");
+       }
+
+       idle_tsc = add64(idle_tsc, sub64(idle_stop, idle_start));
+#endif
+}
+
 /*===========================================================================*
  *                             schedcheck                                   * 
  *===========================================================================*/
@@ -166,7 +197,7 @@ not_runnable_pick_new:
                proc_ptr = proc_addr(IDLE);
                if (priv(proc_ptr)->s_flags & BILLABLE)
                        bill_ptr = proc_ptr;
-               halt_cpu();
+               idle();
        }
 
 check_misc_flags:
index cc5241aea8ef765176462af580a93809b0e217f7..3c8fb3c7fed61c92c323a8f6484916f7a4e51e98 100644 (file)
@@ -19,6 +19,7 @@ _PROTOTYPE( void reset_timer, (struct timer *tp)                      );
 _PROTOTYPE( void ser_dump_proc, (void)                                 );
 
 /* main.c */
+_PROTOTYPE( void main, (void)                                          );
 _PROTOTYPE( void prepare_shutdown, (int how)                           );
 _PROTOTYPE( void minix_shutdown, (struct timer *tp)                    );
 
@@ -102,6 +103,7 @@ _PROTOTYPE( void stop_profile_clock, (void)                         );
 #endif
 
 /* functions defined in architecture-dependent files. */
+_PROTOTYPE( void prot_init, (void)                                             );
 _PROTOTYPE( phys_bytes phys_copy, (phys_bytes source, phys_bytes dest,
                 phys_bytes count)                                       );
 _PROTOTYPE( void phys_copy_fault, (void));
index ecafdc2cb765e9a15f44cdb4c40e3d2982f21c5d..22cbcbe5c75d14934a3bb7e449fbc18597c2e4a0 100644 (file)
@@ -141,18 +141,26 @@ register message *m_ptr;  /* pointer to request message */
     break;
     }
 #endif
-
     case GET_IRQACTIDS: {
         length = sizeof(irq_actids);
         src_vir = (vir_bytes) irq_actids;
         break;
     }
-
-    case GET_PRIVID:
+    case GET_PRIVID: {
        if (!isokendpt(m_ptr->I_VAL_LEN2_E, &proc_nr)) 
                return EINVAL;
        return proc_addr(proc_nr)->p_priv->s_id;
-
+    }
+    case GET_IDLETSC: {
+#ifdef CONFIG_IDLE_TSC
+        length = sizeof(idle_tsc);
+        src_vir = (vir_bytes) &idle_tsc;
+        break;
+#else
+        kprintf("do_getinfo: kernel not compiled with CONFIG_IDLE_TSC\n");
+        return(EINVAL);
+#endif
+    }
     default:
        kprintf("do_getinfo: invalid request %d\n", m_ptr->I_REQUEST);
         return(EINVAL);
index fa31cf3fe779e3d004585adc9a9dc39760d7a3a9..bc70c141945baeb3521c1fdcd099c863f74a2feb 100644 (file)
@@ -11,7 +11,6 @@
 #include <signal.h>
 #include <string.h>
 
-#include <minix/sysutil.h>
 #include <minix/sys_config.h>
 
 /*===========================================================================*
index cd9f53252da03f66cab68b3633fb556fccae2b71..c5baf71f6264df9d3289da9985157057c615b74f 100644 (file)
@@ -10,6 +10,7 @@ libsys_FILES=" \
        kputc.c \
        tickdelay.c \
        get_randomness.c \
+       getidle.c \
        getuptime.c \
        getuptime2.c \
        env_get_prm.c \
diff --git a/lib/sysutil/getidle.c b/lib/sysutil/getidle.c
new file mode 100644 (file)
index 0000000..fdd6622
--- /dev/null
@@ -0,0 +1,92 @@
+/* getidle.c - by David van Moolenbroek <dcvmoole@cs.vu.nl> */
+
+/* Usage:
+ *
+ *   double idleperc;
+ *   getidle();
+ *   ...
+ *   idleperc = getidle();
+ *   printf("CPU usage: %lg%%\n", 100.0 - idleperc);
+ *
+ * This routine goes through PM to get the idle time, rather than making the
+ * sys_getinfo() call to the kernel directly. This means that it can be used
+ * by non-system processes as well, but it will incur some extra overhead in
+ * the system case. The overhead does not end up being measured, because the
+ * system is clearly not idle while the system calls are being made. In any
+ * case, for this reason, only one getidle() run is allowed at a time.
+ *
+ * Note that the kernel has to be compiled with CONFIG_IDLE_TSC support.
+ */
+
+#define _MINIX 1
+#define _SYSTEM 1
+#include <minix/sysinfo.h>
+#include <minix/u64.h>
+#include <minix/sysutil.h>
+
+static u64_t start, idle;
+static int running = 0;
+
+static double make_double(u64_t d)
+{
+/* Convert a 64-bit fixed point value into a double.
+ * This whole thing should be replaced by something better eventually.
+ */
+  double value;
+  int i;
+
+  value = (double) ex64hi(d);
+  for (i = 0; i < sizeof(unsigned long); i += 2)
+       value *= 65536.0;
+
+  value += (double) ex64lo(d);
+
+  return value;
+}
+
+double getidle(void)
+{
+  u64_t stop, idle2;
+  u64_t idelta, tdelta;
+  double ifp, tfp, rfp;
+  int r;
+
+  if (!running) {
+       r = getsysinfo_up(PM_PROC_NR, SIU_IDLETSC, sizeof(idle), &idle);
+       if (r != sizeof(idle))
+               return -1.0;
+
+       running = 1;
+
+       read_tsc_64(&start);
+
+       return 0.0;
+  }
+  else {
+       read_tsc_64(&stop);
+
+       running = 0;
+
+       r = getsysinfo_up(PM_PROC_NR, SIU_IDLETSC, sizeof(idle2), &idle2);
+       if (r != sizeof(idle2))
+               return -1.0;
+
+       idelta = sub64(idle2, idle);
+       tdelta = sub64(stop, start);
+
+       if (cmp64(idelta, tdelta) >= 0)
+               return 100.0;
+
+       ifp = make_double(idelta);
+       tfp = make_double(tdelta);
+
+       rfp = ifp / tfp * 100.0;
+
+       if (rfp < 0.0) rfp = 0.0;
+       else if (rfp > 100.0) rfp = 100.0;
+
+       return rfp;
+  }
+
+  running = !running;
+}
index b64c038331907895f902a8fe379436ced5ca2c19..e8523080a3e84c1b5d473960a035618449a8ae4b 100644 (file)
@@ -16,7 +16,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <minix/profile.h>
-#include <minix/syslib.h>
+#include <minix/sysutil.h>
 #include <minix/u64.h>
 
 PRIVATE char cpath[CPROF_CPATH_MAX_LEN];       /* current call path string */
@@ -61,7 +61,7 @@ char *name;
   if (cprof_locked) return; else cprof_locked = 1;
 
   /* Read CPU cycle count into local variable. */
-  read_tsc(&start.hi, &start.lo);
+  read_tsc_64(&start);
 
   /* Run init code once after system boot. */
   if (init == 0) {
@@ -105,8 +105,7 @@ char *name;
   }
 
   /* Save initial cycle count on stack. */
-  cprof_stk[cprof_stk_top].start_1.hi = start.hi;
-  cprof_stk[cprof_stk_top].start_1.lo = start.lo;
+  cprof_stk[cprof_stk_top].start_1 = start;
 
   /* Check available call path len. */
   if (cpath_len + strlen(name) + 1 > CPROF_CPATH_MAX_LEN) {
@@ -167,8 +166,7 @@ char *name;
   cprof_stk[cprof_stk_top].slot = cprof_slot;
 
   /* Again save CPU cycle count on stack. */
-  read_tsc(&cprof_stk[cprof_stk_top].start_2.hi,
-               &cprof_stk[cprof_stk_top].start_2.lo);
+  read_tsc_64(&cprof_stk[cprof_stk_top].start_2);
   cprof_locked = 0;
 }
 
@@ -201,8 +199,7 @@ char *name;
                sub64(spent, cprof_stk[cprof_stk_top].spent_deeper));
 
   /* Clear spent_deeper for call level we're leaving. */
-  cprof_stk[cprof_stk_top].spent_deeper.lo = 0;
-  cprof_stk[cprof_stk_top].spent_deeper.hi = 0;
+  cprof_stk[cprof_stk_top].spent_deeper = cvu64(0);
 
   /* Adjust call path string and stack. */
   cpath_len = cprof_stk[cprof_stk_top].cpath_len;
@@ -246,12 +243,9 @@ PRIVATE void cprof_init() {
   for (i=0; i<CPROF_STACK_SIZE; i++) {
        cprof_stk[i].cpath_len = 0;
        cprof_stk[i].slot = 0;
-       cprof_stk[i].start_1.lo = 0;
-       cprof_stk[i].start_1.hi = 0;
-       cprof_stk[i].start_2.lo = 0;
-       cprof_stk[i].start_2.hi = 0;
-       cprof_stk[i].spent_deeper.lo = 0;
-       cprof_stk[i].spent_deeper.hi = 0;
+       cprof_stk[i].start_1 = cvu64(0);
+       cprof_stk[i].start_2 = cvu64(0);
+       cprof_stk[i].spent_deeper = cvu64(0);
   }
 }
 
index 27e0aed917b538d38629a65af7a550fe1357cac0..72286848660484a082d5332bcc4ac5704a16de3d 100644 (file)
@@ -274,11 +274,13 @@ PUBLIC int do_getsysinfo_up()
   vir_bytes src_addr, dst_addr;
   struct loadinfo loadinfo;
   size_t len, real_len;
+  u64_t idle_tsc;
   int s;
 
   switch(m_in.SIU_WHAT) {
   case SIU_LOADINFO:                   /* loadinfo is obtained via PM */
-        sys_getloadinfo(&loadinfo);
+        if ((s = sys_getloadinfo(&loadinfo)) != OK)
+               return s;
         src_addr = (vir_bytes) &loadinfo;
         real_len = sizeof(struct loadinfo);
         break;
@@ -286,6 +288,12 @@ PUBLIC int do_getsysinfo_up()
         src_addr = (vir_bytes) &system_hz;
         real_len = sizeof(system_hz);
        break;
+  case SIU_IDLETSC:
+       if ((s = sys_getidletsc(&idle_tsc)) != OK)
+               return s;
+       src_addr = (vir_bytes) &idle_tsc;
+       real_len = sizeof(idle_tsc);
+       break;
   default:
        return(EINVAL);
   }