System statistical and call profiling

support by Rogier Meurs <rogier@meurs.org>.
This commit is contained in:
Ben Gras
2006-10-30 15:53:38 +00:00
parent fa0ba56bc9
commit 7195fe3325
76 changed files with 2650 additions and 77 deletions

View File

@@ -10,14 +10,14 @@ s = system
CC = exec cc
CPP = $l/cpp
LD = $(CC) -.o
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
HEAD = mpx.o
OBJS = start.o protect.o klib.o table.o kprintf.o main.o proc.o \
i8259.o exception.o system.o clock.o utility.o debug.o
i8259.o exception.o system.o clock.o utility.o debug.o profile.o
SYSTEM = system.a
LIBS = -ltimers
LIBS = -ltimers -lsysutil
# What to make.

View File

@@ -29,6 +29,7 @@
#include "proto.h" /* function prototypes */
#include "glo.h" /* global variables */
#include "ipc.h" /* IPC constants */
#include "profile.h" /* system profiling */
#include "debug.h" /* debugging, MUST be last kernel header */
#endif /* KERNEL_H */

View File

@@ -181,6 +181,13 @@ PUBLIC void main()
}
#endif
#if SPROFILE
sprofiling = 0; /* we're not profiling until instructed to */
#endif /* SPROFILE */
#if CPROFILE
cprof_procs_no = 0; /* init nr of hash table slots used */
#endif /* CPROFILE */
/* MINIX is now ready. All boot image processes are on the ready queue.
* Return to the assembly code to start running the current process.
*/

207
kernel/profile.c Normal file
View File

