# Copyright (C) 2021 Free Software Foundation, Inc. # # This file is part of the GNU Binutils. # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, # MA 02110-1301, USA. use strict; package acct; use vars qw(%Acct $Erp); my($debug_f, $retVal, $OpenDis, $OpenFsingle, $Read_rules_txt); my(@Comparison, @hashSample, @acctHeader); my(%RANGE, %Rules); my($ERROR_ACCT_MISMATCH, $ERROR_NEGATIVE_TIME, $ERROR_PERL_ERROR, $ERROR_DIFF_RANGE, $ERROR_ZERO_METRIC, $ERROR_HIGH_UNKNOWN, $ERROR_CALLER_VERIF, $ERROR_SIGNAL_LOST); BEGIN { # use Exporter (); # @ISA = 'Exporter'; # @EXPORT_OK = ('&readAcct', '%Acct'); $debug_f = $ENV{PERL_DEBUG}; $retVal = 0; $OpenDis = 0; $OpenFsingle = 0; $#Comparison = -1; $Read_rules_txt = 0; $Erp = {}; @hashSample = []; %RANGE = ( Count => { P_RANGE => 0, P_RATE => 0, N_RANGE => 0, N_RATE => 0, FMT => "%d" }, Total => { P_RANGE => 0.20, P_RATE => 3, N_RANGE => -0.20, N_RATE => -3, FMT => "%6.3f" }, Cpu => { P_RANGE => 0.5, P_RATE => 10, N_RANGE => -0.5, N_RATE => -10, FMT => "%6.3f" ,P_RANGE_2AVG => 0.5, P_RATE_2AVG => 10, N_RANGE_2AVG => -0.5, N_RATE_2AVG => -10 }, Cycles => { P_RANGE => 0.5, P_RATE => 10, N_RANGE => -0.5, N_RATE => -10, FMT => "%6.3f" ,P_RANGE_2AVG => 0.5, P_RATE_2AVG => 10, N_RANGE_2AVG => -0.5, N_RATE_2AVG => -10 }, Cycles1 => { P_RANGE => 0.5, P_RATE => 10, N_RANGE => -0.5, N_RATE => -10, FMT => "%6.3f" ,P_RANGE_2AVG => 0.5, P_RATE_2AVG => 10, N_RANGE_2AVG => -0.5, N_RATE_2AVG => -10 }, Sync => { P_RANGE => 0.5, P_RATE => 3, N_RANGE => -0.5, N_RATE => -3, FMT => "%6.3f" }, Unkn => { P_RANGE => 0.10, P_RATE => 0.5, FMT => "%6.3f" } ); $ERROR_SIGNAL_LOST = 44; $ERROR_DIFF_RANGE = 84; $ERROR_HIGH_UNKNOWN = 85; $ERROR_PERL_ERROR = 86; $ERROR_ACCT_MISMATCH = 87; $ERROR_CALLER_VERIF = 88; $ERROR_ZERO_METRIC = 94; $ERROR_NEGATIVE_TIME = 103; } sub debug { my ($lineN, $fmt); if ( $debug_f == 0 ) { return; } $lineN = shift @_; $fmt = shift @_; if ( $debug_f == 2 ) { warn "DEBUG:#$lineN:\n"; } warn sprintf($fmt, @_); } sub set_retVal { if ( $retVal == 0 ) { $retVal = $_[0]; if ($retVal != 0 ) { warn sprintf("DEBUG: retVal=%d\n", $retVal); } } return $retVal; } sub diffRule { # The format of the comparison rule is: # , , , # Cpu, 3, 1 # Total, 2, 3 my ($str) = @_; my (@arr); @arr = split (/,/, $str); if ($#arr == 2) { # Old version push @arr, $arr[0]; } push @Comparison, [@arr]; } sub read_rules { my ($name, $rule, $line, @arr); return if ( $Read_rules_txt == 1); $Read_rules_txt = 1; open(FP, ") { chomp ($line); $line =~ s/\s*//g; # Remove all blanks $line =~ s/\\s/ /g; # Replace \s with space next if ( $line =~ m/^$/ ); next if ( $line =~ m/^#/ ); if ( $line =~ m/=/ ) { # Set a calculation rule ($name, $rule) = split (/=/, $line); $Rules{$name} = [split(/\+/, $rule)]; next; } # Set a comparison rule &diffRule($line); } close(FP); } sub dump_acct() { my ($i, $n, $key, $fmt, @fmt_head); printf "dump_acct:\n"; foreach $i ( @acctHeader ) { $fmt = sprintf("%%%ds ", length($i)); push @fmt_head, $fmt; printf $fmt, $i; } printf "\n"; foreach $key (sort keys %Acct) { $n = 0; foreach $i ( @{$Acct{$key}} ) { $fmt = $n <= $#fmt_head ? $fmt_head[$n] : " %10s"; $n++; printf $fmt, $i; } printf " '%s'", $key; if ( exists $Rules{$key} ) { printf " := %s", join(" + ", @{$Rules{$key}}); } printf "\n"; } } sub readAcct { # Read the *.acct file into hash $Acct with the function name as key. # The format of *.acct is : # X ... my ($fileName, @checkTime) = @_; my ($name, $i, $key, $line, @arr); # file *.acct is generated while the test program is running. if (!open(FP, "<$fileName")) { printf "acct::readAcct: Cannot open '%s'\n\n", $fileName; exit($ERROR_ACCT_MISMATCH); } while ($line = ) { # Skip the first lines (header) last if ( $line =~ m/^X\s+/ ); } @acctHeader = split (/\s+/, $line); push @acctHeader, "Comment"; while ($line = ) { chomp($line); $line =~ s/^\s*//; # Delete leading spaces next if ( $line =~ m/^$/ ); @arr = split (/\s+/, $line); $name = pop(@arr); if (defined $Acct{$name}) { for ($i = 1; $i <= $#arr; $i++ ) { $Acct{$name}[$i] += $arr[$i]; } } else { $Acct{$name} = [ @arr ]; } foreach $i ( @checkTime ) { next if ($i > $#arr); if ( $arr[$i] < 0 ) { &set_retVal($ERROR_NEGATIVE_TIME); last; } } } close(FP); &read_rules; # &checkCallersCallees; if ( $debug_f != 0 ) { printf "\nreadAcct: '%s'\n", $fileName; printf "checkTime: "; if( $#checkTime == -1 ) { printf "\n"; } else { print "[ ", join(", ", @checkTime), " ]\n"; } foreach $i ( @Comparison ) { print "Comparison rule: ", join(", ", @{$i}), "\n"; } &dump_acct; printf "\n"; } } sub read_er_print_out { my ($fileName, $colName) = @_; my ($name, @arr, $head_f, $line, $key, $i); $Erp = {}; $head_f = 1; open(FP, "<$fileName") or return; while ($line = ) { chomp($line); $line =~ s/^\s*//; # Delete leading spaces next if ( $line =~ m/^$/ ); if ($head_f == 1) { # Skip the first lines (header) next unless ( $line =~ m/^\d/ ); next unless ( ($line =~ m/\s*$/) || ($line =~ m/\s*$/) ); $head_f = 0; if ($colName == -1) { @arr = split (/\s+/, $line); $colName = $#arr + 1; } } @arr = split (/\s+/, $line, $colName); $name = pop(@arr); if (defined $Erp->{$name}) { for ($i = 0; $i <= $#arr; $i++ ) { $Erp->{$name}[$i] += $arr[$i]; } } else { $Erp->{$name} = [ @arr ]; } $i = index($name, "("); if ($i > 0) { my $funcName = substr($name, 0, $i); if (defined $Erp->{$funcName}) { for ($i = 0; $i <= $#arr; $i++ ) { $Erp->{$funcName}[$i] += $arr[$i]; } } else { $Erp->{$funcName} = [ @arr ]; } } } close(FP); if ( $debug_f != 0 ) { printf "read_er_print_out:\n"; foreach $key (sort keys %{$Erp}) { foreach $i ( @{$Erp->{$key}} ) { printf " %10s", $i; } printf " %-10s", "'$key'"; if ( exists $Rules{$key} ) { printf " += %s", join(" + ", @{$Rules{$key}}); } printf "\n"; } } } sub createKDiff { my ($colSample) = @_; my ($key, $str, $i, $head_str); open(DIFF_fp, ">diff.out"); $head_str = "X"; for $i ( 0..$#Comparison ) { $head_str .= &get_head_str($i); } $head_str .= " Name"; printf DIFF_fp "%s\n", $head_str; foreach $key (sort keys %Acct) { # Restore a hash 'Erp' $Erp = $hashSample[$Acct{$key}[$colSample]]; $str = &doComp($key, $head_str); printf DIFF_fp "%s (Sample %d)\n", $str,$Acct{$key}[$colSample]; } close(DIFF_fp); &closeDisFile(); } sub commandToScr1_fp() { my ($str) = @_; printf Scr1_fp "#\n#%s\n%s\n", $str, $str; } sub openFsingleScr { return if ($OpenFsingle == 1); open(Scr1_fp, ">>erp_fsingle.scr"); $OpenFsingle = 1; } sub closeFsingleScr { return if ($OpenFsingle != 1); $OpenFsingle = 2; close(Scr1_fp); } sub openDisFile { &openFsingleScr(); return if ($OpenDis == 1); open(Dis_fp, ">>discrepancy.out"); $OpenDis = 1; } sub closeDisFile { &closeFsingleScr(); return if ($OpenDis != 1); $OpenDis = 2; close(Dis_fp); } sub with_diff { my ($i) = @_; my ($key); $key = $Comparison[$i][0]; if( ! exists $RANGE{$key} ) { printf "acct::with_diff: '$key' is a wrong key\n\n"; exit $ERROR_PERL_ERROR; } if ($RANGE{$key}->{FMT} !~ m/^%d/) { return 1; } return 0; } sub get_head_str() { my ($i) = @_; my ($str); $str = $Comparison[$i][3]; while (length($str) < 16) { $str = "*" . $str . "*"; } if (with_diff($i)) { return sprintf("| %17s %7s %7s %s", $str, "Diff", "%", "x"); } else { return sprintf("| %17s %s", $str, "x"); } } sub doComp { my ($fname, $head_str) = @_; my ($key, $R, $r1, $r2, $diff, $rate, $flagX, $x, $i, $retStr, $discrepancy, $err_diff_range, $err_zero_metric, $err_acct_mismatch); sub setRate { my ($val, $diff) = @_; return sprintf("%6.1f", ($diff/$val)*100) if ( $val != 0 ); return sprintf("%6.1f", "0.0") if ( $diff >= -0.05 && $diff <= 0.05); return sprintf("%6.1f", "100") if ( $diff > 0 ); return sprintf("%6.1f", "-100"); } $err_diff_range = 0; $err_zero_metric = 0; $err_acct_mismatch = 0; $discrepancy = " "; $flagX = " "; $retStr = ""; for $i ( 0..$#Comparison ) { $r1 = $Acct{$fname}[$Comparison[$i][1]]; $r2 = 0; if ( ! exists $Rules{$fname} ) { if ( exists $Erp->{$fname} ) { $r2 = $Erp->{$fname}[$Comparison[$i][2]]; } } else { foreach my $key1 ( @{$Rules{$fname}} ) { my $sign = 1; $key = $key1; if (substr($key1, 0, 1) eq '-') { $key = substr($key1, 1); $sign = -1; } if ( exists $Erp->{$key} ) { $r2 += $sign * $Erp->{$key}[$Comparison[$i][2]]; } } } $key = $Comparison[$i][0]; if( ! exists $RANGE{$key} ) { printf "acct::doComp: '$key' is a wrong key\n\n"; exit $ERROR_PERL_ERROR; } $R = $RANGE{$key}; $r1 = sprintf($R->{FMT}, $r1); $r2 = sprintf($R->{FMT}, $r2); $diff = sprintf($R->{FMT}, $r1 - $r2); $rate = &setRate($r1, $diff); if ((( $diff > $R->{P_RANGE} ) && ( $rate >= $R->{P_RATE} )) || ( ( $fname ne '') && ( $diff < $R->{N_RANGE} ) && ( $rate <= $R->{N_RATE} ))) { $x = ($Acct{$fname}[0] eq "Y") ? "y" : "x"; if ( $x ne "y" ) { $flagX = "X"; &openDisFile(); printf Dis_fp "%s/ %s\n", $fname, $Comparison[$i][3]; $discrepancy .= " $Comparison[$i][3]"; if (with_diff($i)) { if ( $r2 > 0 ) { $err_diff_range = $ERROR_DIFF_RANGE; } else { $err_zero_metric = $ERROR_ZERO_METRIC; } } else { $err_acct_mismatch = $ERROR_ACCT_MISMATCH; } } } else { $x = " "; } if (with_diff($i)) { $retStr .= sprintf("| %8s %8s %7s %7s %s", $r1, $r2, $diff, $rate, $x); } else { $retStr .= sprintf("| %8s %8s %s", $r1, $r2, $x); } } $retStr = $flagX . $retStr . sprintf(" %-10s", $fname); if ( exists $Rules{$fname} ) { $retStr .= sprintf " := %s", join(" + ", @{$Rules{$fname}}); } if ($discrepancy ne " ") { if ($err_acct_mismatch != 0) { $retVal = $err_acct_mismatch; } &set_retVal($err_zero_metric); &set_retVal($err_diff_range); printf Scr1_fp "#%s\n#%s\n", $head_str, $retStr; &commandToScr1_fp(sprintf("%s %s 1", 'fsingle', $fname)); &commandToScr1_fp(sprintf("%s %s 1", 'csingle', $fname)); } return ($retStr); } sub doComp2AVG { my ($fname, $head_str, @avg) = @_; my ($key, $R, $r1, $r2, $diff, $rate, $flagX, $x, $i, $retStr, $discrepancy, $err_diff_range, $err_zero_metric, $err_acct_mismatch); sub setRate { my ($val, $diff) = @_; return sprintf("%6.1f", ($diff/$val)*100) if ( $val != 0 ); return sprintf("%6.1f", "0.0") if ( $diff >= -0.05 && $diff <= 0.05); return sprintf("%6.1f", "100") if ( $diff > 0 ); return sprintf("%6.1f", "-100"); } $err_diff_range = 0; $err_zero_metric = 0; $err_acct_mismatch = 0; $discrepancy = " "; $flagX = " "; $retStr = ""; for $i ( 0..$#Comparison ) { $r1 = $avg[$i]; $r2 = 0; if ( ! exists $Rules{$fname} ) { if ( exists $Erp->{$fname} ) { $r2 = $Erp->{$fname}[$Comparison[$i][2]]; } } else { foreach my $key1 ( @{$Rules{$fname}} ) { my $sign = 1; $key = $key1; if (substr($key1, 0, 1) eq '-') { $key = substr($key1, 1); $sign = -1; } if ( exists $Erp->{$key} ) { $r2 += $sign * $Erp->{$key}[$Comparison[$i][2]]; } } } $key = $Comparison[$i][0]; if( ! exists $RANGE{$key} ) { printf "acct::doComp: '$key' is a wrong key\n\n"; exit $ERROR_PERL_ERROR; } $R = $RANGE{$key}; $r1 = sprintf($R->{FMT}, $r1); $r2 = sprintf($R->{FMT}, $r2); $diff = sprintf($R->{FMT}, $r1 - $r2); $rate = &setRate($r1, $diff); if ((( $diff > $R->{P_RANGE_2AVG} ) && ( $rate >= $R->{P_RATE_2AVG} )) || ( ( $fname ne '') && ( $diff < $R->{N_RANGE_2AVG} ) && ( $rate <= $R->{N_RATE_2AVG} ))) { $flagX = "X"; $x = "x"; $discrepancy .= " $Comparison[$i][3]"; if (with_diff($i)) { if ( $r2 > 0 ) { $err_diff_range = $ERROR_DIFF_RANGE; } else { $err_zero_metric = $ERROR_ZERO_METRIC; } } else { $err_acct_mismatch = $ERROR_ACCT_MISMATCH; } } else { $x = " "; } if (with_diff($i)) { $retStr .= sprintf("| %8s %8s %7s %7s %s", $r1, $r2, $diff, $rate, $x); } else { $retStr .= sprintf("| %8s %8s %s", $r1, $r2, $x); } } $retStr = $flagX . $retStr . sprintf(" %-10s", $fname); if ( exists $Rules{$fname} ) { $retStr .= sprintf " := %s", join(" + ", @{$Rules{$fname}}); } if ($discrepancy ne " ") { if ($err_acct_mismatch != 0) { $retVal = $err_acct_mismatch; } &set_retVal($err_zero_metric); &set_retVal($err_diff_range); &openDisFile(); printf Scr1_fp "#%s\n#%s\n", $head_str, $retStr; &commandToScr1_fp(sprintf("%s %s 1", 'fsingle', $fname)); printf Dis_fp "%s/%s\n", $fname, $discrepancy; } else { } return ($retStr); } sub checkUnknown() { my ($total, $i, $R); sub checkUnknRate() { my ($name, $N) = @_; my ($val, $rate, $fmt); $val = $Erp->{$name}[$Comparison[$N][2]]; $val = sprintf($R->{FMT}, $val); $rate = sprintf($R->{FMT},($val / $total) * 100); if (($val > $R->{'P_RANGE'}) && ($rate > $R->{'P_RATE'})) { &set_retVal($ERROR_HIGH_UNKNOWN); &openFsingleScr(); $fmt = "#%-8s %10s %10s %s\n"; printf Scr1_fp $fmt, $Comparison[$N][0], '%', '', $name; printf Scr1_fp $fmt, ' ', $rate, $total, $val; &commandToScr1_fp(sprintf("%s %s 1", 'fsingle', '')); &commandToScr1_fp(sprintf("%s %s 1", 'csingle', '')); &commandToScr1_fp(sprintf("%s %s 1", 'fsingle', $name)); &commandToScr1_fp(sprintf("%s %s 1", 'csingle', $name)); &closeFsingleScr(); return 1; } return 0; } return if ( ! exists $Erp->{''} ); return if ( $ENV{NOJAVA} ); $R = $RANGE{'Unkn'}; for $i ( 0..$#Comparison ) { $total = $Erp->{''}[$Comparison[$i][2]]; next if ( $total == 0 ); $total = sprintf($R->{FMT}, $total); # last if &checkUnknRate('', $i); last if &checkUnknRate('', $i); last if &checkUnknRate('diff.out"); $head_str = " "; for $i ( 0..$#Comparison ) { printf DIFF_fp "Comparison[%d]: %s,%d,%d\n", $i, $Comparison[$i][0], $Comparison[$i][1], $Comparison[$i][2], $Comparison[$i][3]; $head_str .= &get_head_str($i); } printf DIFF_fp "\nX| Compare the acct file (first column) with the er_print output (second column):\n"; $head_str .= " Name"; printf DIFF_fp "%s\n", $head_str; foreach $key (sort keys %Acct) { $str = &doComp($key, $head_str); printf DIFF_fp "%s\n", $str; } &checkCallersCallees; close(DIFF_fp); &closeDisFile(); return -s "discrepancy.out" } sub createDiff2AVG { my ($key, $str, $i, $n, $head_str, @avg, $temp, $fname); &checkUnknown(); open(DIFF_fp, ">>diff.out"); printf DIFF_fp "\n==================\n"; $head_str = " "; for $i ( 0..$#Comparison ) { printf DIFF_fp "Comparison[%d]: %s,%d\n", $i, $Comparison[$i][0], $Comparison[$i][2]; $head_str .= &get_head_str($i); } printf DIFF_fp "\n#| Compare the avg value (first column) with the er_print output (second column):\n"; $head_str .= " Name"; printf DIFF_fp "%s\n", $head_str; for $i ( 0..$#Comparison ) { $avg[$i] = 0; } $n=0; foreach $fname (sort keys %Acct) { $n++; for $i ( 0..$#Comparison ) { if ( ! exists $Rules{$fname} ) { if ( exists $Erp->{$fname} ) { $temp = $Erp->{$fname}[$Comparison[$i][2]]; } } else { foreach my $key1 ( @{$Rules{$fname}} ) { my $sign = 1; $key = $key1; if (substr($key1, 0, 1) eq '-') { $key = substr($key1, 1); $sign = -1; } if ( exists $Erp->{$key} ) { $temp += $sign * $Erp->{$key}[$Comparison[$i][2]]; } } } $avg[$i] += $temp; } } for $i ( 0..$#Comparison ) { $avg[$i] /= $n; } foreach $key (sort keys %Acct) { $str = &doComp2AVG($key, $head_str, @avg); printf DIFF_fp "%s\n", $str; } close(DIFF_fp); &closeDisFile(); } sub sumOutlinedCode { # Add a time of the outlined code. my ($name, $eName); foreach $name (keys %Acct) { foreach $eName (keys %$Erp) { next if ("$eName" !~ m/^($name)\s--/); if (defined $Rules{$name}) { push @{$Rules{$name}}, $eName; } else { $Rules{$name} = [$eName]; } } } } sub checkCallersCallees { my (@arr, $name, $colName, $line, $nline, %Calls); open(FP, ") { last if ( $line =~ m/\s+sec.\s+/ ); } $nline = 0; while ($line = ) { chomp($line); $line =~ s/^\s*//; # Delete leading spaces next if ( $line =~ m/^$/ ); @arr = split (/\s+/, $line, $colName); $name = pop(@arr); # New Callers-Callees format does not have * in the Stack Fragment section # - translate old format to new format for compatibility if ($name eq "*MAIN") { $name = "MAIN"; }; last if ($name eq "MAIN"); $nline += 1; } if ($nline == 0) { printf "checkCallersCallees: No Callers of MAIN\n"; &set_retVal($ERROR_CALLER_VERIF); close(FP); return; } while ($line = ) { chomp($line); $line =~ s/^\s*//; # Delete leading spaces next if ( $line =~ m/^$/ ); @arr = split (/\s+/, $line, $colName); $name = pop(@arr); $Calls{$name} = 1; if ( $line =~ /Parallel/ ) { #f90synprog M_EXPERT or M_MACHINE @arr = split (/\s\s+/, $line, $colName); $name = pop(@arr); @arr = split (/\s/, $name); $Calls{$arr[0]} = 1; } } close(FP); foreach $name (sort keys %Acct) { next if ( $name eq '' ) ; next if ( $name eq '' ) ; next if (defined $Calls{$name}) ; printf "checkCallersCallees: '$name' is not inside callees\n"; &set_retVal($ERROR_CALLER_VERIF); } } return 1; END{}