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

@ -4,7 +4,7 @@ MAKE = exec make -$(MAKEFLAGS)
BZIP2=bzip2-1.0.3
FLEX=flex-2.5.4
SMALLPROGRAMS=`arch` aal advent ash autil awk bc byacc cawf cron de dhcpd dis88 elle elvis ftp101 ftpd200 ibm indent m4 make mdb mined patch pax ps reboot rlogind scripts sh simple syslogd talk talkd telnet telnetd urlget yap
SMALLPROGRAMS=`arch` aal advent ash autil awk bc byacc cawf cron de dhcpd dis88 elle elvis ftp101 ftpd200 ibm indent m4 make mdb mined patch pax profile ps reboot rlogind scripts sh simple syslogd talk talkd telnet telnetd urlget yap
usage:
@echo "Usage: make all # Compile all commands" >&2

29
commands/profile/Makefile Normal file
View file

@ -0,0 +1,29 @@
# Makefile for profile and profalyze.pl
CC=exec cc
all: profile sprofalyze.pl cprofalyze.pl
profile: profile.c
$(CC) -o $@ profile.c
install -S 65m $@
sprofalyze.pl:
install $@
cprofalyze.pl:
install $@
install: /usr/bin/profile /usr/bin/sprofalyze.pl /usr/bin/cprofalyze.pl
/usr/bin/profile: profile
install -cs -o bin $? $@
/usr/bin/sprofalyze.pl: sprofalyze.pl
install -o bin $? $@
/usr/bin/cprofalyze.pl: cprofalyze.pl
install -o bin $? $@
clean:
rm -f *.o core *.bak profile

273
commands/profile/cprofalyze.pl Executable file
View file

@ -0,0 +1,273 @@
#!/usr/local/bin/perl
#
# cprofalyze.pl
#
# Analyzes the output files created by the profile command for
# Call Profiling.
#
# Changes:
# 14 Aug, 2006 Created (Rogier Meurs)
#
$UNSIGNED_MAX_DIV_K = 2**32/1000;
if ($#ARGV == 0 || process_args(@ARGV)) {
print "Usage:\n";
print " cprofalyze.pl <clock> [-f] [-aoct] [-i] [-n number] file ...\n\n";
print " clock CPU clock of source machine in MHz (mandatory)\n";
print " -f print totals per function (original order lost)\n";
print " -a sort alphabetically (default)\n";
print " -o no sort (original order)\n";
print " -c sort by number of calls\n";
print " -t sort by time spent\n";
print " -n print maximum of number lines per process\n";
print " -i when -[ao] used: print full paths\n";
exit 1;
}
sub process_args {
$_ = shift;
return 1 unless /^(\d+)$/;
return 1 if $1 == 0;
$MHz = $1;
$sort_method = "A";
while (@_[0] =~ /^-/) {
$_ = shift;
SWITCH: {
if (/^-a$/) { $sort_method = "A"; last SWITCH; }
if (/^-o$/) { $sort_method = "O"; last SWITCH; }
if (/^-c$/) { $sort_method = "C"; last SWITCH; }
if (/^-t$/) { $sort_method = "T"; last SWITCH; }
if (/^-i$/) { $print_full_paths = 1; last SWITCH; }
if (/^-f$/) { $print_totals = 1; last SWITCH; }
if (/^-n$/) {
$_ = shift;
return 1 unless /^(\d+)$/;
return 1 unless $1 > 0;
$show_paths = $1;
last SWITCH;
}
return 1;
}
}
$print_full_paths == 1 && ($sort_method eq "T" || $sort_method eq "C") &&
{ $print_full_paths = 0 };
@files = @_;
return 0;
}
print <<EOF;
Notes:
- Calls attributed to a path are calls done on that call level.
For instance: a() is called once and calls b() twice. Call path "a" is
attributed 1 call, call path "a b" is attributed 2 calls.
- Time spent blocking is included.
- Time attributed to a path is time spent on that call level.
For instance: a() spends 10 cycles in its own body and calls b() which
spends 5 cycles in its body. Call path "a" is attributed 10 cycles,
call path "a b" is attributed 5 cycles.
- Time is attributed when a function exits. Functions calls that have not
returned yet are therefore not measured. This is most notable in main
functions that are printed as having zero cycles.
- When "profile reset" was run, the actual resetting in a process happens
when a function is entered. In some processes (for example, blocking
ones) this may not happen immediately, or at all.
EOF
print "Clockspeed entered: $MHz MHz. ";
SWITCH: {
if ($sort_method eq "A")
{ print "Sorting alphabetically. "; last SWITCH; }
if ($sort_method eq "C")
{ print "Sorting by calls. "; last SWITCH; }
if ($sort_method eq "T")
{ print "Sorting by time spent. "; last SWITCH; }
print "No sorting applied. ";
}
print "\n";
$print_totals and print "Printing totals per function. ";
$show_paths == 0 ? print "Printing all call paths.\n" :
print "Printing max. $show_paths lines per process.\n";
foreach $file (@files) {
$file_res = read_file($file);
next if $file_res == 0;
print_file($print_totals ? make_totals($file_res) : $file_res);
}
exit 0;
sub read_file
{
$file = shift;
my %file_res = ();
my @exe;
my $exe_name, $slots_used, $buf, $lo, $hi, $cycles_div_k, $ms;
unless (open(FILE, $file)) {
print "\nERROR: Unable to open $file: $!\n";
return 0;
}
$file =~ s/^.*\///; # basename
# First line: check file type.
$_ = <FILE>; chomp;
if (!/^call$/) {
if (/^stat$/) {
print "Statistical Profiling output file: ";
print "Use sprofalyze.pl instead.\n";
} else {
print "Not a profiling output file.\n";
}
return 0;
}
# Second line: header with call path string size.
$_ = <FILE>; chomp;
($CPATH_MAX_LEN, $PROCNAME_LEN) = split(/ /);
$SLOT_SIZE = $CPATH_MAX_LEN + 16;
$EXE_HEADER_SIZE = $PROCNAME_LEN + 4;
# Read in the data for all the processes and put it in a hash of lists.
# A list for each process, which contains lists itself for each call
# path.
until(eof(FILE)) {
read(FILE, $buf, $EXE_HEADER_SIZE) == $EXE_HEADER_SIZE or
die ("Short read.");
($exe_name, $slots_used) = unpack("Z${PROCNAME_LEN}i", $buf);
@exe = ();
for ($i=0; $i<$slots_used; $i++) {
read(FILE, $buf, $SLOT_SIZE) == $SLOT_SIZE or
die ("Short read.");
($chain, $cpath, $calls, $lo, $hi) =
unpack("iA${CPATH_MAX_LEN}iII", $buf);
$cycles_div_k = $hi * $UNSIGNED_MAX_DIV_K;
$cycles_div_k += $lo / 1000;
$ms = $cycles_div_k / $MHz;
push @exe, [ ($cpath, $calls, $ms) ];
}
$file_res{$exe_name} = [ @exe ];
}
return \%file_res;
}
# Aggregate calls and cycles of paths into totals for each function.
sub make_totals
{
my $ref = shift;
my %file_res = %{$ref};
my $exe;
my %res, %calls, %time;
my @totals;
foreach $exe (sort keys %file_res) {
@totals = ();
%calls = ();
%time = ();
@ar = @{$file_res{$exe}};
foreach $path (@ar) {
$_ = $path->[0];
s/^.* //; # basename of call path
$calls{$_} += $path->[1];
$time{$_} += $path->[2];
}
foreach $func (keys %calls) {
push @totals, [ ($func, $calls{$func}, $time{$func}) ];
}
$res{$exe} = [ @totals ];
}
return \%res;
}
sub print_file
{
my $ref = shift;
my %file_res = %{$ref};
my $exe;
printf "\n========================================";
printf "========================================\n";
printf("Data file: %s\n", $file);
printf "========================================";
printf "========================================\n\n";
# If we have the kernel, print it first. Then the others.
print_exe($file_res{"kernel"}, "kernel") if exists($file_res{"kernel"});
foreach $exe (sort keys %file_res) {
print_exe($file_res{$exe}, $exe) unless $exe eq "kernel";
}
}
sub print_exe
{
my $ref = shift;
my $name = shift;
my @exe = @{$ref};
my @funcs, @oldfuncs;
my $slots_used = @exe;
# Print a header.
printf "----------------------------------------";
printf "----------------------------------------\n";
$print_totals ? printf "%-8s %60s functions\n", $name, $slots_used :
printf "%-8s %59s call paths\n", $name, $slots_used;
printf "----------------------------------------";
printf "----------------------------------------\n";
printf("%10s %12s path\n", "calls", "msecs");
printf "----------------------------------------";
printf "----------------------------------------\n";
SWITCH: {
if ($sort_method eq "A") {
@exe = sort { lc($a->[0]) cmp lc($b->[0]) } @exe; last SWITCH; }
if ($sort_method eq "C") {
@exe = reverse sort { $a->[1] <=> $b->[1] } @exe; last SWITCH; }
if ($sort_method eq "T") {
@exe = reverse sort { $a->[2] <=> $b->[2] } @exe; last SWITCH; }
last SWITCH;
}
my $paths;
@oldfuncs = ();
foreach $path (@exe) {
printf("%10u %12.2f ", $path->[1], $path->[2]);
if ($print_full_paths == 1 ||
($sort_method eq "C" || $sort_method eq "T")) {
print $path->[0];
} else {
@funcs = split(/ /, $path->[0]);
for (my $j=0; $j<=$#funcs; $j++) {
if ($j<=$#oldfuncs && $funcs[$j] eq $oldfuncs[$j]) {
print " ---";
} else {
print " " if ($j > 0);
print $funcs[$j];
}
}
@oldfuncs = @funcs;
}
print "\n";
last if (++$paths == $show_paths);
}
print "\n";
}