@@ -0,0 +1,207 @@
/*
* This file contains several functions and variables used for system
* profiling.
*
* Statistical Profiling:
* The interrupt handler and control functions for the CMOS clock.
*
* Call Profiling:
* The table used for profiling data and a function to get its size.
*
* The function used by kernelspace processes to register the locations
* of their control struct and profiling table.
*
* Changes:
* 14 Aug, 2006 Created, (Rogier Meurs)
*/
#include <minix/config.h>
#if SPROFILE || CPROFILE
#include <minix/profile.h>
#include "kernel.h"
#include "profile.h"
#include "proc.h"
#endif
#if SPROFILE
#include <string.h>
#include <ibm/cmos.h>
/* Function prototype for the CMOS clock handler. */
FORWARD _PROTOTYPE( int cmos_clock_handler, (irq_hook_t *hook) );
/* A hook for the CMOS clock interrupt handler. */
PRIVATE irq_hook_t cmos_clock_hook;
/*===========================================================================*
* init_cmos_clock *
*===========================================================================*/
PUBLIC void init_cmos_clock(unsigned freq)
{
int r;
/* Register interrupt handler for statistical system profiling.
* This uses the CMOS timer.
*/
cmos_clock_hook.proc_nr_e = CLOCK;
put_irq_handler(&cmos_clock_hook, CMOS_CLOCK_IRQ, cmos_clock_handler);
enable_irq(&cmos_clock_hook);
intr_disable();
/* Set CMOS timer frequency. */
outb(RTC_INDEX, RTC_REG_A);
outb(RTC_IO, RTC_A_DV_OK | freq);
/* Enable CMOS timer interrupts. */
outb(RTC_INDEX, RTC_REG_B);
r = inb(RTC_IO);
outb(RTC_INDEX, RTC_REG_B);
outb(RTC_IO, r | RTC_B_PIE);
/* Mandatory read of CMOS register to enable timer interrupts. */
outb(RTC_INDEX, RTC_REG_C);
inb(RTC_IO);
intr_enable();
}
/*===========================================================================*
* cmos_clock_stop *
*===========================================================================*/
PUBLIC void stop_cmos_clock()
{
int r;
intr_disable();
/* Disable CMOS timer interrupts. */
outb(RTC_INDEX, RTC_REG_B);
r = inb(RTC_IO);
outb(RTC_INDEX, RTC_REG_B);
outb(RTC_IO, r & !RTC_B_PIE);
intr_enable();
/* Unregister interrupt handler. */
disable_irq(&cmos_clock_hook);
rm_irq_handler(&cmos_clock_hook);
}
/*===========================================================================*
* cmos_clock_handler *
*===========================================================================*/
PRIVATE int cmos_clock_handler(hook)
irq_hook_t *hook;
{
/* This executes on every tick of the CMOS timer. */
/* Are we profiling, and profiling memory not full? */
if (!sprofiling || sprof_info.mem_used == -1) return (1);
/* Check if enough memory available before writing sample. */
if (sprof_info.mem_used + sizeof(sprof_info) > sprof_mem_size) {
sprof_info.mem_used = -1;
return(1);
}
/* All is OK */
/* Idle process? */
if (priv(proc_ptr)->s_proc_nr == IDLE) {
sprof_info.idle_samples++;
} else
/* Runnable system process? */
if (priv(proc_ptr)->s_flags & SYS_PROC && !proc_ptr->p_rts_flags) {
/* Note: k_reenter is always 0 here. */
/* Store sample (process name and program counter). */
phys_copy(vir2phys(proc_ptr->p_name),
(phys_bytes) (sprof_data_addr + sprof_info.mem_used),
(phys_bytes) strlen(proc_ptr->p_name));
phys_copy(vir2phys(&proc_ptr->p_reg.pc),
(phys_bytes) (sprof_data_addr+sprof_info.mem_used +
sizeof(proc_ptr->p_name)),
(phys_bytes) sizeof(proc_ptr->p_reg.pc));
sprof_info.mem_used += sizeof(sprof_sample);
sprof_info.system_samples++;
} else {
/* User process. */
sprof_info.user_samples++;
}
sprof_info.total_samples++;
/* Mandatory read of CMOS register to re-enable timer interrupts. */
outb(RTC_INDEX, RTC_REG_C);
inb(RTC_IO);
return(1); /* reenable interrupts */
}
#endif /* SPROFILE */
#if CPROFILE
/*
* The following variables and functions are used by the procentry/
* procentry syslib functions when linked with kernelspace processes.
* For userspace processes, the same variables and function are defined
* elsewhere. This enables different functionality and variable sizes,
* which is needed is a few cases.
*/
/* A small table is declared for the kernelspace processes. */
struct cprof_tbl_s cprof_tbl[CPROF_TABLE_SIZE_KERNEL];
/* Function that returns table size. */
PUBLIC int profile_get_tbl_size(void)
{
return CPROF_TABLE_SIZE_KERNEL;
}
/* Function that returns on which execution of procentry to announce. */
PUBLIC int profile_get_announce(void)
{
return CPROF_ACCOUNCE_KERNEL;
}
/*
* The kernel "announces" its control struct and table locations
* to itself through this function.
*/
PUBLIC void profile_register(ctl_ptr, tbl_ptr)
void *ctl_ptr;
void *tbl_ptr;
{
int len, proc_nr;
vir_bytes vir_dst;
struct proc *rp;
/* Store process name, control struct, table locations. */
proc_nr = KERNEL;
rp = proc_addr(proc_nr);
cprof_proc_info[cprof_procs_no].endpt = rp->p_endpoint;
cprof_proc_info[cprof_procs_no].name = rp->p_name;
len = (phys_bytes) sizeof (void *);
vir_dst = (vir_bytes) ctl_ptr;
cprof_proc_info[cprof_procs_no].ctl =
numap_local(proc_nr, vir_dst, len);
vir_dst = (vir_bytes) tbl_ptr;
cprof_proc_info[cprof_procs_no].buf =
numap_local(proc_nr, vir_dst, len);
cprof_procs_no++;
}
#endif /* CPROFILE */

