# # 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 = ; 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;