446
commands/profile/profile.c Normal file
View file

@ -0,0 +1,446 @@
/* profile - profile operating system
*
* The profile command is used to control Statistical and Call Profiling.
* It writes the profiling data collected by the kernel to a file.
*
* Changes:
* 14 Aug, 2006 Created (Rogier Meurs)
*/
#define SPROFILE 1
#define CPROFILE 1
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <minix/profile.h>
#define EHELP 1
#define ESYNTAX 2
#define EMEM 3
#define EOUTFILE 4
#define EFREQ 5
#define EACTION 6
#define START 1
#define STOP 2
#define GET 3
#define RESET 4
#define SPROF (action==START||action==STOP)
#define CPROF (!SPROF)
#define DEF_OUTFILE_S "profile.stat.out"
#define DEF_OUTFILE_C "profile.call.out"
#define DEF_OUTFILE (SPROF?DEF_OUTFILE_S:DEF_OUTFILE_C)
#define NPIPE "/tmp/profile.npipe"
#define MIN_MEMSIZE 1
#define DEF_MEMSIZE 64
#define MIN_FREQ 3
#define MAX_FREQ 15
#define DEF_FREQ 6
#define BUFSIZE 1024
#define MB (1024*1024)
#define SYNCING "SYNC"
#define DEV_LOG "/dev/log"
int action = 0;
int mem_size = 0;
int mem_used = 0;
int freq = 0;
char *outfile = "";
char *mem_ptr;
int outfile_fd, npipe_fd;
struct sprof_info_s sprof_info;
struct cprof_info_s cprof_info;
_PROTOTYPE(int handle_args, (int argc, char *argv[]));
_PROTOTYPE(int start, (void));
_PROTOTYPE(int stop, (void));
_PROTOTYPE(int get, (void));
_PROTOTYPE(int reset, (void));
_PROTOTYPE(int create_named_pipe, (void));
_PROTOTYPE(int alloc_mem, (void));
_PROTOTYPE(int init_outfile, (void));
_PROTOTYPE(int write_outfile, (void));
_PROTOTYPE(int detach, (void));
int main(int argc, char *argv[])
{
int res;
if (res = handle_args(argc, argv)) {
switch(res) {
case ESYNTAX:
printf("Error in parameters.\n");
return 1;
break;
case EACTION:
printf("Specify one of start|stop|get|reset.\n");
return 1;
break;
case EMEM:
printf("Incorrect memory size.\n");
return 1;
break;
case EFREQ:
printf("Incorrect frequency.\n");
return 1;
break;
case EOUTFILE:
printf("Output filename missing.\n");
return 1;
break;
default:
break;
}
printf("Statistical Profiling:\n");
printf(" profile start [-m memsize] [-o outfile] [-f frequency]\n");
printf(" profile stop\n\n");
printf("Call Profiling:\n");
printf(" profile get [-m memsize] [-o outfile]\n");
printf(" profile reset\n\n");
printf(" - memsize in MB, default: %u\n", DEF_MEMSIZE);
printf(" - default output file: profile.{stat|call}.out\n");
printf( " - sample frequencies (default: %u):\n", DEF_FREQ);
printf(" 3 8192 Hz 10 64 Hz\n");
printf(" 4 4096 Hz 11 32 Hz\n");
printf(" 5 2048 Hz 12 16 Hz\n");
printf(" 6 1024 Hz 13 8 Hz\n");
printf(" 7 512 Hz 14 4 Hz\n");
printf(" 8 256 Hz 15 2 Hz\n");
printf(" 9 128 Hz\n\n");
printf("Use [sc]profalyze.pl to analyze output file.\n");
return 1;
}
switch(action) {
case START:
if (start()) return 1;
break;
case STOP:
if (stop()) return 1;
break;
case GET:
if (get()) return 1;
break;
case RESET:
if (reset()) return 1;
break;
default:
break;
}
return 0;
}
int handle_args(int argc, char *argv[])
{
while (--argc) {
++argv;
if (strcmp(*argv, "-h") == 0 || strcmp(*argv, "help") == 0 ||
strcmp(*argv, "--help") == 0) {
return EHELP;
} else
if (strcmp(*argv, "-m") == 0) {
if (--argc == 0) return ESYNTAX;
if (sscanf(*++argv, "%u", &mem_size) != 1 ||
mem_size < MIN_MEMSIZE ) return EMEM;
} else
if (strcmp(*argv, "-f") == 0) {
if (--argc == 0) return ESYNTAX;
if (sscanf(*++argv, "%u", &freq) != 1 ||
freq < MIN_FREQ || freq > MAX_FREQ) return EFREQ;
} else
if (strcmp(*argv, "-o") == 0) {
if (--argc == 0) return ESYNTAX;
outfile = *++argv;
} else
if (strcmp(*argv, "start") == 0) {
if (action) return EACTION;
action = START;
} else
if (strcmp(*argv, "stop") == 0) {
if (action) return EACTION;
action = STOP;
} else
if (strcmp(*argv, "get") == 0) {
if (action) return EACTION;
action = GET;
} else
if (strcmp(*argv, "reset") == 0) {
if (action) return EACTION;
action = RESET;
}
}
/* No action specified. */
if (!action) return EHELP;
/* Init unspecified parameters. */
if (action == START || action == GET) {
if (strcmp(outfile, "") == 0) outfile = DEF_OUTFILE;
if (mem_size == 0) mem_size = DEF_MEMSIZE;
mem_size *= MB; /* mem_size in bytes */
}
if (action == START) {
mem_size -= mem_size % sizeof(sprof_sample); /* align to sample size */
if (freq == 0) freq = DEF_FREQ; /* default frequency */
}
return 0;
}
int start()
{
/* This is the "starter process" for statistical profiling.
*
* Create output file for profiling data. Create named pipe to
* synchronize with stopper process. Fork so the parent can exit.
* Allocate memory for profiling data. Start profiling in kernel.
* Complete detachment from terminal. Write known string to named
* pipe, which blocks until read by stopper process. Redirect
* stdout/stderr to the named pipe. Write profiling data to file.
* Clean up.
*/
int log_fd;
if (init_outfile() || create_named_pipe()) return 1;
printf("Starting statistical profiling.\n");
if (fork() != 0) exit(0);
if (alloc_mem()) return 1;
if (sprofile(PROF_START, mem_size, freq, &sprof_info, mem_ptr)) {
perror("sprofile");
printf("Error starting profiling.\n");
return 1;
}
detach();
/* Temporarily redirect to system log to catch errors. */
log_fd = open(DEV_LOG, O_WRONLY);
dup2(log_fd, 1);
dup2(log_fd, 2);
if ((npipe_fd = open(NPIPE, O_WRONLY)) < 0) {
printf("Unable to open named pipe %s.\n", NPIPE);
return 1;
} else
/* Synchronize with stopper process. */
write(npipe_fd, SYNCING, strlen(SYNCING));
/* Now redirect to named pipe. */
dup2(npipe_fd, 1);
dup2(npipe_fd, 2);
mem_used = sprof_info.mem_used;
if (mem_used == -1) {
printf("WARNING: Profiling was stopped prematurely due to ");
printf("insufficient memory.\n");
printf("Try increasing available memory using the -m switch.\n");
}
if (write_outfile()) return 1;
close(log_fd);
close(npipe_fd);
unlink(NPIPE);
close(outfile_fd);
free(mem_ptr);
return 0;
}
int stop()
{
/* This is the "stopper" process for statistical profiling.
*
* Stop profiling in kernel. Read known string from named pipe
* to synchronize with starter proces. Read named pipe until EOF
* and write to stdout, this allows feedback from started process
* to be printed.
*/
int n;
char buf[BUFSIZE];
if (sprofile(PROF_STOP, 0, 0, 0, 0)) {
perror("sprofile");
printf("Error stopping profiling.\n");
return 1;
} else printf("Statistical profiling stopped.\n");
if ((npipe_fd = open(NPIPE, O_RDONLY)) < 0) {
printf("Unable to open named pipe %s.\n", NPIPE);
return 1;
} else
/* Synchronize with starter process. */
read(npipe_fd, buf, strlen(SYNCING));
while ((n = read(npipe_fd, buf, BUFSIZE)) > 0)
write(1, buf, n);
close(npipe_fd);
return 0;
}
int get()
{
/* Get function for call profiling.
*
* Create output file. Allocate memory. Perform system call to get
* profiling table and write it to file. Clean up.
*/
if (init_outfile()) return 1;
printf("Getting call profiling data.\n");
if (alloc_mem()) return 1;
if (cprofile(PROF_GET, mem_size, &cprof_info, mem_ptr)) {
perror("cprofile");
printf("Error getting data.\n");
return 1;
}
mem_used = cprof_info.mem_used;
if (mem_used == -1) {
printf("ERROR: unable to get data due to insufficient memory.\n");
printf("Try increasing available memory using the -m switch.\n");
} else
if (cprof_info.err) {
printf("ERROR: the following error(s) happened during profiling:\n");
if (cprof_info.err & CPROF_CPATH_OVERRUN)
printf(" call path overrun\n");
if (cprof_info.err & CPROF_STACK_OVERRUN)
printf(" call stack overrun\n");
if (cprof_info.err & CPROF_TABLE_OVERRUN)
printf(" hash table overrun\n");
printf("Try changing values in /usr/src/include/minix/profile.h ");
printf("and then rebuild the system.\n");
} else
if (write_outfile()) return 1;
close(outfile_fd);
free(mem_ptr);
return 0;
}
int reset()
{
/* Reset function for call profiling.
*
* Perform system call to reset profiling table.
*/
printf("Resetting call profiling data.\n");
if (cprofile(PROF_RESET, 0, 0, 0)) {
perror("cprofile");
printf("Error resetting data.\n");
return 1;
}
return 0;
}
int alloc_mem()
{
if ((mem_ptr = malloc(mem_size)) == 0) {
printf("Unable to allocate memory.\n");
printf("Used chmem to increase available proces memory?\n");
return 1;
} else memset(mem_ptr, '\0', mem_size);
return 0;
}
int init_outfile()
{
if ((outfile_fd = open(outfile, O_CREAT | O_TRUNC | O_WRONLY)) <= 0) {
printf("Unable to create outfile %s.\n", outfile);
return 1;
} else chmod(outfile, S_IRUSR | S_IWUSR);
return 0;
}
int create_named_pipe()
{
if ((mkfifo(NPIPE, S_IRUSR | S_IWUSR) == -1) && (errno != EEXIST)) {
printf("Unable to create named pipe %s.\n", NPIPE);
return 1;
} else
return 0;
}
int detach()
{
setsid();
(void) chdir("/");
close(0);
close(1);
close(2);
}
int write_outfile()
{
int n, towrite, written = 0;
char *buf = mem_ptr;
char header[80];
printf("Writing to %s ...", outfile);
/* Write header. */
if (SPROF)
sprintf(header, "stat\n%d %d %d %d\n", sprof_info.total_samples,
sprof_info.idle_samples,
sprof_info.system_samples,
sprof_info.user_samples);
else
sprintf(header, "call\n%u %u\n",
CPROF_CPATH_MAX_LEN, CPROF_PROCNAME_LEN);
n = write(outfile_fd, header, strlen(header));
if (n < 0) { printf("Error writing to outfile %s.\n", outfile); return 1; }
/* Write data. */
towrite = mem_used == -1 ? mem_size : mem_used;
while (towrite > 0) {
n = write(outfile_fd, buf, towrite);
if (n < 0)
{ printf("Error writing to outfile %s.\n", outfile); return 1; }
towrite -= n;
buf += n;
written += n;
}
printf(" header %d bytes, data %d bytes.\n", strlen(header), written);
return 0;
}