41
kernel/profile.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef PROFILE_H
#define PROFILE_H
#if SPROFILE || CPROFILE
#include <minix/profile.h>
#endif
#if SPROFILE /* statistical profiling */
_PROTOTYPE( void init_cmos_clock, (unsigned freq) );
_PROTOTYPE( void stop_cmos_clock, (void) );
EXTERN int sprofiling; /* whether profiling is running */
EXTERN int sprof_mem_size; /* available user memory for data */
EXTERN struct sprof_info_s sprof_info; /* profiling info for user program */
EXTERN phys_bytes sprof_data_addr; /* user address to write data */
EXTERN phys_bytes sprof_info_addr; /* user address to write info struct */
#endif /* SPROFILE */
#if CPROFILE /* call profiling */
EXTERN int cprof_mem_size; /* available user memory for data */
EXTERN struct cprof_info_s cprof_info; /* profiling info for user program */
EXTERN phys_bytes cprof_data_addr; /* user address to write data */
EXTERN phys_bytes cprof_info_addr; /* user address to write info struct */
EXTERN int cprof_procs_no; /* number of profiled processes */
EXTERN struct cprof_proc_info_s { /* info about profiled process */
int endpt; /* endpoint */
char *name; /* name */
phys_bytes ctl; /* location of control struct */
phys_bytes buf; /* location of buffer */
int slots_used; /* table slots used */
} cprof_proc_info_inst;
EXTERN struct cprof_proc_info_s cprof_proc_info[NR_SYS_PROCS];
#endif /* CPROFILE */
#endif /* PROFILE_H */

View File

@@ -190,6 +190,11 @@ PRIVATE void initialize(void)
map(SYS_ABORT, do_abort); /* abort MINIX */
map(SYS_GETINFO, do_getinfo); /* request system information */
map(SYS_IOPENABLE, do_iopenable); /* Enable I/O */
/* Profiling. */
map(SYS_SPROF, do_sprofile); /* start/stop statistical profiling */
map(SYS_CPROF, do_cprofile); /* get/reset call profiling data */
map(SYS_PROFBUF, do_profbuf); /* announce locations to kernel */
}
/*===========================================================================*

View File

@@ -179,5 +179,20 @@ _PROTOTYPE( int do_iopenable, (message *m_ptr) );
_PROTOTYPE( int do_setgrant, (message *m_ptr) );
_PROTOTYPE( int do_readbios, (message *m_ptr) );
_PROTOTYPE( int do_sprofile, (message *m_ptr) );
#if ! SPROFILE
#define do_sprofile do_unused
#endif
_PROTOTYPE( int do_cprofile, (message *m_ptr) );
#if ! CPROFILE
#define do_cprofile do_unused
#endif
_PROTOTYPE( int do_profbuf, (message *m_ptr) );
#if ! CPROFILE
#define do_profbuf do_unused
#endif
#endif /* SYSTEM_H */

View File

@@ -6,9 +6,11 @@ i = $u/include
# Programs, flags, etc.
CC = exec cc $(CFLAGS) -c
CCNOPROF = exec cc $(CFLAGSNOPROF) -c # no call profiling for these
CPP = $l/cpp
LD = $(CC) -.o
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
CFLAGSNOPROF = -I$i
LDFLAGS = -i
SYSTEM = ../system.a
@@ -50,6 +52,9 @@ OBJECTS = \
$(SYSTEM)(do_iopenable.o) \
$(SYSTEM)(do_vm.o) \
$(SYSTEM)(do_vm_setbuf.o) \
$(SYSTEM)(do_sprofile.o) \
$(SYSTEM)(do_cprofile.o) \
$(SYSTEM)(do_profbuf.o)
$(SYSTEM): $(OBJECTS)
aal cr $@ *.o
@@ -163,3 +168,12 @@ do_vm.o: do_vm.c
$(SYSTEM)(do_vm_setbuf.o): do_vm_setbuf.c
$(CC) do_vm_setbuf.c
$(SYSTEM)(do_sprofile.o): do_sprofile.c
$(CC) do_sprofile.c
$(SYSTEM)(do_cprofile.o): do_cprofile.c
$(CC) do_cprofile.c
$(SYSTEM)(do_profbuf.o): do_profbuf.c
$(CC) do_profbuf.c

