Add 'getidle' CPU utilization measurement infrastructure

This commit is contained in:
David van Moolenbroek 2009-12-02 11:52:26 +00:00
parent be2087ecf9
commit fce9fd4b4e
22 changed files with 190 additions and 31 deletions

View file

@ -482,6 +482,7 @@
# define GET_HZ 18 /* get HZ value */ # define GET_HZ 18 /* get HZ value */
# define GET_WHOAMI 19 /* get own name and endpoint */ # define GET_WHOAMI 19 /* get own name and endpoint */
# define GET_RANDOMNESS_BIN 20 /* get one randomness bin */ # 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_ENDPT m7_i4 /* calling process */
#define I_VAL_PTR m7_p1 /* virtual address at caller */ #define I_VAL_PTR m7_p1 /* virtual address at caller */
#define I_VAL_LEN m7_i1 /* max length of value */ #define I_VAL_LEN m7_i1 /* max length of value */

View 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_LOADINFO 1 /* retrieve load info data */
#define SIU_SYSTEMHZ 2 /* retrieve system clock frequency */ #define SIU_SYSTEMHZ 2 /* retrieve system clock frequency */
#define SIU_IDLETSC 3 /* retrieve cumulative idle timestamp count */
/* Exported system parameters. */ /* Exported system parameters. */