349
commands/profile/sprofalyze.pl Executable file
View file

@ -0,0 +1,349 @@
#!/usr/local/bin/perl
#
# sprofalyze.pl
#
# Analyzes the output files created by the profile command for
# Statistical Profiling.
#
# Changes:
# 14 Aug, 2006 Created (Rogier Meurs)
#
# Configuration options:
# Location and parameters of nm program to extract symbol tables
$nm = "/usr/bin/nm -dn";
# Location of src (including trailing /)
$src_root = qw(
/usr/src/
);
# Location of system executables within src. Add new servers/drivers here.
@exes = qw(
kernel/kernel
servers/ds/ds
servers/vfs/vfs
servers/mfs/mfs
servers/inet/inet
servers/is/is
servers/pm/pm
servers/rs/rs
servers/rs/service
drivers/at_wini/at_wini
drivers/bios_wini/bios_wini
drivers/cmos/cmos
drivers/dp8390/dp8390
drivers/dpeth/dpeth
drivers/floppy/floppy
drivers/fxp/fxp
drivers/lance/lance
drivers/log/log
drivers/memory/memory
drivers/pci/pci
drivers/printer/printer
drivers/random/random
drivers/rtl8139/rtl8139
drivers/sb16/sb16_dsp
drivers/sb16/sb16_mixer
drivers/ti1225/ti1225
drivers/tty/tty
);
# 8< ----------- no user configurable parameters below this line ----------- >8
$SAMPLE_SIZE = 12;
$MINIMUM_PERC = 1.0;
if ($#ARGV < 0 || process_args(@ARGV)) {
print "Usage:\n";
print " sprofalyze.pl [-p percentage] file ...\n\n";
print " percentage print only processes/functions >= percentage\n";
exit 1;
}
sub process_args {
$_ = shift;
if (/^-p$/) {
$_ = shift;
return 1 unless /^(\d{1,2})(.\d+)?$/;
$MINIMUM_PERC = $1 + $2;
} else {
unshift @_, $_;
}
@files = @_;
return 1 unless @files > 0;
return 0;
}
if (read_symbols()) { exit 1; }
print "Showing processes and functions using at least $MINIMUM_PERC% time.\n";
foreach $file (@files) {
if (process_datafile($file)) { exit 1; }
}
exit 0;
sub read_symbols
{
print "Building indexes from symbol tables:";
for ($i=0; $i<= $#exes; $i++) {
my $exe = @exes[$i];
$shortname = $exe;
$shortname =~ s/^.*\///;
print " " if $i <= $#exes;
print $shortname;
$fullname = $src_root . $exe;
if ((! -x $fullname) || (! -r $fullname)) {
print "\nERROR: $fullname does not exist or not readable.\n";
print "Did you do a make?\n";
return 1;
}
# Create a hash entry for each symbol table (text) entry.
foreach $_ (`$nm $fullname`) {
if (/^0{0,7}(\d{0,8})\s[tT]\s(\w{1,8})\n$/) {
${$shortname."_hash"}{$1} = $2;
}
}
# Create hash entries for every possible pc value. This will pay off.
@lines = sort { $a <=> $b } keys %{$shortname."_hash"};
for ($y = 0; $y <= $#lines; $y++) {
for ($z = @lines->[$y] + 1; $z < @lines->[$y + 1]; $z++) {
${$shortname."_hash"}{$z} =
${$shortname."_hash"}{@lines->[$y]}
}
}
}
# Clock and system are in kernel image but are seperate processes.
push(@exes, "clock");
push(@exes, "system");
%clock_hash = %kernel_hash;
%system_hash = %kernel_hash;
print ".\n\n";
return 0;
}
sub process_datafile
{
my %res = ();
my %merged = ();
my $file = shift;
my $buf, $pc, $exe, $total_system_perc;
unless (open(FILE, $file)) {
print "\nERROR: Unable to open $file: $!\n";
return 0;
}
# First line: check file type.
$_ = <FILE>; chomp;
if (!/^stat$/) {
if (/^call$/) {
print "Call Profiling output file: ";
print "Use cprofalyze.pl instead.\n";
} else {
print "Not a profiling output file.\n";
}
return 0;
}
$file =~ s/^.*\///;
printf "\n========================================";
printf "========================================\n";
printf("Data file: %s\n", $file);
printf "========================================";
printf "========================================\n\n";
# Read header with total, idle, system and user hits.
$_ = <FILE>;
chomp;
($total_hits, $idle_hits, $system_hits, $user_hits) = split (/ /);
my $system_perc = sprintf("%3.f", $system_hits / $total_hits * 100);
my $user_perc = sprintf("%3.f", $user_hits / $total_hits * 100);
my $idle_perc = 100 - $system_perc - $user_perc;
printf(" System process ticks: %10d (%3d%%)\n", $system_hits, $system_perc);
printf(" User process ticks: %10d (%3d%%)", $user_hits, $user_perc);
printf(" Details of system process\n");
printf(" Idle time ticks: %10d (%3d%%)", $idle_hits, $idle_perc);
printf(" samples, aggregated and\n");
printf(" ---------- ----");
printf(" per process, are below.\n");
printf(" Total ticks: %10d (100%)\n\n", $total_hits);
# Read sample records from file and increase relevant counters.
until (eof(FILE)) {
read(FILE, $buf, $SAMPLE_SIZE) == $SAMPLE_SIZE or die ("Short read.");
($exe, $pc) = unpack("Z8i", $buf);
# p_name "mem" refers to executable "memory".
$exe =~ s/^mem/memory/;
# We can access the hash by pc because they are all in there.
if (!defined(${$exe."_hash"}{$pc})) {
print "ERROR: Undefined in symbol table indexes: ";
print "executable $exe address $pc\n";
print "Did you include this executable in the configuration?\n";
return 1;
}
$res{$exe}{${$exe."_hash"}{$pc}} ++;
}
# We only need to continue with executables that had any hits.
my @actives = ();
foreach my $exe (@exes) {
$exe =~ s/^.*\///;
next if (!exists($res{$exe}));
push(@actives, $exe);
}
# Calculate number of samples for each executable and create aggregated hash.
%exe_hits = ();
foreach $exe (@actives) {
foreach my $hits (values %{$res{$exe}}) {
$exe_hits{$exe} += $hits;
}
foreach my $key (keys %{$res{$exe}}) {
$merged{sprintf("%8s %8s", $exe, $key)} = $res{$exe}{$key};
}
}
$total_system_perc = 0;
# Print the aggregated results.
process_hash("", \%merged);
# Print results for each executable in decreasing order.
foreach my $exe
(reverse sort { $exe_hits{$a} <=> $exe_hits{$b} } keys %exe_hits)
{
process_hash($exe, \%{$res{$exe}}) if
$exe_hits{$exe} >= $system_hits / 100 * $MINIMUM_PERC;
}
# Print total of processes <threshold.
printf "----------------------------------------";
printf "----------------------------------------\n";
printf("%-47s %5.1f%% of system process samples\n",
"processes <$MINIMUM_PERC% (not showing functions)",
100 - $total_system_perc);
printf "----------------------------------------";
printf "----------------------------------------\n";
printf("%-47s 100.0%%\n\n", "total");
close(FILE);
return 0;
}
sub process_hash
{
my $exe = shift;
my $ref = shift;
%hash = %{$ref};
my $aggr = $exe eq "";
# Print a header.
printf "----------------------------------------";
printf "----------------------------------------\n";
if ($aggr) {
$astr_max = 55;
$perc_hits = $system_hits / 100;
printf("Total system process time %46d samples\n", $system_hits);
} else {
$astr_max = 64;
$perc_hits = $exe_hits{$exe} / 100;
$total_system_perc += $exe_perc =
sprintf("%5.1f", $exe_hits{$exe} / $system_hits * 100);
printf("%-47s %5.1f%% of system process samples\n", $exe, $exe_perc);
}
printf "----------------------------------------";
printf "----------------------------------------\n";
# Delete functions <threshold. Get percentage for all functions >threshold.
my $below_thres_hits;
my $total_exe_perc;
foreach my $func (keys %hash)
{
if ($hash{$func} < $perc_hits * $MINIMUM_PERC) {
$below_thres_hits += $hash{$func};
delete($hash{$func});
} else {
$total_exe_perc += sprintf("%3.1f", $hash{$func} / $perc_hits);
}
}
# Now print the hash entries in decreasing order.
@sorted = reverse sort { $hash{$a} <=> $hash{$b} } keys %hash;
$astr_hits = ($hash{@sorted->[0]} > $below_thres_hits ?
$hash{@sorted->[0]} : $below_thres_hits) / $astr_max;
foreach $func (@sorted) {
process_line($func, $hash{$func});
}
# Print <threshold hits.
my $below_thres;
if ($aggr) { $below_thres = " "; }
$below_thres .= sprintf("%8s", "<" . $MINIMUM_PERC . "%");
process_line($below_thres, $below_thres_hits, 100 - $total_exe_perc);
# Print footer.
printf "----------------------------------------";
printf "----------------------------------------\n";
printf("%-73s 100.0%%\n\n", $aggr ? "total" : $exe);
}
sub process_line
{
my $func = shift;
my $hits = shift;
my $perc = $hits / $perc_hits;
my $astr = $hits / $astr_hits;
if ($aggr) {
print "$func ";
} else {
printf("%8s ", $func);
}
for ($i = 0; $i < $astr_max; $i++) {
if ($i <= $astr) {
print "*";
} else {
print " ";
}
}
print " ";
if (my $rest = shift) {
printf("%5.1f%%\n", $rest);
} else {
printf("%5.1f%%\n", $perc);
}
}