160
kernel/system/do_cprofile.c Normal file
View File

@@ -0,0 +1,160 @@
/* The kernel call that is implemented in this file:
* m_type: SYS_CPROFILE
*
* The parameters for this kernel call are:
* m7_i1: PROF_ACTION (get/reset profiling data)
* m7_i2: PROF_MEM_SIZE (available memory for data)
* m7_i4: PROF_ENDPT (endpoint of caller)
* m7_p1: PROF_CTL_PTR (location of info struct)
* m7_p2: PROF_MEM_PTR (location of memory for data)
*
* Changes:
* 14 Aug, 2006 Created (Rogier Meurs)
*/
#include "../system.h"
#if CPROFILE
#include <string.h>
/*===========================================================================*
* do_cprofile *
*===========================================================================*/
PUBLIC int do_cprofile(m_ptr)
register message *m_ptr; /* pointer to request message */
{
int proc_nr, i, err = 0, k = 0;
vir_bytes vir_dst;
phys_bytes phys_src, phys_dst, len;
switch (m_ptr->PROF_ACTION) {
case PROF_RESET:
/* Reset profiling tables. */
cprof_ctl_inst.reset = 1;
kprintf("CPROFILE notice: resetting tables:");
for (i=0; i<cprof_procs_no; i++) {
kprintf(" %s", cprof_proc_info[i].name);
/* Test whether proc still alive. */
if (!isokendpt(cprof_proc_info[i].endpt, &proc_nr)) {
kprintf("endpt not valid %u (%s)\n",
cprof_proc_info[i].endpt, cprof_proc_info[i].name);
continue;
}
/* Set reset flag. */
phys_src = vir2phys((vir_bytes) &cprof_ctl_inst.reset);
phys_dst = (phys_bytes) cprof_proc_info[i].ctl;
len = (phys_bytes) sizeof(cprof_ctl_inst.reset);
phys_copy(phys_src, phys_dst, len);
}
kprintf("\n");
return OK;
case PROF_GET:
/* Get profiling data.
*
* Calculate physical addresses of user pointers. Copy to user
* program the info struct. Copy to user program the profiling
* tables of the profiled processes.
*/
if(!isokendpt(m_ptr->PROF_ENDPT, &proc_nr))
return EINVAL;
vir_dst = (vir_bytes) m_ptr->PROF_CTL_PTR;
len = (phys_bytes) sizeof (int *);
cprof_info_addr = numap_local(proc_nr, vir_dst, len);
vir_dst = (vir_bytes) m_ptr->PROF_MEM_PTR;
len = (phys_bytes) sizeof (char *);
cprof_data_addr = numap_local(proc_nr, vir_dst, len);
cprof_mem_size = m_ptr->PROF_MEM_SIZE;
kprintf("CPROFILE notice: getting tables:");
/* Copy control structs of profiled processes to calculate total
* nr of bytes to be copied to user program and find out if any
* errors happened. */
cprof_info.mem_used = 0;
cprof_info.err = 0;
for (i=0; i<cprof_procs_no; i++) {
kprintf(" %s", cprof_proc_info[i].name);
/* Test whether proc still alive. */
if (!isokendpt(cprof_proc_info[i].endpt, &proc_nr)) {
kprintf("endpt not valid %u (%s)\n",
cprof_proc_info[i].endpt, cprof_proc_info[i].name);
continue;
}
/* Copy control struct from proc to local variable. */
phys_src = cprof_proc_info[i].ctl;
phys_dst = vir2phys((vir_bytes) &cprof_ctl_inst);
len = (phys_bytes) sizeof(cprof_ctl_inst);
phys_copy(phys_src, phys_dst, len);
/* Calculate memory used. */
cprof_proc_info[i].slots_used = cprof_ctl_inst.slots_used;
cprof_info.mem_used += CPROF_PROCNAME_LEN;
cprof_info.mem_used += sizeof(cprof_proc_info_inst.slots_used);
cprof_info.mem_used += cprof_proc_info[i].slots_used *
sizeof(cprof_tbl_inst);
/* Collect errors. */
cprof_info.err |= cprof_ctl_inst.err;
}
kprintf("\n");
/* Do we have the space available? */
if (cprof_mem_size < cprof_info.mem_used) cprof_info.mem_used = -1;
/* Copy the info struct to the user process. */
phys_copy(vir2phys((vir_bytes) &cprof_info), cprof_info_addr,
(phys_bytes) sizeof(cprof_info));
/* If there is no space or errors occurred, don't bother copying. */
if (cprof_info.mem_used == -1 || cprof_info.err) return OK;
/* For each profiled process, copy its name, slots_used and profiling
* table to the user process. */
phys_dst = cprof_data_addr;
for (i=0; i<cprof_procs_no; i++) {
phys_src = vir2phys((vir_bytes) cprof_proc_info[i].name);
len = (phys_bytes) strlen(cprof_proc_info[i].name);
phys_copy(phys_src, phys_dst, len);
phys_dst += CPROF_PROCNAME_LEN;
phys_src = cprof_proc_info[i].ctl +
sizeof(cprof_ctl_inst.reset);
len = (phys_bytes) sizeof(cprof_ctl_inst.slots_used);
phys_copy(phys_src, phys_dst, len);
phys_dst += len;
phys_src = cprof_proc_info[i].buf;
len = (phys_bytes)
(sizeof(cprof_tbl_inst) * cprof_proc_info[i].slots_used);
phys_copy(phys_src, phys_dst, len);
phys_dst += len;
}
return OK;
default:
return EINVAL;
}
}
#endif /* CPROFILE */

