From: Ben Gras Date: Mon, 30 Oct 2006 15:53:38 +0000 (+0000) Subject: System statistical and call profiling X-Git-Tag: v3.1.3~157 X-Git-Url: http://zhaoyanbai.com/repos/?a=commitdiff_plain;h=7195fe33254002323e47fcfed8d0da64770f4ccc;p=minix.git System statistical and call profiling support by Rogier Meurs . --- diff --git a/commands/Makefile b/commands/Makefile index 1030ccf1c..8a8f2fcad 100755 --- a/commands/Makefile +++ b/commands/Makefile @@ -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 diff --git a/commands/profile/Makefile b/commands/profile/Makefile new file mode 100644 index 000000000..2fa766aaa --- /dev/null +++ b/commands/profile/Makefile @@ -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 + diff --git a/commands/profile/cprofalyze.pl b/commands/profile/cprofalyze.pl new file mode 100755 index 000000000..caae0e765 --- /dev/null +++ b/commands/profile/cprofalyze.pl @@ -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 [-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 <; 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. + $_ = ; 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"; +} + diff --git a/commands/profile/profile.c b/commands/profile/profile.c new file mode 100644 index 000000000..c987a3e66 --- /dev/null +++ b/commands/profile/profile.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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; +} + diff --git a/commands/profile/sprofalyze.pl b/commands/profile/sprofalyze.pl new file mode 100755 index 000000000..3c2d7c08b --- /dev/null +++ b/commands/profile/sprofalyze.pl @@ -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. + $_ = ; 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. + $_ = ; + 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. + 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 ; + +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"; +} diff --git a/commands/profile/tools/proftsc.pl b/commands/profile/tools/proftsc.pl new file mode 100755 index 000000000..d4d1afaa5 --- /dev/null +++ b/commands/profile/tools/proftsc.pl @@ -0,0 +1,33 @@ +#!/usr/local/bin/perl + +$SAMPLE_SZ = 8; + +$file = shift; + +open (FILE, $file) or die ("Unable to open $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"; +#} diff --git a/drivers/at_wini/Makefile b/drivers/at_wini/Makefile index 738e75567..25c17e7a7 100644 --- a/drivers/at_wini/Makefile +++ b/drivers/at_wini/Makefile @@ -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) diff --git a/drivers/bios_wini/Makefile b/drivers/bios_wini/Makefile index ff0de5496..56f193bc2 100644 --- a/drivers/bios_wini/Makefile +++ b/drivers/bios_wini/Makefile @@ -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 diff --git a/drivers/cmos/Makefile b/drivers/cmos/Makefile index ae345e2bc..5366c41b5 100644 --- a/drivers/cmos/Makefile +++ b/drivers/cmos/Makefile @@ -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 diff --git a/drivers/dp8390/Makefile b/drivers/dp8390/Makefile index de9883398..6f7b06e15 100644 --- a/drivers/dp8390/Makefile +++ b/drivers/dp8390/Makefile @@ -11,7 +11,7 @@ d = .. # programs, flags, etc. CC = exec cc -CFLAGS = -I$i +CFLAGS = -I$i $(CPROFILE) LDFLAGS = -i LIBS = -lsysutil -lsys -ltimers diff --git a/drivers/dpeth/Makefile b/drivers/dpeth/Makefile index cd6215d18..833962b68 100644 --- a/drivers/dpeth/Makefile +++ b/drivers/dpeth/Makefile @@ -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 . -## -## 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 diff --git a/drivers/floppy/Makefile b/drivers/floppy/Makefile index 700284b5d..db9bd0bd3 100644 --- a/drivers/floppy/Makefile +++ b/drivers/floppy/Makefile @@ -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 diff --git a/drivers/fxp/Makefile b/drivers/fxp/Makefile index 7ae75237c..9cf6ba817 100644 --- a/drivers/fxp/Makefile +++ b/drivers/fxp/Makefile @@ -11,7 +11,7 @@ d = .. # programs, flags, etc. CC = exec cc -CFLAGS = -I$i +CFLAGS = -I$i $(CPROFILE) LDFLAGS = -i LIBS = -lsysutil -lsys -ltimers diff --git a/drivers/lance/Makefile b/drivers/lance/Makefile index 2e81959a2..b6b7eef51 100644 --- a/drivers/lance/Makefile +++ b/drivers/lance/Makefile @@ -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 diff --git a/drivers/libdriver/Makefile b/drivers/libdriver/Makefile index b1a7c252d..93bb1d740 100644 --- a/drivers/libdriver/Makefile +++ b/drivers/libdriver/Makefile @@ -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 diff --git a/drivers/log/Makefile b/drivers/log/Makefile index b929d882a..06f2f7d56 100644 --- a/drivers/log/Makefile +++ b/drivers/log/Makefile @@ -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 diff --git a/drivers/memory/ramdisk/proto b/drivers/memory/ramdisk/proto index 38987d9bb..63a375d7c 100644 --- a/drivers/memory/ramdisk/proto +++ b/drivers/memory/ramdisk/proto @@ -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 diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index e24f056c2..965a4bce5 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -11,7 +11,7 @@ d = .. # programs, flags, etc. CC = cc -CFLAGS = -I$i +CFLAGS = -I$i $(CPROFILE) LDFLAGS = -i LIBS = -lsysutil -lsys -ltimers diff --git a/drivers/printer/Makefile b/drivers/printer/Makefile index 975fe054b..0d63d622e 100644 --- a/drivers/printer/Makefile +++ b/drivers/printer/Makefile @@ -11,7 +11,7 @@ d = .. # programs, flags, etc. CC = exec cc -CFLAGS = -I$i +CFLAGS = -I$i $(CPROFILE) LDFLAGS = -i LIBS = -lsysutil -lsys diff --git a/drivers/random/Makefile b/drivers/random/Makefile index e1d7fc074..b535ceea8 100644 --- a/drivers/random/Makefile +++ b/drivers/random/Makefile @@ -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 diff --git a/drivers/rescue/Makefile b/drivers/rescue/Makefile index 9389d8a6f..66cf545ca 100644 --- a/drivers/rescue/Makefile +++ b/drivers/rescue/Makefile @@ -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 diff --git a/drivers/rtl8139/Makefile b/drivers/rtl8139/Makefile index 1890aa444..d37ab9aaa 100644 --- a/drivers/rtl8139/Makefile +++ b/drivers/rtl8139/Makefile @@ -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 diff --git a/drivers/sb16/Makefile b/drivers/sb16/Makefile index ce978abb0..1241064de 100644 --- a/drivers/sb16/Makefile +++ b/drivers/sb16/Makefile @@ -10,7 +10,7 @@ d = .. # programs, flags, etc. CC = exec cc -CFLAGS = -I$i +CFLAGS = -I$i $(CPROFILE) LDFLAGS = -i LIBS = -lsysutil -lsys diff --git a/drivers/ti1225/Makefile b/drivers/ti1225/Makefile index 77efdac0a..e5529e678 100644 --- a/drivers/ti1225/Makefile +++ b/drivers/ti1225/Makefile @@ -11,7 +11,7 @@ d = .. # programs, flags, etc. CC = exec cc -CFLAGS = -I$i +CFLAGS = -I$i $(CPROFILE) LDFLAGS = -i LIBS = -lsysutil -lsys -ltimers diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index f45468ec4..3661fa720 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -12,7 +12,7 @@ d = .. # programs, flags, etc. MAKE = exec make CC = exec cc -CFLAGS = -I$i +CFLAGS = -I$i LDFLAGS = -i LIBS = -lsysutil -lsys -ltimers diff --git a/drivers/tty/keymaps/Makefile b/drivers/tty/keymaps/Makefile index 1b485fae9..bd82dec6c 100644 --- a/drivers/tty/keymaps/Makefile +++ b/drivers/tty/keymaps/Makefile @@ -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 diff --git a/include/ibm/interrupt.h b/include/ibm/interrupt.h index e163f64fe..53d452f80 100644 --- a/include/ibm/interrupt.h +++ b/include/ibm/interrupt.h @@ -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 */ diff --git a/include/minix/callnr.h b/include/minix/callnr.h index 6e045e919..c2c62f67c 100755 --- a/include/minix/callnr.h +++ b/include/minix/callnr.h @@ -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 diff --git a/include/minix/com.h b/include/minix/com.h index 32f4305c5..978a0ffa0 100755 --- a/include/minix/com.h +++ b/include/minix/com.h @@ -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 diff --git a/include/minix/config.h b/include/minix/config.h index dcf60ccb2..0d6df0429 100755 --- a/include/minix/config.h +++ b/include/minix/config.h @@ -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 */ diff --git a/include/minix/profile.h b/include/minix/profile.h new file mode 100644 index 000000000..91193a4b9 --- /dev/null +++ b/include/minix/profile.h @@ -0,0 +1,109 @@ +#ifndef _PROFILE_H +#define _PROFILE_H + +#include + +/* + * 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 + +# 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 */ + diff --git a/include/minix/syslib.h b/include/minix/syslib.h index 4ae273c30..9eeb93a03 100755 --- a/include/minix/syslib.h +++ b/include/minix/syslib.h @@ -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 */ diff --git a/kernel/Makefile b/kernel/Makefile index d7ec1e9f2..35de79fd8 100755 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -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. diff --git a/kernel/kernel.h b/kernel/kernel.h index 37838801b..7df4de501 100755 --- a/kernel/kernel.h +++ b/kernel/kernel.h @@ -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 */ diff --git a/kernel/main.c b/kernel/main.c index 14d27ed5f..dd2100b80 100755 --- a/kernel/main.c +++ b/kernel/main.c @@ -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. */ diff --git a/kernel/profile.c b/kernel/profile.c new file mode 100644 index 000000000..ce4f50ac0 --- /dev/null +++ b/kernel/profile.c @@ -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 + +#if SPROFILE || CPROFILE + +#include +#include "kernel.h" +#include "profile.h" +#include "proc.h" + +#endif + +#if SPROFILE + +#include +#include + +/* 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 */ + diff --git a/kernel/profile.h b/kernel/profile.h new file mode 100644 index 000000000..c8f54d87b --- /dev/null +++ b/kernel/profile.h @@ -0,0 +1,41 @@ +#ifndef PROFILE_H +#define PROFILE_H + +#if SPROFILE || CPROFILE +#include +#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 */ + diff --git a/kernel/system.c b/kernel/system.c index 1968e6b8d..c0151ac4e 100755 --- a/kernel/system.c +++ b/kernel/system.c @@ -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 */ } /*===========================================================================* diff --git a/kernel/system.h b/kernel/system.h index 857689f7f..c3ff6a838 100644 --- a/kernel/system.h +++ b/kernel/system.h @@ -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 */ diff --git a/kernel/system/Makefile b/kernel/system/Makefile index f94a4d46a..7916a24de 100644 --- a/kernel/system/Makefile +++ b/kernel/system/Makefile @@ -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 diff --git a/kernel/system/do_cprofile.c b/kernel/system/do_cprofile.c new file mode 100644 index 000000000..506373049 --- /dev/null +++ b/kernel/system/do_cprofile.c @@ -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 + +/*===========================================================================* + * 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; iPROF_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 +#include #if USE_PRIVCTL diff --git a/kernel/system/do_profbuf.c b/kernel/system/do_profbuf.c new file mode 100644 index 000000000..7638e046d --- /dev/null +++ b/kernel/system/do_profbuf.c @@ -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 */ + diff --git a/kernel/system/do_sprofile.c b/kernel/system/do_sprofile.c new file mode 100644 index 000000000..9803338af --- /dev/null +++ b/kernel/system/do_sprofile.c @@ -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 */ + diff --git a/kernel/table.c b/kernel/table.c index 3af05c4e1..9626fcdeb 100755 --- a/kernel/table.c +++ b/kernel/table.c @@ -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 }, diff --git a/lib/other/Makefile.in b/lib/other/Makefile.in index 8f99cff13..675661ac4 100644 --- a/lib/other/Makefile.in +++ b/lib/other/Makefile.in @@ -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 \ diff --git a/lib/other/_cprofile.c b/lib/other/_cprofile.c new file mode 100644 index 000000000..8d0f69e67 --- /dev/null +++ b/lib/other/_cprofile.c @@ -0,0 +1,16 @@ +#include + +#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); +} + diff --git a/lib/other/_sprofile.c b/lib/other/_sprofile.c new file mode 100644 index 000000000..7ae532a77 --- /dev/null +++ b/lib/other/_sprofile.c @@ -0,0 +1,17 @@ +#include + +#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); +} + diff --git a/lib/syscall/Makefile.in b/lib/syscall/Makefile.in index debbe2002..82094ba48 100644 --- a/lib/syscall/Makefile.in +++ b/lib/syscall/Makefile.in @@ -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 \ diff --git a/lib/syscall/cprofile.s b/lib/syscall/cprofile.s new file mode 100755 index 000000000..99d7a8666 --- /dev/null +++ b/lib/syscall/cprofile.s @@ -0,0 +1,7 @@ +.sect .text +.extern __cprofile +.define _cprofile +.align 2 + +_cprofile: + jmp __cprofile diff --git a/lib/syscall/sprofile.s b/lib/syscall/sprofile.s new file mode 100755 index 000000000..61ff1c6bd --- /dev/null +++ b/lib/syscall/sprofile.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sprofile +.define _sprofile +.align 2 + +_sprofile: + jmp __sprofile diff --git a/lib/syslib/Makefile.in b/lib/syslib/Makefile.in index 48aeb2ae3..bcefd8804 100644 --- a/lib/syslib/Makefile.in +++ b/lib/syslib/Makefile.in @@ -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 \ diff --git a/lib/syslib/sys_cprof.c b/lib/syslib/sys_cprof.c new file mode 100644 index 000000000..293030516 --- /dev/null +++ b/lib/syslib/sys_cprof.c @@ -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 + diff --git a/lib/syslib/sys_profbuf.c b/lib/syslib/sys_profbuf.c new file mode 100644 index 000000000..9c9bf3688 --- /dev/null +++ b/lib/syslib/sys_profbuf.c @@ -0,0 +1,23 @@ +#include "syslib.h" + +#include + +#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 + diff --git a/lib/syslib/sys_sprof.c b/lib/syslib/sys_sprof.c new file mode 100644 index 000000000..1553e8985 --- /dev/null +++ b/lib/syslib/sys_sprof.c @@ -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 + diff --git a/lib/sysutil/Makefile.in b/lib/sysutil/Makefile.in index 06cd6d32f..c80adaa6d 100644 --- a/lib/sysutil/Makefile.in +++ b/lib/sysutil/Makefile.in @@ -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 diff --git a/lib/sysutil/profile.c b/lib/sysutil/profile.c new file mode 100644 index 000000000..179de031a --- /dev/null +++ b/lib/sysutil/profile.c @@ -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 + +#if CPROFILE + +#include +#include +#include +#include +#include +#include +#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> 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 + +#if CPROFILE + +#include +#include + +/* 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 */ + diff --git a/lib/sysutil/read_tsc.h b/lib/sysutil/read_tsc.h new file mode 100644 index 000000000..ff72854f8 --- /dev/null +++ b/lib/sysutil/read_tsc.h @@ -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 */ + diff --git a/lib/sysutil/read_tsc.s b/lib/sysutil/read_tsc.s new file mode 100644 index 000000000..5d45b090d --- /dev/null +++ b/lib/sysutil/read_tsc.s @@ -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 + diff --git a/man/Makefile b/man/Makefile index 0e6f40559..a44864b57 100644 --- a/man/Makefile +++ b/man/Makefile @@ -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) diff --git a/man/man1/profile.1 b/man/man1/profile.1 new file mode 100644 index 000000000..32ab70acf --- /dev/null +++ b/man/man1/profile.1 @@ -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 + set to 1. For call profiling support, recompile the +system with new libraries with CPROFILE 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 diff --git a/servers/ds/Makefile b/servers/ds/Makefile index 5ceffb4ac..5f4897320 100644 --- a/servers/ds/Makefile +++ b/servers/ds/Makefile @@ -13,9 +13,9 @@ f = $u/src/servers/fs # programs, flags, etc. CC = exec cc -CFLAGS = -I$i +CFLAGS = -I$i $(CPROFILE) LDFLAGS = -i -LIBS = -lsysutil -lsys +LIBS = -lsysutil -lsys OBJ = main.o store.o diff --git a/servers/inet/Makefile b/servers/inet/Makefile index 3ca6f771d..995428357 100644 --- a/servers/inet/Makefile +++ b/servers/inet/Makefile @@ -6,9 +6,9 @@ g = generic # Programs, flags, and libraries CC = cc CPPFLAGS = -I. -D_MINIX -CFLAGS = $(OPT) $(CPPFLAGS) +CFLAGS = $(OPT) $(CPPFLAGS) $(CPROFILE) LDFLAGS = -LIBS = -lsysutil -lsys +LIBS = -lsysutil -lsys .c.o: $(CC) $(CFLAGS) -o $@ -c $< diff --git a/servers/is/Makefile b/servers/is/Makefile index 8c7963014..9c3c9cbf1 100644 --- a/servers/is/Makefile +++ b/servers/is/Makefile @@ -13,9 +13,9 @@ f = $u/src/servers/fs # programs, flags, etc. CC = exec cc -CFLAGS = -I$i +CFLAGS = -I$i $(CPROFILE) LDFLAGS = -i -LIBS = -lsysutil -lsys +LIBS = -lsysutil -lsys OBJ = main.o dmp.o dmp_kernel.o dmp_pm.o dmp_fs.o dmp_rs.o dmp_ds.o diff --git a/servers/mfs/Makefile b/servers/mfs/Makefile index 8ed054eb3..4cb8d35db 100644 --- a/servers/mfs/Makefile +++ b/servers/mfs/Makefile @@ -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: diff --git a/servers/mfs/device.c b/servers/mfs/device.c index 7940dde29..5a8662a20 100644 --- a/servers/mfs/device.c +++ b/servers/mfs/device.c @@ -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; diff --git a/servers/pm/Makefile b/servers/pm/Makefile index 2904d09b9..17d52f216 100644 --- a/servers/pm/Makefile +++ b/servers/pm/Makefile @@ -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) diff --git a/servers/pm/profile.c b/servers/pm/profile.c new file mode 100644 index 000000000..471bf7692 --- /dev/null +++ b/servers/pm/profile.c @@ -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 +#include +#include "pm.h" +#include +#include +#include +#include +#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 + diff --git a/servers/pm/proto.h b/servers/pm/proto.h index 518f7650c..9b9436a91 100644 --- a/servers/pm/proto.h +++ b/servers/pm/proto.h @@ -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) ); diff --git a/servers/pm/table.c b/servers/pm/table.c index a130221b3..d1aaecf3e 100644 --- a/servers/pm/table.c +++ b/servers/pm/table.c @@ -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]; diff --git a/servers/rs/Makefile b/servers/rs/Makefile index a1e998a0e..04bb1ace8 100644 --- a/servers/rs/Makefile +++ b/servers/rs/Makefile @@ -11,10 +11,10 @@ b = $i/ibm # programs, flags, etc. CC = exec cc -CFLAGS = -I$i +CFLAGS = -I$i $(CPROFILE) LDFLAGS = -i -UTIL_LIBS = -lsys -LIBS = -lsysutil -lsys +UTIL_LIBS = -lsysutil -lsys +LIBS = -lsysutil -lsys UTIL_OBJ = service.o OBJ = exec.o main.o manager.o diff --git a/servers/sm/Makefile b/servers/sm/Makefile index 3258319d6..3fa3184ba 100644 --- a/servers/sm/Makefile +++ b/servers/sm/Makefile @@ -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 diff --git a/servers/vfs/Makefile b/servers/vfs/Makefile index 9d80d14d4..837d9626d 100644 --- a/servers/vfs/Makefile +++ b/servers/vfs/Makefile @@ -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 $@ diff --git a/servers/vfs/table.c b/servers/vfs/table.c index f347ac335..aada1f952 100644 --- a/servers/vfs/table.c +++ b/servers/vfs/table.c @@ -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];