View file

@ -0,0 +1,27 @@
#!/usr/local/bin/perl
$SAMPLE_SZ = 12;
$file = shift;
open (FILE, $file) or die ("Unable to open $file: $!");
$_ = <FILE>;
until (eof(FILE)) {
read(FILE, $buf, $SAMPLE_SZ) == $SAMPLE_SZ or die ("Short read.");
($exe, $pc) = unpack("A8i", $buf);
# System and clock task are in kernel image.
# $exe =~ s/^system/kernel/;
# $exe =~ s/^clock/kernel/;
# Memory has p_name "mem" in kernel.
# $exe =~ s/^mem/memory/;
$pcs{$pc}++ if ($exe eq "fs");
}
foreach $pc (sort { $a <=> $b } keys %pcs) {
print "$pc $pcs{$pc}\n";
}

View file

@ -0,0 +1,33 @@
#!/usr/local/bin/perl
$SAMPLE_SZ = 8;
$file = shift;
open (FILE, $file) or die ("Unable to open $file: $!");
$_ = <FILE>;
until (eof(FILE)) {
read(FILE, $buf, $SAMPLE_SZ) == $SAMPLE_SZ or die ("Short read.");
($high, $low) = unpack("II", $buf);
if ($high - $prevhigh == 0) {
push (@res, $low - $prevlow);
}
$prevhigh = $high;
$prevlow = $low;
#print "$high $low\n";
# $pcs{$pc}++ if ($exe eq "kernel");
}
foreach $diff (sort { $a <=> $b } @res) {
print $diff."\n";
}
#foreach $pc (sort { $a <=> $b } keys %pcs) {
# print "$pc $pcs{$pc}\n";
#}

View file

@ -13,7 +13,7 @@ p = ../libpci
# programs, flags, etc.
MAKE = exec make
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys -ltimers
@ -25,7 +25,7 @@ LIBDRIVER = $d/libdriver/driver.o $d/libdriver/drvlib.o
all build: $(DRIVER)
$(DRIVER): $(OBJ) $(LIBDRIVER)
$(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBDRIVER) $(LIBS)
install -S 8k $(DRIVER)
install -S 32k $(DRIVER)
$(LIBDRIVER):
cd $d/libdriver && $(MAKE)

View file

@ -12,7 +12,7 @@ d = ..
# programs, flags, etc.
MAKE = exec make
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys -ltimers

View file

@ -12,7 +12,7 @@ d = ..
# programs, flags, etc.
MAKE = exec make
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys

View file

@ -11,7 +11,7 @@ d = ..
# programs, flags, etc.
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys -ltimers

View file

@ -1,29 +1,6 @@
##
## Makefile for ISA ethernet drivers May 02, 2000
##
## $Log$
## Revision 1.5 2006/08/02 22:51:46 beng
## At least 8k stack for all drivers so that malloc() works, for grants,
## for printf().
##
## Revision 1.4 2006/07/10 12:43:38 philip
## Safecopy support in ethernet drivers.
##
## Revision 1.3 2005/07/19 13:21:48 jnherder
## Renamed src/lib/utils to src/lib/sysutil --- because of new src/lib/util
##
## Revision 1.2 2005/07/19 12:12:47 jnherder
## Changed Makefiles: drivers are now installed in /usr/sbin.
## TTY now gets SYS_EVENT message with sigset (e.g., SIGKMESS, SIGKSTOP).
##
## Revision 1.1 2005/06/29 10:16:46 beng
## Import of dpeth 3c501/3c509b/.. ethernet driver by
## Giovanni Falzoni <fgalzoni@inwind.it>.
##
## Revision 2.0 2005/06/26 16:16:46 lsodgf0
## Initial revision for Minix 3.0.6
##
## $Id$
## Programs, flags, etc.
DRIVER = dpeth
@ -32,8 +9,8 @@ debug = 0
CC = exec cc
LD = $(CC)
CPPFLAGS= -I.. -I/usr/include -Ddebug=$(debug)
CFLAGS = $(CPPFLAGS)
CPPFLAGS= -I.. -I/usr/include -Ddebug=$(debug) $(CPROFILE)
CFLAGS = -ws $(CPPFLAGS)
LDFLAGS = -i -o $@
SRCS = 3c501.c 3c509.c 3c503.c ne.c wd.c 8390.c devio.c netbuff.c dp.c

View file

@ -12,7 +12,7 @@ d = ..
# programs, flags, etc.
MAKE = exec make
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys -ltimers

View file

@ -11,7 +11,7 @@ d = ..
# programs, flags, etc.
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys -ltimers

View file

@ -11,7 +11,7 @@ d = ..
# programs, flags, etc.
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys
#-lutils -ltimers

View file

@ -9,7 +9,7 @@ m = $i/minix
# Programs, flags, etc.
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys

View file

@ -11,9 +11,9 @@ d = ..
# programs, flags, etc.
MAKE = exec make
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsys -lsysutil
LIBS = -lsysutil -lsys
OBJ = log.o diag.o kputc.o
LIBDRIVER = $d/libdriver/driver.o

View file

@ -1,5 +1,5 @@
boot
210 250
280 310
d--755 0 0
bin d--755 0 0
at_wini ---755 0 0 at_wini