View File

@@ -8,6 +8,7 @@
#include "../system.h"
#include "../ipc.h"
#include <signal.h>
#include <string.h>
#if USE_PRIVCTL

View File

@@ -0,0 +1,55 @@
/* The kernel call that is implemented in this file:
* m_type: SYS_PROFBUF
*
* The parameters for this kernel call are:
* m7_p1: PROF_CTL_PTR (location of control struct)
* m7_p2: PROF_MEM_PTR (location of profiling table)
*
* Changes:
* 14 Aug, 2006 Created (Rogier Meurs)
*/
#include "../system.h"
#if CPROFILE
/*===========================================================================*
* do_profbuf *
*===========================================================================*/
PUBLIC int do_profbuf(m_ptr)
register message *m_ptr; /* pointer to request message */
{
/* This kernel call is used by profiled system processes when Call
* Profiling is enabled. It is called on the first execution of procentry.
* By means of this kernel call, the profiled processes inform the kernel
* about the location of their profiling table and the control structure
* which is used to enable the kernel to have the tables cleared.
*/
int proc_nr, len;
vir_bytes vir_dst;
struct proc *rp;
/* Store process name, control struct, table locations. */
isokendpt(m_ptr->m_source, &proc_nr);
rp = proc_addr(proc_nr);
cprof_proc_info[cprof_procs_no].endpt = who_e;
cprof_proc_info[cprof_procs_no].name = rp->p_name;
len = (phys_bytes) sizeof (void *);
vir_dst = (vir_bytes) m_ptr->PROF_CTL_PTR;
cprof_proc_info[cprof_procs_no].ctl =
numap_local(proc_nr, vir_dst, len);
vir_dst = (vir_bytes) m_ptr->PROF_MEM_PTR;
cprof_proc_info[cprof_procs_no].buf =
numap_local(proc_nr, vir_dst, len);
cprof_procs_no++;
return OK;
}
#endif /* CPROFILE */

