diff options
Diffstat (limited to 'utils/stat2resid')
-rw-r--r-- | utils/stat2resid/Makefile | 56 | ||||
-rw-r--r-- | utils/stat2resid/parse-gcstats.prl | 232 | ||||
-rw-r--r-- | utils/stat2resid/prefix.txt | 10 | ||||
-rw-r--r-- | utils/stat2resid/process-gcstats.prl | 45 | ||||
-rw-r--r-- | utils/stat2resid/stat2resid.prl | 81 |
5 files changed, 424 insertions, 0 deletions
diff --git a/utils/stat2resid/Makefile b/utils/stat2resid/Makefile new file mode 100644 index 0000000000..f474f5229d --- /dev/null +++ b/utils/stat2resid/Makefile @@ -0,0 +1,56 @@ +TOP=../.. +include $(TOP)/mk/boilerplate.mk + +DYN_LOADABLE_BITS = \ + parse-gcstats.prl \ + process-gcstats.prl + +SCRIPT_PROG=stat2resid +SCRIPT_OBJS=stat2resid.prl + +# +# Could be overridden from the cmd line (see install rule below). +# +INSTALLING=0 + +ifneq "$(BIN_DIST)" "1" +SCRIPT_SUBST_VARS=DEFAULT_TMPDIR +endif + +INTERP=perl + +# +# The stat2resid script is configured with a different +# path to the supporting perl scripts, depending on whether it +# is to be installed or not. +# +ifeq "$(INSTALLING)" "1" +ifeq "$(BIN_DIST)" "1" +SCRIPT_PREFIX_FILES += prefix.txt +endif +endif + +# +# install setup +# +INSTALL_LIBS += $(DYN_LOADABLE_BITS) +INSTALL_SCRIPTS += $(SCRIPT_PROG) + + +# +# Before really installing the script, we have to +# reconfigure it such that the paths it refers to, +# point to the installed utils. +# +install :: + $(RM) $(SCRIPT_PROG) + $(MAKE) $(MFLAGS) INSTALLING=1 $(SCRIPT_PROG) + +include $(TOP)/mk/target.mk + +# Hack to re-create the in-situ build tree script after +# having just installed it. +# +install :: + @$(RM) $(SCRIPT_PROG) + @$(MAKE) $(MFLAGS) $(SCRIPT_PROG) diff --git a/utils/stat2resid/parse-gcstats.prl b/utils/stat2resid/parse-gcstats.prl new file mode 100644 index 0000000000..d882ee6348 --- /dev/null +++ b/utils/stat2resid/parse-gcstats.prl @@ -0,0 +1,232 @@ +# +# Subroutines to parses a ghc Garbage Collection stats file +# +#%gcstats = &parse_stats($ARGV[0]); +#&print_stats(">-", %gcstats); +#exit 0; + +sub to_num { + local ($text) = @_; + return($1 * 1000000000 + $2 * 1000000 + $3 * 1000 + $4) + if ( $text =~ /^(\d*),(\d*),(\d*),(\d*)$/ ); + return($1 * 1000000 + $2 * 1000 + $3) + if ( $text =~ /^(\d*),(\d*),(\d*)$/ ); + return($1 * 1000 + $2) + if ( $text =~ /^(\d*),(\d*)$/ ); + return($1) + if ( $text =~ /^(\d*)$/ ); + die "Error converting $text\n"; +} + +sub from_num { + local ($num) = @_; + local ($b, $m, $t, $o) = (int($num/1000000000), int($num/1000000)%1000, + int($num/1000)%1000, $num%1000); + return(sprintf("%d,%03d,%03d,%03d", $b, $m, $t, $o)) if $b > 0; + return(sprintf("%d,%03d,%03d", $m, $t, $o)) if $m > 0; + return(sprintf("%d,%03d", $t, $o)) if $t > 0; + return(sprintf("%d", $o)) if $o > 0; +} + +sub parse_stats { + local($filename) = @_; + local($tot_alloc, $tot_gc_user, $tot_mut_user, $tot_user, + $tot_gc_elap, $tot_mut_elap, $tot_elap); + local($statsfile, $line, $row, $col, $val); + local(@stats, @hdr1, @hdr2, @line_vals); + local(%the_stats); + local(*STATS); + + open(STATS, $filename) || die "Cant open $filename \n"; + @stats = <STATS>; + + do {$line = shift(@stats);} until ($line !~ /^$/); + chop($line); + ($the_stats{"command"}, $the_stats{"args"}) = split(' ', $line, 2); + + do {$line = shift(@stats);} until ($line !~ /^$/); + $line =~ /Collector:\s*([A-Z]+)\s*HeapSize:\s*([\d,]+)/; + $the_stats{"collector"} = $1; + $the_stats{"heapsize"} = &to_num($2); + + do {$line = shift(@stats);} until ($line !~ /^$/); + chop($line); + @hdr1 = split(' ', $line); + $line = shift(@stats); + chop($line); + @hdr2 = split(' ', $line); + + $row = 0; + $tot_alloc = 0; + $tot_gc_user = 0; + $tot_gc_elap = 0; + $tot_mut_user = 0; + $tot_mut_elap = 0; + $tot_user = 0; + $tot_elap = 0; + + while (($line = shift(@stats)) !~ /^\s*\d+\s*$/) { + chop($line); + @line_vals = split(' ', $line); + + $col = -1; + word: + while(++$col <= $#line_vals) { + + $val = $line_vals[$col]; + $_ = @hdr1[$col] . @hdr2[$col]; + + /^Allocbytes$/ && do { $tot_alloc += $val; + $the_stats{"alloc_$row"} = $val; + next word; }; + + /^Collectbytes$/ && do { $the_stats{"collect_$row"} = $val; + next word; }; + + /^Livebytes$/ && do { $the_stats{"live_$row"} = $val; + next word; }; + + /^Residency$/ && do { next word; }; + + /^GCuser$/ && do { $tot_gc_user += $val; + $the_stats{"gc_user_$row"} = $val; + next word; }; + + /^GCelap$/ && do { $tot_gc_elap += $val; + $the_stats{"gc_elap_$row"} = $val; + next word; }; + + /^TOTuser$/ && do { $the_stats{"mut_user_$row"} = + $val - $tot_user - $the_stats{"gc_user_$row"}; + $tot_mut_user += $the_stats{"mut_user_$row"}; + $tot_user = $val; + next word; }; + + /^TOTelap$/ && do { $the_stats{"mut_elap_$row"} = + $val - $tot_elap - $the_stats{"gc_elap_$row"}; + $tot_mut_elap += $the_stats{"mut_elap_$row"}; + $tot_elap = $val; + next word; }; + + /^PageGC$/ && do { $the_stats{"gc_pflts_$row"} = $val; + next word; }; + + /^FltsMUT$/ && do { $the_stats{"mut_pflts_$row"} = $val; + next word; }; + + /^Collection/ && do { $the_stats{"mode_$row"} = $val; + next word; }; + + /^Astkbytes$/ && do {next word; }; + /^Bstkbytes$/ && do {next word; }; + /^CafNo$/ && do {next word; }; + /^Cafbytes$/ && do {next word; }; + + /^NoAstk$/ && do {next word; }; + /^ofBstk$/ && do {next word; }; + /^RootsReg$/ && do {next word; }; + /^OldGen$/ && do {next word; }; + /^RootsCaf$/ && do {next word; }; + /^Sizebytes$/ && do {next word; }; + /^Resid\%heap$/ && do {next word; }; + + /^$/ && do {next word; }; + + print STDERR "Unknown: $_ = $val\n"; + }; + + $row++; + }; + $tot_alloc += $line; + $the_stats{"alloc_$row"} = $line; + +arg: while($_ = $stats[0]) { + shift(@stats); + + /^\s*([\d,]+) bytes alloc/ && do { local($a) = &to_num($1); + $a == $tot_alloc || die "Total $a != $tot_alloc \n"; + $the_stats{"alloc_total"} = $tot_alloc; + next arg; }; + + /^\s*([\d]+) garbage/ && do { $1 == $row || die "GCNo $1 != $row \n"; + $the_stats{"gc_no"} = $row; + next arg; }; + + /Total time\s+([\d\.]+)s\s+\(\s*([\d.]+)s elapsed\)/ && do { + $the_stats{"user_total"} = $1; + $the_stats{"elap_total"} = $2; + $the_stats{"mut_user_total"} = $1 - $tot_gc_user; + $the_stats{"mut_elap_total"} = $2 - $tot_gc_elap; + $the_stats{"mut_user_$row"} = $1 - $tot_gc_user - $tot_mut_user; + $the_stats{"mut_elap_$row"} = $2 - $tot_gc_elap - $tot_mut_elap; + next arg; }; + + /GC\s+time\s+([\d\.]+)s\s+\(\s*([\d.]+)s elapsed\)/ && do { + # $1 == $tot_gc_user || die "GCuser $1 != $tot_gc_user \n"; + # $2 == $tot_gc_elap || die "GCelap $2 != $tot_gc_elap \n"; + $the_stats{"gc_user_total"} = $tot_gc_user; + $the_stats{"gc_elap_total"} = $tot_gc_elap; + next arg; }; + + /MUT\s+time/ && do { next arg; }; + /INIT\s+time/ && do { next arg; }; + /^\s*([\d,]+) bytes maximum residency/ && do { next arg; }; + + /\%GC time/ && do { next arg; }; + /Alloc rate/ && do { next arg; }; + /Productivity/ && do { next arg; }; + /^$/ && do { next arg; }; + /^\#/ && do { next arg; }; # Allows comments to follow + + print STDERR "Unmatched line: $_"; + } + + close(STATS); + %the_stats; +} + +sub print_stats { + local ($filename, %out_stats) = @_; + local($statsfile, $row); + + open($statsfile, $filename) || die "Cant open $filename \n"; + select($statsfile); + + print $out_stats{"command"}, " ", $out_stats{"args"}, "\n\n"; + print "Collector: ", $out_stats{"collector"}, " HeapSize: ", &from_num($out_stats{"heapsize"}), " (bytes)\n\n"; + + $row = 0; + while ($row < $out_stats{"gc_no"}) { + printf "%7d %7d %7d %5.2f %5.2f %5.2f %5.2f %4d %4d %s\n", + $out_stats{"alloc_$row"}, + $out_stats{"collect_$row"}, + $out_stats{"live_$row"}, + $out_stats{"gc_user_$row"}, + $out_stats{"gc_elap_$row"}, + $out_stats{"mut_user_$row"}, + $out_stats{"mut_elap_$row"}, + $out_stats{"gc_pflts_$row"}, + $out_stats{"mut_pflts_$row"}, + $out_stats{"mode_$row"}; + $row++; + }; + printf "%7d %s %5.2f %5.2f \n\n", + $out_stats{"alloc_$row"}, " " x 27, + $out_stats{"mut_user_$row"}, + $out_stats{"mut_elap_$row"}; + + printf "Total Alloc: %s\n", &from_num($out_stats{"alloc_total"}); + printf " GC No: %d\n\n", $out_stats{"gc_no"}; + + printf " MUT User: %6.2fs\n", $out_stats{"mut_user_total"}; + printf " GC User: %6.2fs\n", $out_stats{"gc_user_total"}; + printf "Total User: %6.2fs\n\n", $out_stats{"user_total"}; + + printf " MUT Elap: %6.2fs\n", $out_stats{"mut_elap_total"}; + printf " GC Elap: %6.2fs\n", $out_stats{"gc_elap_total"}; + printf "Total Elap: %6.2fs\n", $out_stats{"elap_total"}; + + close($statsfile); +} + +1; diff --git a/utils/stat2resid/prefix.txt b/utils/stat2resid/prefix.txt new file mode 100644 index 0000000000..0de9d61f25 --- /dev/null +++ b/utils/stat2resid/prefix.txt @@ -0,0 +1,10 @@ +# +# stat2resid - generating graphs from garbage collection stats. +# +# To use the script on your system, the following variable +# needs to be uncommented and set, if it hasn't already +# been set above automatically: +# +#$libdir='/local/fp/lib/fptools/i386-unknown-footos/ghc-2.02'; +# + diff --git a/utils/stat2resid/process-gcstats.prl b/utils/stat2resid/process-gcstats.prl new file mode 100644 index 0000000000..ff41cf6af9 --- /dev/null +++ b/utils/stat2resid/process-gcstats.prl @@ -0,0 +1,45 @@ +# +# Subroutines which derive information from +# ghc garbage collection stats -- %gcstat +# + +sub max_residency { + local(%gcstats) = @_; + local($i, $max) = (-1, 0); + + if ($gcstats{"collector"} eq "APPEL") { + die "APPEL stats: average residency not possible\n" ; + } + + while(++$i < $gcstats{"gc_no"}) { + $max = $gcstats{"live_$i"} > $max ? + $gcstats{"live_$i"} : $max; + } + $max; +} + +sub avg_residency { + local(%gcstats) = @_; + local($i, $j, $total); + + if ($gcstats{"collector"} eq "APPEL") { + die "APPEL stats: average residency not possible\n" ; + } + + if ($gcstats{"gc_no"} == 0) { return(0); }; + + $i = 0; $j = 0; + $total = $gcstats{"live_$i"} * $gcstats{"mut_user_$i"} / 2; + + while(++$i < $gcstats{"gc_no"}) { + $total += ($gcstats{"live_$i"} + $gcstats{"live_$j"}) + * $gcstats{"mut_user_$i"} / 2; + $j = $i; + }; + + $total += $gcstats{"live_$j"} * $gcstats{"mut_user_$i"} / 2; + + int($total / $gcstats{"mut_user_total"}); +} + +1; diff --git a/utils/stat2resid/stat2resid.prl b/utils/stat2resid/stat2resid.prl new file mode 100644 index 0000000000..bf0a262428 --- /dev/null +++ b/utils/stat2resid/stat2resid.prl @@ -0,0 +1,81 @@ +# +# (c) The GRASP/AQUA Project, Glasgow University, 1992-1996 +# +# Perl script expect bindings for the following variables to be prepended +# +# DEFAULT_TMPDIR libdir +# +# without them, not much success :-( +# + +$debug = 0; # first line of script, builds confidence :-) +$outsuffix = ".resid.ps"; # change as appropriate + +if ( $ENV{'TMPDIR'} ) { # where to make tmp file names + $tmpfile = $ENV{'TMPDIR'} . "/$$.resid.data"; +} else { + $tmpfile ="${DEFAULT_TMPDIR}/$$.resid.data"; + $ENV{'TMPDIR'} = ${DEFAULT_TMPDIR}; # set the env var as well +} + +@INC = ( ${libdir} ); + +require('parse-gcstats.prl') || die "Can't load parse-gcstats.prl!\n"; +require('process-gcstats.prl') || die "Can't load process-gcstats.prl!\n"; + +if ($#ARGV < 0) { + $infile = "-"; + $outfile = ""; # gnuplot: set output +} elsif ($#ARGV == 0) { + $infile = $ARGV[0]; + if ($infile =~ /^(.*)\.stat$/) { + $base = $1; + } else { + $base = $infile; + $infile = "$base.stat"; + }; + $outfile = "\"$base$outsuffix\""; # gnuplot: set output "outfile" +} elsif ($#ARGV == 1) { + $infile = $ARGV[0]; + $outfile = "\"$ARGV[1]\""; +} else { + die "Usage: command [infile[.stat] [outfile]]"; +}; + +%gcstats = &parse_stats($infile); + +&print_stats(">&STDERR", %gcstats) if $debug; + +if ($gcstats{"collector"} eq "APPEL") { + die "APPEL stats: no residency plot possible\n"; +} + +# +# stats are now loaded into %gcstats -- write out info +# + +open(DATAFILE, ">$tmpfile") || die "Cant open >$tmpfile \n"; +$i = -1; +$user = 0; +printf DATAFILE "%4.2f %d\n", $user, 0; +while (++$i < $gcstats{"gc_no"}) { + $user += $gcstats{"mut_user_$i"}; + printf DATAFILE "%4.2f %d\n", $user, $gcstats{"live_$i"}; +}; +printf DATAFILE "%4.2f %d\n", $gcstats{"mut_user_total"}, 0; +close(DATAFILE); + +open(PLOTFILE, "|gnuplot") || die "Cant pipe into |gnuplot \n"; +print PLOTFILE "set data style linespoints\n"; +print PLOTFILE "set function style lines\n"; +print PLOTFILE "set nokey\n"; +print PLOTFILE "set xlabel \"Mutator Time (secs)\"\n"; +print PLOTFILE "set ylabel \"Heap Residency (bytes)\" 0,-1\n"; +print PLOTFILE "set term post eps \"Times-Roman\" 20\n"; +printf PLOTFILE "set title \"%s %s (%s)\"\n", $gcstats{"command"}, $gcstats{"args"}, $infile; +print PLOTFILE "set output $outfile\n" ; +print PLOTFILE "plot \"$tmpfile\"\n"; +close(PLOTFILE); + +unlink($tmpfile); +exit 0; |