View file

@ -11,7 +11,7 @@ d = ..
# programs, flags, etc.
CC = cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys -ltimers

View file

@ -11,7 +11,7 @@ d = ..
# programs, flags, etc.
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys

View file

@ -12,7 +12,7 @@ d = ..
# programs, flags, etc.
MAKE = exec make
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys

View file

@ -12,7 +12,7 @@ d = ..
# programs, flags, etc.
MAKE = exec make
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsys -lsysutil

View file

@ -12,7 +12,7 @@ d = ..
# programs, flags, etc.
MAKE = exec make
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys -ltimers

View file

@ -10,7 +10,7 @@ d = ..
# programs, flags, etc.
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys

View file

@ -11,7 +11,7 @@ d = ..
# programs, flags, etc.
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys -ltimers

View file

@ -5,7 +5,7 @@ LK = /usr/lib/keymaps
.SUFFIXES: .src .map
.src.map:
$(CC) -DKEYSRC=\"$<\" genmap.c
$(CC) -DKEYSRC=\"$<\" $(CPROFILE) genmap.c -lsysutil -lsys
./a.out > $@
@rm -f a.out

View file

@ -46,6 +46,7 @@
#define XT_WINI_IRQ 5 /* xt winchester */
#define FLOPPY_IRQ 6 /* floppy disk */
#define PRINTER_IRQ 7
#define CMOS_CLOCK_IRQ 8
#define KBD_AUX_IRQ 12 /* AUX (PS/2 mouse) port in kbd controller */
#define AT_WINI_0_IRQ 14 /* at winchester controller 0 */
#define AT_WINI_1_IRQ 15 /* at winchester controller 1 */

View file

@ -1,4 +1,4 @@
#define NCALLS 98 /* number of system calls allowed */
#define NCALLS 100 /* number of system calls allowed */
#define EXIT 1
#define FORK 2
@ -78,6 +78,8 @@
#define FCHMOD 95 /* to FS */
#define FCHOWN 96 /* to FS */
#define GETSYSINFO_UP 97 /* to PM or FS */
#define SPROF 98 /* to PM */
#define CPROF 99 /* to PM */
/* Calls provided by PM and FS that are not part of the API */
#define EXEC_NEWMEM 100 /* from FS or RS to PM: new memory map for

View file

@ -308,7 +308,11 @@
# define SYS_SETGRANT (KERNEL_CALL + 34) /* sys_setgrant() */
# define SYS_READBIOS (KERNEL_CALL + 35) /* sys_readbios() */
#define NR_SYS_CALLS 36 /* number of system calls */
# define SYS_SPROF (KERNEL_CALL + 36) /* sys_sprof() */
# define SYS_CPROF (KERNEL_CALL + 37) /* sys_cprof() */
# define SYS_PROFBUF (KERNEL_CALL + 38) /* sys_profbuf() */
#define NR_SYS_CALLS 39 /* number of system calls */
/* Pseudo call for use in kernel/table.c. */
#define SYS_ALL_CALLS (NR_SYS_CALLS)
@ -523,6 +527,14 @@
#define SEL_ERRORFDS m8_p3
#define SEL_TIMEOUT m8_p4
/* Field names for SYS_SPROF, _CPROF, _PROFBUF. */
#define PROF_ACTION m7_i1 /* start/stop/reset/get */
#define PROF_MEM_SIZE m7_i2 /* available memory for data */
#define PROF_FREQ m7_i3 /* sample frequency */
#define PROF_ENDPT m7_i4 /* endpoint of caller */
#define PROF_CTL_PTR m7_p1 /* location of info struct */
#define PROF_MEM_PTR m7_p2 /* location of profiling data */
/* Field names for GETSYSINFO_UP (PM). */
#define SIU_WHAT m2_i1
#define SIU_LEN m2_i2

View file

@ -127,4 +127,8 @@
#define ASKDEV _ASKDEV
#define FASTLOAD _FASTLOAD
/* Enable or disable system profiling. */
#define SPROFILE 0 /* statistical profiling */
#define CPROFILE 0 /* call profiling */
#endif /* _CONFIG_H */

109
include/minix/profile.h Normal file
View file

@ -0,0 +1,109 @@
#ifndef _PROFILE_H
#define _PROFILE_H
#include <ansi.h>
/*
* Types relating to system profiling. Types are supplied for both
* statistical profiling and call profiling.
*/
#if SPROFILE
# define PROF_START 0 /* start statistical profiling */
# define PROF_STOP 1 /* stop statistical profiling */
/* Info struct to be copied to from kernel to user program. */
struct sprof_info_s {
int mem_used;
int total_samples;
int idle_samples;
int system_samples;
int user_samples;
} sprof_info_inst;
/* What a profiling sample looks like (used for sizeof()). */
struct {
char name[8];
int pc;
} sprof_sample;
#endif /* SPROFILE */
#if CPROFILE
#include <sys/types.h>
# define PROF_GET 2 /* get call profiling tables */
# define PROF_RESET 3 /* reset call profiling tables */
/* Hash table size in each profiled process is table size + index size.
*
* Table size = CPROF_TABLE_SIZE * (CPROF_CPATH_MAX_LEN + 16).
* Index size = CPROF_INDEX_SIZE * 4;
*
* Making CPROF_CPATH_MAX_LEN too small may cause call path overruns.
* Making CPROF_TABLE_SIZE too small may cause table overruns.
*
* There are some restrictions: processes in the boot image are loaded
* below 16 MB and the kernel is loaded in lower memory (below 640 kB). The
* latter is reason to use a different size for the kernel table.
*/
#define CPROF_TABLE_SIZE_OTHER 3000 /* nr of slots in hash table */
#define CPROF_TABLE_SIZE_KERNEL 1500 /* kernel has a smaller table */
#define CPROF_CPATH_MAX_LEN 256 /* len of cpath string field: */
/* MUST BE MULTIPLE OF WORDSIZE */
#define CPROF_INDEX_SIZE (10*1024)/* size of index to hash table */
#define CPROF_STACK_SIZE 24 /* size of call stack */
#define CPROF_PROCNAME_LEN 8 /* len of proc name field */
#define CPROF_CPATH_OVERRUN 0x1 /* call path overrun */
#define CPROF_STACK_OVERRUN 0x2 /* call stack overrun */
#define CPROF_TABLE_OVERRUN 0x4 /* hash table overrun */
#define CPROF_ANNOUNCE_OTHER 1 /* processes announce their profiling
* data on n-th entry of procentry */
#define CPROF_ACCOUNCE_KERNEL 10000 /* kernel announces not directly */
/* Prototype for function called by procentry to get size of table. */
_PROTOTYPE(int profile_get_tbl_size, (void) );
/* Prototype for function called by procentry to get announce number. */
_PROTOTYPE(int profile_get_announce, (void) );
/* Prototype for function called by procentry to announce control struct
* and table locations to the kernel. */
_PROTOTYPE(void profile_register, (void *ctl_ptr, void *tbl_ptr) );
/* Info struct to be copied from kernel to user program. */
struct cprof_info_s {
int mem_used;
int err;
} cprof_info_inst;
/* Data structures for control structure and profiling data table in the
* in the profiled processes.
*/
struct cprof_ctl_s {
int reset; /* kernel sets to have table reset */
int slots_used; /* proc writes nr slots used in table */
int err; /* proc writes errors that occurred */
} cprof_ctl_inst;
struct cprof_tbl_s {
struct cprof_tbl_s *next; /* next in chain */
char cpath[CPROF_CPATH_MAX_LEN]; /* string with call path */
int calls; /* nr of executions of path */
u64_t cycles; /* execution time of path, in cycles */
} cprof_tbl_inst;
#endif /* CPROFILE */
_PROTOTYPE( int sprofile, (int action, int size, int freq,
void *ctl_ptr, void *mem_ptr) );
_PROTOTYPE( int cprofile, (int action, int size, void *ctl_ptr,
void *mem_ptr) );
#endif /* PROFILE_H */

View file

@ -197,5 +197,13 @@ _PROTOTYPE( char *pci_dev_name, (U16_t vid, U16_t did) );
_PROTOTYPE( char *pci_slot_name, (int devind) );
_PROTOTYPE( int pci_set_acl, (struct rs_pci *rs_pci) );
/* Profiling. */
_PROTOTYPE( int sys_sprof, (int action, int size, int freq, int endpt,
void *ctl_ptr, void *mem_ptr) );
_PROTOTYPE( int sys_cprof, (int action, int size, int endpt,
void *ctl_ptr, void *mem_ptr) );
_PROTOTYPE( int sys_profbuf, (void *ctl_ptr, void *mem_ptr) );
#endif /* _SYSLIB_H */

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 },

View file

@ -7,6 +7,7 @@ LIBRARIES=libc
libc_FILES=" \
_brk.c \
_sbrk.c \
_cprofile.c \
_devctl.c \
__pm_findproc.c \
_getnpid.c \
@ -18,6 +19,7 @@ libc_FILES=" \
_reboot.c \
_seekdir.c \
_sysuname.c \
_sprofile.c \
_svrctl.c \
asynchio.c \
basename.c \