View File

@@ -0,0 +1,94 @@
/* The kernel call that is implemented in this file:
* m_type: SYS_SPROFILE
*
* The parameters for this kernel call are:
* m7_i1: PROF_ACTION (start/stop profiling)
* m7_i2: PROF_MEM_SIZE (available memory for data)
* m7_i3: PROF_FREQ (requested sample frequency)
* m7_i4: PROF_ENDPT (endpoint of caller)
* m7_p1: PROF_CTL_PTR (location of info struct)
* m7_p2: PROF_MEM_PTR (location of memory for data)
*
* Changes:
* 14 Aug, 2006 Created (Rogier Meurs)
*/
#include "../system.h"
#if SPROFILE
/*===========================================================================*
* do_sprofile *
*===========================================================================*/
PUBLIC int do_sprofile(m_ptr)
register message *m_ptr; /* pointer to request message */
{
int proc_nr, i;
vir_bytes vir_dst;
phys_bytes length;
switch(m_ptr->PROF_ACTION) {
case PROF_START:
/* Starting profiling.
*
* Check if profiling is not already running. Calculate physical
* addresses of user pointers. Reset counters. Start CMOS timer.
* Turn on profiling.
*/
if (sprofiling) {
kprintf("SYSTEM: start s-profiling: already started\n");
return EBUSY;
}
isokendpt(m_ptr->PROF_ENDPT, &proc_nr);
vir_dst = (vir_bytes) m_ptr->PROF_CTL_PTR;
length = (phys_bytes) sizeof (int *);
sprof_info_addr = numap_local(proc_nr, vir_dst, length);
vir_dst = (vir_bytes) m_ptr->PROF_MEM_PTR;
length = (phys_bytes) sizeof (char *);
sprof_data_addr = numap_local(proc_nr, vir_dst, length);
sprof_info.mem_used = 0;
sprof_info.total_samples = 0;
sprof_info.idle_samples = 0;
sprof_info.system_samples = 0;
sprof_info.user_samples = 0;
sprof_mem_size = m_ptr->PROF_MEM_SIZE;
init_cmos_clock(m_ptr->PROF_FREQ);
sprofiling = 1;
return OK;
case PROF_STOP:
/* Stopping profiling.
*
* Check if profiling is indeed running. Turn off profiling.
* Stop CMOS timer. Copy info struct to user process.
*/
if (!sprofiling) {
kprintf("SYSTEM: stop s-profiling: not started\n");
return EBUSY;
}
sprofiling = 0;
stop_cmos_clock();
phys_copy(vir2phys((vir_bytes) &sprof_info),
sprof_info_addr, (phys_bytes) sizeof(sprof_info));
return OK;
default:
return EINVAL;
}
}
#endif /* SPROFILE */

View File

@@ -82,9 +82,9 @@ PUBLIC char *t_stack[TOT_STACK_SPACE / sizeof(char *)];
*/
#define FS_C SYS_KILL, SYS_VIRCOPY, SYS_SAFECOPYFROM, SYS_SAFECOPYTO, \
SYS_VIRVCOPY, SYS_UMAP, SYS_GETINFO, SYS_EXIT, SYS_TIMES, SYS_SETALARM, \
SYS_PRIVCTL, SYS_TRACE , SYS_SETGRANT
SYS_PRIVCTL, SYS_TRACE , SYS_SETGRANT, SYS_PROFBUF
#define DRV_C FS_C, SYS_SEGCTL, SYS_IRQCTL, SYS_INT86, SYS_DEVIO, \
SYS_SDEVIO, SYS_VDEVIO, SYS_SETGRANT
SYS_SDEVIO, SYS_VDEVIO, SYS_SETGRANT, SYS_PROFBUF
PRIVATE int
fs_c[] = { FS_C },