View 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_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_getlocktimings(dst) sys_getinfo(GET_LOCKTIMING, dst, 0,0,0)
#define sys_getprivid(nr) sys_getinfo(GET_PRIVID, 0, 0,0, nr) #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, _PROTOTYPE(int sys_getinfo, (int request, void *val_ptr, int val_len,
void *val_ptr2, int val_len2) ); void *val_ptr2, int val_len2) );
_PROTOTYPE(int sys_whoami, (endpoint_t *ep, char *name, int namelen)); _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) ); void *ctl_ptr, void *mem_ptr) );
_PROTOTYPE( int sys_profbuf, (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 */ #endif /* _SYSLIB_H */

View file

@ -51,6 +51,7 @@ _PROTOTYPE( int getuptime2, (clock_t *ticks, time_t *boottime));
_PROTOTYPE( int tickdelay, (clock_t ticks)); _PROTOTYPE( int tickdelay, (clock_t ticks));
_PROTOTYPE( int micro_delay_calibrate, (void)); _PROTOTYPE( int micro_delay_calibrate, (void));
_PROTOTYPE( u32_t sys_hz, (void)); _PROTOTYPE( u32_t sys_hz, (void));
_PROTOTYPE( double getidle, (void));
_PROTOTYPE( void util_stacktrace, (void)); _PROTOTYPE( void util_stacktrace, (void));
_PROTOTYPE( void util_nstrcat, (char *str, unsigned long n) ); _PROTOTYPE( void util_nstrcat, (char *str, unsigned long n) );
_PROTOTYPE( void util_stacktrace_strcat, (char *)); _PROTOTYPE( void util_stacktrace_strcat, (char *));
@ -81,5 +82,9 @@ struct util_timingdata {
typedef struct util_timingdata util_timingdata_t; 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 */ #endif /* _MINIX_SYSUTIL_H */

View file

@ -8,7 +8,6 @@
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <minix/sysutil.h>
#include "../../proc.h" #include "../../proc.h"
#include "../../proto.h" #include "../../proto.h"
#include "../../vm.h" #include "../../vm.h"

View file

@ -6,7 +6,6 @@
#include <minix/type.h> #include <minix/type.h>
#include <minix/syslib.h> #include <minix/syslib.h>
#include <minix/sysutil.h>
#include <minix/cpufeature.h> #include <minix/cpufeature.h>
#include <string.h> #include <string.h>

View file

@ -119,7 +119,6 @@ struct tss_s {
EXTERN struct tss_s tss; EXTERN struct tss_s tss;
_PROTOTYPE( void prot_init, (void) );
_PROTOTYPE( void idt_init, (void) ); _PROTOTYPE( void idt_init, (void) );
_PROTOTYPE( void init_codeseg, (struct segdesc_s *segdp, phys_bytes base, _PROTOTYPE( void init_codeseg, (struct segdesc_s *segdp, phys_bytes base,
vir_bytes size, int privilege) ); vir_bytes size, int privilege) );

View file

@ -9,7 +9,6 @@
#include <ibm/bios.h> #include <ibm/bios.h>
#include <minix/portio.h> #include <minix/portio.h>
#include <minix/u64.h> #include <minix/u64.h>
#include <minix/sysutil.h>
#include <a.out.h> #include <a.out.h>
#include "proto.h" #include "proto.h"

View file

@ -118,6 +118,8 @@ PUBLIC int bsp_timer_int_handler(void)
{ {
unsigned ticks; unsigned ticks;
IDLE_STOP;
if(minix_panicing) if(minix_panicing)
return 0; return 0;
@ -228,6 +230,8 @@ PUBLIC int ap_timer_int_handler(void)
int expired = 0; int expired = 0;
struct proc * p, * billp; struct proc * p, * billp;
IDLE_STOP;
/* Update user and system accounting times. Charge the current process /* Update user and system accounting times. Charge the current process
* for user time. If the current process is not billable, that is, if a * 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 * non-user process is running, charge the billable process for system

View file

@ -45,6 +45,12 @@
#define lock reallock #define lock reallock
#define unlock realunlock #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() */ /* args to intr_init() */
#define INTS_ORIG 0 /* restore interrupts */ #define INTS_ORIG 0 /* restore interrupts */
#define INTS_MINIX 1 /* initialize interrupts for minix */ #define INTS_MINIX 1 /* initialize interrupts for minix */

View file

@ -1,8 +1,6 @@
#ifndef GLO_H #ifndef GLO_H
#define GLO_H #define GLO_H
#include <minix/sysutil.h>
/* Global variables used in the kernel. This file contains the declarations; /* Global variables used in the kernel. This file contains the declarations;
* storage space for the variables is allocated in table.c, because EXTERN is * 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 * 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 */ EXTERN int config_no_apic; /* optionaly turn off apic */
#endif #endif
#ifdef CONFIG_IDLE_TSC
EXTERN u64_t idle_tsc;
EXTERN u64_t idle_stop;
EXTERN int idle_active;
#endif
/* VM */ /* VM */
EXTERN int vm_running; EXTERN int vm_running;
EXTERN int catch_pagefaults; EXTERN int catch_pagefaults;

View file

@ -112,6 +112,8 @@ PUBLIC void irq_handle(int irq)
{ {
irq_hook_t * hook; irq_hook_t * hook;
IDLE_STOP;
/* here we need not to get this IRQ until all the handlers had a say */ /* here we need not to get this IRQ until all the handlers had a say */
hw_intr_mask(irq); hw_intr_mask(irq);
hook = irq_handlers[irq]; hook = irq_handlers[irq];

View file

@ -5,6 +5,8 @@
#define CONFIG_APIC #define CONFIG_APIC
/* boot verbose */ /* boot verbose */
#define CONFIG_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 /* This is the master header for the kernel. It includes some other files
* and defines the principal constants. * and defines the principal constants.
@ -20,6 +22,7 @@
#include <minix/const.h> /* MINIX specific constants */ #include <minix/const.h> /* MINIX specific constants */
#include <minix/type.h> /* MINIX specific types, e.g. message */ #include <minix/type.h> /* MINIX specific types, e.g. message */
#include <minix/ipc.h> /* MINIX run-time system */ #include <minix/ipc.h> /* MINIX run-time system */
#include <minix/sysutil.h> /* MINIX utility library functions */
#include <timers.h> /* watchdog timer management */ #include <timers.h> /* watchdog timer management */
#include <errno.h> /* return codes and error numbers */ #include <errno.h> /* return codes and error numbers */

View file

@ -16,6 +16,7 @@
#include <minix/callnr.h> #include <minix/callnr.h>
#include <minix/com.h> #include <minix/com.h>
#include <minix/endpoint.h> #include <minix/endpoint.h>
#include <minix/u64.h>
#include "proc.h" #include "proc.h"
#include "debug.h" #include "debug.h"
#include "clock.h" #include "clock.h"
@ -189,6 +190,10 @@ PUBLIC void main()
#endif /* SPROFILE */ #endif /* SPROFILE */
cprof_procs_no = 0; /* init nr of hash table slots used */ cprof_procs_no = 0; /* init nr of hash table slots used */
#ifdef CONFIG_IDLE_TSC
idle_tsc = cvu64(0);
#endif
vm_running = 0; vm_running = 0;
krandom.random_sources = RANDOM_SOURCES; krandom.random_sources = RANDOM_SOURCES;
krandom.random_elements = RANDOM_ELEMENTS; krandom.random_elements = RANDOM_ELEMENTS;

View file

@ -40,6 +40,7 @@
#include <signal.h> #include <signal.h>
#include <minix/portio.h> #include <minix/portio.h>
#include <minix/u64.h> #include <minix/u64.h>
#include <minix/syslib.h>
#include "debug.h" #include "debug.h"
#include "kernel.h" #include "kernel.h"
@ -50,6 +51,7 @@
* other parts of the kernel through lock_...(). The lock temporarily disables * other parts of the kernel through lock_...(). The lock temporarily disables
* interrupts to prevent race conditions. * interrupts to prevent race conditions.
*/ */
FORWARD _PROTOTYPE( void idle, (void));
FORWARD _PROTOTYPE( int mini_send, (struct proc *caller_ptr, int dst_e, FORWARD _PROTOTYPE( int mini_send, (struct proc *caller_ptr, int dst_e,
message *m_ptr, int flags)); message *m_ptr, int flags));
FORWARD _PROTOTYPE( int mini_receive, (struct proc *caller_ptr, int src, 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); 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 * * schedcheck *
*===========================================================================*/ *===========================================================================*/
@ -166,7 +197,7 @@ not_runnable_pick_new:
proc_ptr = proc_addr(IDLE); proc_ptr = proc_addr(IDLE);
if (priv(proc_ptr)->s_flags & BILLABLE) if (priv(proc_ptr)->s_flags & BILLABLE)
bill_ptr = proc_ptr; bill_ptr = proc_ptr;
halt_cpu(); idle();
} }
check_misc_flags: check_misc_flags:

View file

@ -19,6 +19,7 @@ _PROTOTYPE( void reset_timer, (struct timer *tp) );
_PROTOTYPE( void ser_dump_proc, (void) ); _PROTOTYPE( void ser_dump_proc, (void) );
/* main.c */ /* main.c */
_PROTOTYPE( void main, (void) );
_PROTOTYPE( void prepare_shutdown, (int how) ); _PROTOTYPE( void prepare_shutdown, (int how) );
_PROTOTYPE( void minix_shutdown, (struct timer *tp) ); _PROTOTYPE( void minix_shutdown, (struct timer *tp) );
@ -102,6 +103,7 @@ _PROTOTYPE( void stop_profile_clock, (void) );
#endif #endif
/* functions defined in architecture-dependent files. */ /* functions defined in architecture-dependent files. */
_PROTOTYPE( void prot_init, (void) );
_PROTOTYPE( phys_bytes phys_copy, (phys_bytes source, phys_bytes dest, _PROTOTYPE( phys_bytes phys_copy, (phys_bytes source, phys_bytes dest,
phys_bytes count) ); phys_bytes count) );
_PROTOTYPE( void phys_copy_fault, (void)); _PROTOTYPE( void phys_copy_fault, (void));

View file

@ -141,18 +141,26 @@ register message *m_ptr; /* pointer to request message */
break; break;
} }
#endif #endif
case GET_IRQACTIDS: { case GET_IRQACTIDS: {
length = sizeof(irq_actids); length = sizeof(irq_actids);
src_vir = (vir_bytes) irq_actids; src_vir = (vir_bytes) irq_actids;
break; break;
} }
case GET_PRIVID: {
case GET_PRIVID:
if (!isokendpt(m_ptr->I_VAL_LEN2_E, &proc_nr)) if (!isokendpt(m_ptr->I_VAL_LEN2_E, &proc_nr))
return EINVAL; return EINVAL;
return proc_addr(proc_nr)->p_priv->s_id; 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: default:
kprintf("do_getinfo: invalid request %d\n", m_ptr->I_REQUEST); kprintf("do_getinfo: invalid request %d\n", m_ptr->I_REQUEST);
return(EINVAL); return(EINVAL);

View file

@ -11,7 +11,6 @@
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
#include <minix/sysutil.h>
#include <minix/sys_config.h> #include <minix/sys_config.h>
/*===========================================================================* /*===========================================================================*

View file

@ -10,6 +10,7 @@ libsys_FILES=" \
kputc.c \ kputc.c \
tickdelay.c \ tickdelay.c \
get_randomness.c \ get_randomness.c \
getidle.c \
getuptime.c \ getuptime.c \
getuptime2.c \ getuptime2.c \
env_get_prm.c \ env_get_prm.c \

92
lib/sysutil/getidle.c Normal file
View file

@ -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;
}

View file

@ -16,7 +16,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <minix/profile.h> #include <minix/profile.h>
#include <minix/syslib.h> #include <minix/sysutil.h>
#include <minix/u64.h> #include <minix/u64.h>
PRIVATE char cpath[CPROF_CPATH_MAX_LEN]; /* current call path string */ 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; if (cprof_locked) return; else cprof_locked = 1;
/* Read CPU cycle count into local variable. */ /* Read CPU cycle count into local variable. */
read_tsc(&start.hi, &start.lo); read_tsc_64(&start);
/* Run init code once after system boot. */ /* Run init code once after system boot. */
if (init == 0) { if (init == 0) {
@ -105,8 +105,7 @@ char *name;
} }
/* Save initial cycle count on stack. */ /* Save initial cycle count on stack. */
cprof_stk[cprof_stk_top].start_1.hi = start.hi; cprof_stk[cprof_stk_top].start_1 = start;
cprof_stk[cprof_stk_top].start_1.lo = start.lo;
/* Check available call path len. */ /* Check available call path len. */
if (cpath_len + strlen(name) + 1 > CPROF_CPATH_MAX_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; cprof_stk[cprof_stk_top].slot = cprof_slot;
/* Again save CPU cycle count on stack. */ /* Again save CPU cycle count on stack. */
read_tsc(&cprof_stk[cprof_stk_top].start_2.hi, read_tsc_64(&cprof_stk[cprof_stk_top].start_2);
&cprof_stk[cprof_stk_top].start_2.lo);
cprof_locked = 0; cprof_locked = 0;
} }
@ -201,8 +199,7 @@ char *name;
sub64(spent, cprof_stk[cprof_stk_top].spent_deeper)); sub64(spent, cprof_stk[cprof_stk_top].spent_deeper));
/* Clear spent_deeper for call level we're leaving. */ /* 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 = cvu64(0);
cprof_stk[cprof_stk_top].spent_deeper.hi = 0;
/* Adjust call path string and stack. */ /* Adjust call path string and stack. */
cpath_len = cprof_stk[cprof_stk_top].cpath_len; 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++) { for (i=0; i<CPROF_STACK_SIZE; i++) {
cprof_stk[i].cpath_len = 0; cprof_stk[i].cpath_len = 0;
cprof_stk[i].slot = 0; cprof_stk[i].slot = 0;
cprof_stk[i].start_1.lo = 0; cprof_stk[i].start_1 = cvu64(0);
cprof_stk[i].start_1.hi = 0; cprof_stk[i].start_2 = cvu64(0);
cprof_stk[i].start_2.lo = 0; cprof_stk[i].spent_deeper = cvu64(0);
cprof_stk[i].start_2.hi = 0;
cprof_stk[i].spent_deeper.lo = 0;
cprof_stk[i].spent_deeper.hi = 0;
} }
} }

View file

@ -274,11 +274,13 @@ PUBLIC int do_getsysinfo_up()
vir_bytes src_addr, dst_addr; vir_bytes src_addr, dst_addr;
struct loadinfo loadinfo; struct loadinfo loadinfo;
size_t len, real_len; size_t len, real_len;
u64_t idle_tsc;
int s; int s;
switch(m_in.SIU_WHAT) { switch(m_in.SIU_WHAT) {
case SIU_LOADINFO: /* loadinfo is obtained via PM */ case SIU_LOADINFO: /* loadinfo is obtained via PM */
sys_getloadinfo(&loadinfo); if ((s = sys_getloadinfo(&loadinfo)) != OK)
return s;
src_addr = (vir_bytes) &loadinfo; src_addr = (vir_bytes) &loadinfo;
real_len = sizeof(struct loadinfo); real_len = sizeof(struct loadinfo);
break; break;
@ -286,6 +288,12 @@ PUBLIC int do_getsysinfo_up()
src_addr = (vir_bytes) &system_hz; src_addr = (vir_bytes) &system_hz;
real_len = sizeof(system_hz); real_len = sizeof(system_hz);
break; 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: default:
return(EINVAL); return(EINVAL);
} }