16
lib/other/_cprofile.c Normal file
View file

@ -0,0 +1,16 @@
#include <lib.h>
#define cprofile _cprofile
PUBLIC int cprofile(int action, int size, char *ctl_ptr, int *mem_ptr)
{
message m;
m.PROF_ACTION = action;
m.PROF_MEM_SIZE = size;
m.PROF_CTL_PTR = (void *) ctl_ptr;
m.PROF_MEM_PTR = (void *) mem_ptr;
return _syscall(MM, CPROF, &m);
}

17
lib/other/_sprofile.c Normal file
View file

@ -0,0 +1,17 @@
#include <lib.h>
#define sprofile _sprofile
PUBLIC int sprofile(int action, int size, int freq, char *ctl_ptr, int *mem_ptr)
{
message m;
m.PROF_ACTION = action;
m.PROF_MEM_SIZE = size;
m.PROF_FREQ = freq;
m.PROF_CTL_PTR = (void *) ctl_ptr;
m.PROF_MEM_PTR = (void *) mem_ptr;
return _syscall(MM, SPROF, &m);
}

View file

@ -18,6 +18,7 @@ libc_FILES=" \
chroot.s \
close.s \
closedir.s \
cprofile.s \
creat.s \
devctl.s \
dup.s \
@ -92,6 +93,7 @@ libc_FILES=" \
sigreturn.s \
sigsuspend.s \
sleep.s \
sprofile.s \
stat.s \
stime.s \
svrctl.s \

7
lib/syscall/cprofile.s Executable file
View file

@ -0,0 +1,7 @@
.sect .text
.extern __cprofile
.define _cprofile
.align 2
_cprofile:
jmp __cprofile

7
lib/syscall/sprofile.s Executable file
View file

@ -0,0 +1,7 @@
.sect .text
.extern __sprofile
.define _sprofile
.align 2
_sprofile:
jmp __sprofile

View file

@ -26,6 +26,7 @@ libsys_FILES=" \
pci_slot_name.c \
safecopies.c \
sys_abort.c \
sys_cprof.c \
sys_endsig.c \
sys_eniop.c \
sys_exec.c \
@ -45,6 +46,7 @@ libsys_FILES=" \
sys_readbios.c \
sys_safecopy.c \
sys_vsafecopy.c \
sys_profbuf.c \
sys_sdevio.c \
sys_segctl.c \
sys_setalarm.c \
@ -52,6 +54,7 @@ libsys_FILES=" \
sys_sigsend.c \
sys_privctl.c \
sys_setgrant.c \
sys_sprof.c \
sys_times.c \
sys_trace.c \
sys_umap.c \

27
lib/syslib/sys_cprof.c Normal file
View file

@ -0,0 +1,27 @@
#include "syslib.h"
#if CPROFILE
/*===========================================================================*
* sys_cprof *
*===========================================================================*/
PUBLIC int sys_cprof(action, size, endpt, ctl_ptr, mem_ptr)
int action; /* get/reset profiling tables */
int size; /* size of allocated memory */
int endpt; /* caller endpoint */
void *ctl_ptr; /* location of info struct */
void *mem_ptr; /* location of allocated memory */
{
message m;
m.PROF_ACTION = action;
m.PROF_MEM_SIZE = size;
m.PROF_ENDPT = endpt;
m.PROF_CTL_PTR = ctl_ptr;
m.PROF_MEM_PTR = mem_ptr;
return(_taskcall(SYSTASK, SYS_CPROF, &m));
}
#endif

23
lib/syslib/sys_profbuf.c Normal file
View file

@ -0,0 +1,23 @@
#include "syslib.h"
#include <minix/config.h>
#if CPROFILE
/*===========================================================================*
* sys_profbuf *
*===========================================================================*/
PUBLIC int sys_profbuf(ctl_ptr, mem_ptr)
void *ctl_ptr; /* pointer to control structure */
void *mem_ptr; /* pointer to profiling table */
{
message m;
m.PROF_CTL_PTR = ctl_ptr;
m.PROF_MEM_PTR = mem_ptr;
return(_taskcall(SYSTASK, SYS_PROFBUF, &m));
}
#endif

29
lib/syslib/sys_sprof.c Normal file
View file

@ -0,0 +1,29 @@
#include "syslib.h"
#if SPROFILE
/*===========================================================================*
* sys_sprof *
*===========================================================================*/
PUBLIC int sys_sprof(action, size, freq, endpt, ctl_ptr, mem_ptr)
int action; /* start/stop profiling */
int size; /* available profiling memory */
int freq; /* sample frequency */
int endpt; /* caller endpoint */
void *ctl_ptr; /* location of info struct */
void *mem_ptr; /* location of profiling memory */
{
message m;
m.PROF_ACTION = action;
m.PROF_MEM_SIZE = size;
m.PROF_FREQ = freq;
m.PROF_ENDPT = endpt;
m.PROF_CTL_PTR = ctl_ptr;
m.PROF_MEM_PTR = mem_ptr;
return(_taskcall(SYSTASK, SYS_SPROF, &m));
}
#endif

View file

@ -16,6 +16,9 @@ libsysutil_FILES=" \
env_prefix.c \
fkey_ctl.c \
report.c \
taskcall.c"
taskcall.c \
read_tsc.s \
profile_extern.c \
profile.c"
TYPE=both

289
lib/sysutil/profile.c Normal file
View file

@ -0,0 +1,289 @@
/*
* profile.c - library functions for call profiling
*
* For processes that were compiled using ACK with the -Rcem-p option,
* procentry and procexit will be called on entry and exit of their
* functions. Procentry/procexit are implemented here as generic library
* functions.
*
* Changes:
* 14 Aug, 2006 Created (Rogier Meurs)
*/
#include <lib.h>
#if CPROFILE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <minix/profile.h>
#include <minix/syslib.h>
#include <minix/u64.h>
#include "read_tsc.h"
#define U64_LO 0
#define U64_HI 1
PRIVATE char cpath[CPROF_CPATH_MAX_LEN]; /* current call path string */
PRIVATE int cpath_len; /* current call path len */
PRIVATE struct cprof_tbl_s *cprof_slot; /* slot of current function */
PRIVATE struct stack_s { /* stack entry */
int cpath_len; /* call path len */
struct cprof_tbl_s *slot; /* table slot */
u64_t start_1; /* count @ begin of procentry */
u64_t start_2; /* count @ end of procentry */
u64_t spent_deeper; /* spent in called functions */
};
PRIVATE struct stack_s cprof_stk[CPROF_STACK_SIZE]; /* stack */
PRIVATE int cprof_stk_top; /* top of stack */
EXTERN struct cprof_tbl_s cprof_tbl[]; /* hash table */
PRIVATE int cprof_tbl_size; /* nr of slots */
PRIVATE struct cprof_tbl_s *idx[CPROF_INDEX_SIZE]; /* index to table */
PRIVATE struct cprof_ctl_s control; /* for comms with kernel */
PRIVATE int cprof_announce; /* announce on n-th execution
* of procentry */
PRIVATE int cprof_locked; /* for reentrancy */
_PROTOTYPE(void procentry, (char *name) );
_PROTOTYPE(void procexit, (char *name) );
FORWARD _PROTOTYPE(void cprof_init, (void) );
FORWARD _PROTOTYPE(void reset, (void) );
FORWARD _PROTOTYPE(void clear_tbl, (void) );
PUBLIC void procentry (name)
char *name;
{
static int init = 0;
unsigned hash = 0, i = 0, x = 0;
unsigned long hi, lo;
struct cprof_tbl_s *last;
char c;
u64_t start;
/* Procentry is not reentrant. */
if (cprof_locked) return; else cprof_locked = 1;
/* Read CPU cycle count into local variable. */
read_tsc(&start._[U64_HI], &start._[U64_LO]);
/* Run init code once after system boot. */
if (init == 0) {
cprof_init();
init++;
}
/* Announce once. */
if (init > -1 && init++ == cprof_announce) {
/* Tell kernel about control structure and table locations.
*
* In userspace processes, the library function profile_register
* will be used. This function does a kernel call (sys_profbuf) to
* announce to the kernel the location of the control struct and
* hash table. The control struct is used by the kernel to write
* a flag if resetting of the table is requested. The location of
* the table is needed to copy the information to the user process
* that requests it.
*
* Kernelspace processes don't use the library function but have
* their own implemention that executes logic similar to sys_profbuf.
* The reason for this is that the kernel is non-reentrant, therefore
* a kernelspace process is not able to do a kernel call itself since
* this would cause a deadlock.
*/
profile_register((void *) &control, (void *) &cprof_tbl);
init = -1;
}
/* Only continue if sane. */
if (control.err) return;
/* Check if kernel instructed to reset profiling data. */
if (control.reset) reset();
/* Increase stack. */
if (++cprof_stk_top == CPROF_STACK_SIZE) {
printf("CPROFILE error: stack overrun\n");
control.err |= CPROF_STACK_OVERRUN;
return;
}
/* Save initial cycle count on stack. */
cprof_stk[cprof_stk_top].start_1._[U64_HI] = start._[U64_HI];
cprof_stk[cprof_stk_top].start_1._[U64_LO] = start._[U64_LO];
/* Check available call path len. */
if (cpath_len + strlen(name) + 1 > CPROF_CPATH_MAX_LEN) {
printf("CPROFILE error: call path overrun\n");
control.err |= CPROF_CPATH_OVERRUN;
return;
}
/* Save previous call path length on stack. */
cprof_stk[cprof_stk_top].cpath_len = cpath_len;
/* Generate new call path string and length.*/
if (cprof_stk_top > 0) /* Path is space separated. */
cpath[cpath_len++] = ' ';
while ((c = *(name++)) != '\0') /* Append function name. */
cpath[cpath_len++] = c;
cpath[cpath_len] = '\0'; /* Null-termination. */
/* Calculate hash for call path string (algorithm: ELF). */
for (i=0; i<cpath_len; i++) {
hash = (hash << 4) + cpath[i];
if ((x = hash & 0xF0000000L) != 0) {
hash ^= (x >> 24);
hash &= ~x;
}
}
hash %= CPROF_INDEX_SIZE;
/* Look up the slot for this call path in the hash table. */
for (cprof_slot = idx[hash]; cprof_slot != 0; cprof_slot = cprof_slot->next)
if (strcmp(cprof_slot->cpath, cpath) == 0) break;
if (cprof_slot)
cprof_slot->calls++; /* found slot: update call counter */
else {
/* Not found: insert path into hash table. */
if (control.slots_used == cprof_tbl_size) {
printf("CPROFILE error: table overrun\n");
control.err |= CPROF_TABLE_OVERRUN;
return;
}
/* Set values for new slot. */
cprof_slot = &cprof_tbl[control.slots_used++];
strcpy(cprof_slot->cpath, cpath);
cprof_slot->calls = 1;
/* Update index. */
if (idx[hash] == 0) {
/* No collision: simple update. */
idx[hash] = cprof_slot;
} else {
/* Collision: update last in chain. */
for (last = idx[hash]; last->next != 0; last = last->next);
last->next = cprof_slot;
}
}
/* Save slot on stack. */
cprof_stk[cprof_stk_top].slot = cprof_slot;
/* Again save CPU cycle count on stack. */
read_tsc(&cprof_stk[cprof_stk_top].start_2._[U64_HI],
&cprof_stk[cprof_stk_top].start_2._[U64_LO]);
cprof_locked = 0;
}
PUBLIC void procexit (name)
char *name;
{
u64_t stop, spent;
/* Procexit is not reentrant. */
if (cprof_locked) return; else cprof_locked = 1;
/* First thing: read CPU cycle count into local variable. */
read_tsc(&stop._[U64_HI], &stop._[U64_LO]);
/* Only continue if sane. */
if (control.err) return;
/* Update cycle count for this call path. Exclude time spent in procentry/
* procexit by using measurements taken at end of procentry and begin of
* procexit (the "small" difference). This way, only the call overhead for
* the procentry/procexit functions will be attributed to this call path,
* not the procentry/procexit cycles.
*/
/* Calculate "small" difference. */
spent = sub64(stop, cprof_stk[cprof_stk_top].start_2);
cprof_stk[cprof_stk_top].slot->cycles =
add64(cprof_stk[cprof_stk_top].slot->cycles,
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._[U64_LO] = 0;
cprof_stk[cprof_stk_top].spent_deeper._[U64_HI] = 0;
/* Adjust call path string and stack. */
cpath_len = cprof_stk[cprof_stk_top].cpath_len;
cpath[cpath_len] = '\0';
/* Update spent_deeper for call level below. Include time spent in
* procentry/procexit by using measurements taken at begin of procentry
* and end of procexit (the "big" difference). This way the time spent in
* procentry/procexit will be included in spent_deeper and therefore, since
* this value is substracted from the lower call level, it will not be
* attributed to any call path. This way, pollution of the statistics
* because of procentry/procexit is kept to a minimum.
*/
/* Read CPU cycle count. */
read_tsc(&stop._[U64_HI], &stop._[U64_LO]);
/* Calculate "big" difference. */
spent = sub64(stop, cprof_stk[cprof_stk_top].start_1);
cprof_stk_top--; /* decrease stack */
if (cprof_stk_top >= 0) /* don't update non-existent level -1 */
cprof_stk[cprof_stk_top].spent_deeper =
add64(cprof_stk[cprof_stk_top].spent_deeper, spent);
cprof_locked = 0;
}
PRIVATE void cprof_init() {
message m;
int i;
cpath[0] = '\0';
cpath_len = 0;
cprof_stk_top = -1;
control.reset = 0;
control.err = 0;
cprof_tbl_size = profile_get_tbl_size();
cprof_announce = profile_get_announce();
clear_tbl();
for (i=0; i<CPROF_STACK_SIZE; i++) {
cprof_stk[i].cpath_len = 0;
cprof_stk[i].slot = 0;
cprof_stk[i].start_1._[U64_LO] = 0;
cprof_stk[i].start_1._[U64_HI] = 0;
cprof_stk[i].start_2._[U64_LO] = 0;
cprof_stk[i].start_2._[U64_HI] = 0;
cprof_stk[i].spent_deeper._[U64_LO] = 0;
cprof_stk[i].spent_deeper._[U64_HI] = 0;
}
}
PRIVATE void reset()
{
clear_tbl();
control.reset = 0;
}
PRIVATE void clear_tbl()
{
int i;
/* Reset profiling table. */
control.slots_used = 0;
for (i=0; i<CPROF_INDEX_SIZE; i++) idx[i] = 0; /* clear index */
for (i=0; i<cprof_tbl_size; i++) { /* clear table */
memset(cprof_tbl[i].cpath, '\0', CPROF_CPATH_MAX_LEN);
cprof_tbl[i].next = 0;
cprof_tbl[i].calls = 0;
cprof_tbl[i].cycles._[U64_LO] = 0;
cprof_tbl[i].cycles._[U64_HI] = 0;
}
}
#endif /* CPROFILE */

View file

@ -0,0 +1,48 @@
/*
* Library function used by system profiling.
*
* The variables and functions in this file are used by the procentry/
* procentry syslib functions when linked with userspace processes. For
* kernel processes, the same variables and function are defined
* elsewhere. This enables different functionality and variable sizes,
* which is needed is a few cases.
*
* Changes:
* 14 Aug, 2006 Created (Rogier Meurs)
*/
#include <lib.h>
#if CPROFILE
#include <minix/profile.h>
#include <minix/syslib.h>
/* A regular sized table is declared for the userspace processes. */
struct cprof_tbl_s cprof_tbl[CPROF_TABLE_SIZE_OTHER];
/* Function that returns table size. */
PUBLIC int profile_get_tbl_size(void)
{
return CPROF_TABLE_SIZE_OTHER;
}
/* Function that returns on which execution of procentry to announce. */
PUBLIC int profile_get_announce(void)
{
return CPROF_ANNOUNCE_OTHER;
}
/*
* Userspace processes announce their control struct and table locations
* to the kernel through this function.
*/
PUBLIC void profile_register(ctl_ptr, tbl_ptr)
void *ctl_ptr;
void *tbl_ptr;
{
sys_profbuf(ctl_ptr, tbl_ptr);
}
#endif /* CPROFILE */

7
lib/sysutil/read_tsc.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef READ_TSC_H
#define READ_TSC_H
_PROTOTYPE(void read_tsc, (unsigned long *hi, unsigned long *lo) );
#endif /* READ_TSC_H */

23
lib/sysutil/read_tsc.s Normal file
View file

@ -0,0 +1,23 @@
#
! sections
.sect .text; .sect .rom; .sect .data; .sect .bss
.define _read_tsc ! read the cycle counter (Pentium and up)
.sect .text
!*===========================================================================*
! PUBLIC void read_tsc(unsigned long *high, unsigned long *low);
! Read the cycle counter of the CPU. Pentium and up.
.align 16
_read_tsc:
.data1 0x0f ! this is the RDTSC instruction
.data1 0x31 ! it places the TSC in EDX:EAX
push ebp
mov ebp, 8(esp)
mov (ebp), edx
mov ebp, 12(esp)
mov (ebp), eax
pop ebp
ret

View file

@ -12,6 +12,6 @@ install::
chown -R bin $(MAN)
chgrp -R operator $(MAN)
@rm -f $(MAN)/Makefile
find /usr/man -type f | xargs chmod 644
find /usr/man -type d | xargs chmod 755
find /usr/man -type f | xargs -n 20 chmod 644
find /usr/man -type d | xargs -n 20 chmod 755
makewhatis $(MAN)

73
man/man1/profile.1 Normal file
View file

@ -0,0 +1,73 @@
.TH PROFILE 1
.SH NAME
profile \- MINIX system profiling control command
.SH SYNOPSIS
Statistical profiling:
.PP
.B "profile start"
[
.B -m
.I memsize
]
[
.B "-o"
.I outfile
]
[
.B -f
.I frequency
]
.br
.B "profile stop "
.br
.B "sprofalyze.pl"
.PP
Call profiling:
.PP
.B "profile get"
[
.B "-m"
.I memsize
]
[
.B "-o"
.I outfile
]
.br
.B "profile reset"
.br
.B "cprofalyze.pl"
.SH DESCRIPTION
This command controls MINIX system profiling. There is support for
statistical profiling, which causes a CMOS interrupt to regularly sample
the position of the program counter, and call profiling, which uses
compiler hooks to measure the time and frequency of call paths.
For statistical profiling support, recompile the kernel with SPROFILE in
<minix/config.h> set to 1. For call profiling support, recompile the
system with new libraries with CPROFILE <minix/config.h> set to 1 and
environment variable CPROFILE set to -Rcem-p.
.SH OPTIONS
.IP start
start measuring using statistical profiling.
.IP stop
stop measuring using statistical profiling.
.IP get
retrieve call profiling data
.IP reset
reset call profiling data
.IP "-m"
memory buffer size used to store profiling data in, allocated by the
profile command.
.IP "-o outfile"
output file.
.IP "-f frequency"
frequency for statistical sampling.
.PP
After you have the output file, analysis can be done using the
.B sprofalyze.pl
and
.B cprofalyze.pl
commands.
.SH AUTHOR
Rogier Meurs

View file

@ -13,7 +13,7 @@ f = $u/src/servers/fs
# programs, flags, etc.
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys

View file

@ -6,7 +6,7 @@ g = generic
# Programs, flags, and libraries
CC = cc
CPPFLAGS = -I. -D_MINIX
CFLAGS = $(OPT) $(CPPFLAGS)
CFLAGS = $(OPT) $(CPPFLAGS) $(CPROFILE)
LDFLAGS =
LIBS = -lsysutil -lsys

View file

@ -13,7 +13,7 @@ f = $u/src/servers/fs
# programs, flags, etc.
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
LIBS = -lsysutil -lsys

View file

@ -9,9 +9,9 @@ h = $i/minix
# programs, flags, etc.
CC = exec cc
CFLAGS = -I$i $(EXTRA_OPTS)
CFLAGS = -I$i $(EXTRA_OPTS) $(CPROFILE)
LDFLAGS = -i
LIBS = -lsys -lsysutil -ltimers
LIBS = -lsysutil -lsys -ltimers
OBJ = cache.o device.o link.o \
mount.o misc.o open.o pipe.o protect.o read.o \
@ -19,10 +19,13 @@ OBJ = cache.o device.o link.o \
write.o inode.o main.o path.o super.o
# build local binary
install all build: $(SERVER)
all build: $(SERVER)
$(SERVER): $(OBJ)
$(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS)
install -S 16k $@ /sbin
install -S 64k $(SERVER)
install: $(SERVER)
install $(SERVER) /sbin/$(SERVER)
# clean up local files
clean:

View file

@ -125,7 +125,7 @@ off_t *pos;
v[j].iov_addr, v[j].iov_size,
*op == DEV_GATHER_S ? CPF_WRITE : CPF_READ);
if(!GRANT_VALID(gids[j])) {
panic(__FILE__, "grant to iovec buf failed",
panic(__FILE__, "mfs: grant to iovec buf failed",
NO_NUM);
}
new_iovec[j].iov_size = v[j].iov_size;

View file

@ -10,11 +10,12 @@ k = $u/src/kernel
# programs, flags, etc.
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
OBJ = main.o forkexit.o break.o exec.o time.o timers.o \
signal.o alloc.o utility.o table.o trace.o getset.o misc.o
signal.o alloc.o utility.o table.o trace.o getset.o misc.o \
profile.o
# build local binary
all build: $(SERVER)

114
servers/pm/profile.c Normal file
View file

@ -0,0 +1,114 @@
/* This file implements entry points for system profiling.
*
* The entry points in this file are:
* do_sprofile: start/stop statistical profiling
* do_cprofile: get/reset call profiling tables
*
* Changes:
* 14 Aug, 2006 Created (Rogier Meurs)
*/
#include <minix/config.h>
#include <minix/profile.h>
#include "pm.h"
#include <sys/wait.h>
#include <minix/callnr.h>
#include <minix/com.h>
#include <signal.h>
#include "mproc.h"
#include "param.h"
#if SPROFILE || CPROFILE
FORWARD _PROTOTYPE( int check_addrs, (int info_size) );
#endif
/*===========================================================================*
* do_sprofile *
*===========================================================================*/
PUBLIC int do_sprofile(void)
{
#if SPROFILE
int r;
switch(m_in.PROF_ACTION) {
case PROF_START:
if (r = check_addrs(sizeof(sprof_info_inst))) /* check user pointers */
return r;
return sys_sprof(PROF_START, m_in.PROF_MEM_SIZE, m_in.PROF_FREQ,
who_e, m_in.PROF_CTL_PTR, m_in.PROF_MEM_PTR);
case PROF_STOP:
return sys_sprof(PROF_STOP,0,0,0,0,0);
default:
return EINVAL;
}
#else
return ENOSYS;
#endif
}
/*===========================================================================*
* do_cprofile *
*===========================================================================*/
PUBLIC int do_cprofile(void)
{
#if CPROFILE
int r;
switch(m_in.PROF_ACTION) {
case PROF_GET:
if (r = check_addrs(sizeof(cprof_info_inst))) /* check user pointers */
return r;
return sys_cprof(PROF_GET, m_in.PROF_MEM_SIZE, who_e,
m_in.PROF_CTL_PTR, m_in.PROF_MEM_PTR);
case PROF_RESET:
return sys_cprof(PROF_RESET,0,0,0,0);
default:
return EINVAL;
}
#else
return ENOSYS;
#endif
}
#if SPROFILE || CPROFILE
/*===========================================================================*
* check_addrs *
*===========================================================================*/
PRIVATE int check_addrs(info_size)
int info_size;
{
int r;
phys_bytes p;
/* Check if supplied pointers point into user process. */
if ((r = sys_umap(who_e, D, (vir_bytes) m_in.PROF_CTL_PTR,
info_size, &p)) != OK) {
printf("PM: PROFILE: umap failed for process %d\n", who_e);
return r;
}
if ((r =sys_umap(who_e, D, (vir_bytes) m_in.PROF_MEM_PTR,
m_in.PROF_MEM_SIZE, &p)) != OK) {
printf("PM: PROFILE: umap failed for process %d\n", who_e);
return r;
}
return 0;
}
#endif

View file

@ -80,6 +80,10 @@ _PROTOTYPE( phys_clicks start_click, (void) );
_PROTOTYPE( void setreply, (int proc_nr, int result) );
/* profile.c */
_PROTOTYPE( int do_sprofile, (void) );
_PROTOTYPE( int do_cprofile, (void) );
/* signal.c */
_PROTOTYPE( int do_alarm, (void) );
_PROTOTYPE( int do_kill, (void) );

View file

@ -113,6 +113,8 @@ _PROTOTYPE (int (*call_vec[NCALLS]), (void) ) = {
no_sys, /* 95 = (fchmod) */
no_sys, /* 96 = (fchown) */
do_getsysinfo_up,/* 97 = getsysinfo_up */
do_sprofile, /* 98 = sprofile */
do_cprofile, /* 99 = cprofile */
};
/* This should not fail with "array size is negative": */
extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1];

View file

@ -11,9 +11,9 @@ b = $i/ibm
# programs, flags, etc.
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
UTIL_LIBS = -lsys
UTIL_LIBS = -lsysutil -lsys
LIBS = -lsysutil -lsys
UTIL_OBJ = service.o

View file

@ -11,7 +11,7 @@ b = $i/ibm
# programs, flags, etc.
CC = exec cc
CFLAGS = -I$i
CFLAGS = -I$i $(CPROFILE)
LDFLAGS = -i
UTIL_LIBS = -lsys
LIBS = -lsys -lsysutil

View file

@ -9,9 +9,9 @@ h = $i/minix
# programs, flags, etc.
CC = exec cc
CFLAGS = -I$i $(EXTRA_OPTS)
CFLAGS = -I$i $(EXTRA_OPTS) $(CPROFILE)
LDFLAGS = -i
LIBS = -lsys -lsysutil -ltimers
LIBS = -lsysutil -lsys -ltimers
OBJ = main.o open.o read.o write.o pipe.o dmap.o \
path.o device.o mount.o link.o exec.o \
@ -21,6 +21,7 @@ OBJ = main.o open.o read.o write.o pipe.o dmap.o \
# build local binary
install all build: $(SERVER)
$(SERVER): $(OBJ)
$(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS)
install -S 16k $@

View file

@ -116,6 +116,8 @@ PUBLIC _PROTOTYPE (int (*call_vec[]), (void) ) = {
do_chmod, /* 95 = fchmod */
do_chown, /* 96 = fchown */
no_sys, /* 97 = getsysinfo_up */
no_sys, /* 98 = (sprofile) */
no_sys, /* 99 = (cprofile) */
};
/* This should not fail with "array size is negative": */
extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1];