This is the mail archive of the
gdb-patches@sourceware.cygnus.com
mailing list for the GDB project.
regression analysis: gdb.sum postprocessing script
- To: gdb-patches at sourceware dot cygnus dot com
- Subject: regression analysis: gdb.sum postprocessing script
- From: Jimmy Guo <guo at cup dot hp dot com>
- Date: Fri, 24 Mar 2000 11:15:45 -0800 (PST)
Here is a Perl script I wrote to postprocess one or more gdb.sum files
and provide a complete dejagnu test outcome summary (or diff if more
than one gdb.sum files are given). It's most useful if you want to have
a complete view of regression status between two source trees and/or if
you set up your testing to run multiple passes (to see how a test point
behaves in different passes), however you can use it just on one test
run's gdb.sum file and play around with the options to get test outcome
presented in a much organized and useful way than going through the flat
gdb.sum file.
I don't know if this is useful to keep around in the repository ...
- Jimmy Guo, guo@cup.hp.com
Index: gdbsum
/usr/local/bin/diff -c -w -L gdbsum gdbsum.orig gdbsum
*** gdbsum.orig
--- gdbsum Wed Jan 26 16:48:40 2000
***************
*** 0 ****
--- 1,1050 ----
+ #!/usr/local/bin/perl
+
+ #
+ # gdbsum - smart gdb.sum filter / merge / diff utility
+ #
+ # Command line:
+ # gdbsum [<options>] <string label> <gdb.sum> [<string label> <gdb.sum> ...]
+ #
+ # <options>:
+ # [-no-f/ail] don't list FAIL
+ # [-no-xfa/il] don't list XFAIL
+ # [-no-xpa/ss] don't list XPASS
+ # [-no-unr/esolved] don't list UNRESOLVED
+ # [-no-ski/pped] don't list skipped tests
+ # [-no-oth/er] don't list unclassfiable outputs
+ # [-runpass <string>] list only the run pass(es) specified in the
+ # space delimited run pass list (string).
+ #
+ # - the default is to list all
+ #
+ # <string label> identifier for the run corresponding to <gdb.sum>
+ # <gdb.sum> pathname of gdb.sum file
+ #
+ # - if one <string label> <gdb.sum> pair specified, produce filtered test
+ # result to stdout
+ # - if multiple <string label> <gdb.sum> pairs specified, produce a
+ # difference of filtered test result to stdout
+ #
+
+ # initialize
+ #
+ &init;
+
+ # parse command line args
+ #
+ &parseCmdLine;
+ $do_diff = 1 if ($#sum_ids > 0);
+
+ # parse all gdb.sum files specified
+ #
+ $sum_seq = 0;
+ while ($sum_seq <= $#sum_ids) {
+ &parseSumFile($sum_seq, $sum_files[$sum_seq]);
+ $sum_seq++;
+ }
+
+ # merge all passes from multiple runs
+ #
+ $tmp = " $passes[0] ";
+ @allpasses = split(/\s+/, $passes[0]);
+ for ($sum_seq = 1; $sum_seq <= $#sum_ids; $sum_seq++) {
+ @tmps = split(/\s+/, $passes[$sum_seq]);
+ foreach $pass (@tmps) {
+ push(@allpasses, $pass)
+ if (index($tmp, " $pass ") == -1);
+ }
+ $tmp = ' '.join(" ", @allpasses).' ';
+ }
+ @allpasses = sort @allpasses;
+
+ # merge all tests from multiple runs
+ #
+ $tmp = " $tests[0] ";
+ @alltests = split(/\s+/, $tests[0]);
+ for ($sum_seq = 1; $sum_seq <= $#sum_ids; $sum_seq++) {
+ @tmps = split(/\s+/, $tests[$sum_seq]);
+ foreach $test (@tmps) {
+ push(@alltests, $test)
+ if (index($tmp, " $test ") == -1);
+ }
+ $tmp = ' '.join(" ", @alltests).' ';
+ }
+ @alltests = sort @alltests;
+
+ # print filtered / diff'd output to stdout
+ #
+ print $errbuf if ($errbuf ne '');
+ for ($sum_seq = 0; $sum_seq <= $#sum_ids; $sum_seq++) {
+ print " <$sum_seq>:" if ($do_diff);
+ print " $sum_ids[$sum_seq]\n";
+ print " # of expected passes $p_cnts[$sum_seq]\n";
+ print " # of unexpected failures $f_cnts[$sum_seq]\n";
+ print " # of unexpected successes $xpa_cnts[$sum_seq]\n";
+ print " # of expected failures $xfa_cnts[$sum_seq]\n";
+ print " # of unresolved testcases $unr_cnts[$sum_seq]\n";
+ print " # of unsupported tests $ski_cnts[$sum_seq]\n";
+ }
+ print "\n";
+
+ # list 'others' first
+ # these are usually caused by test / system problems
+ # that need to be resolved
+ #
+ &printOthers if (! $no_other);
+
+ # list FAIL, XPASS, XFAIL, and UNRESOLVED test entries
+ #
+ &printEntries if (! ($no_fail && $no_xfail && no_xpass && $no_unr));
+
+ # list skipped tests
+ #
+ &printSkipped if (! $no_ski);
+
+ # done
+ #
+ exit 0;
+
+
+ #
+ # init
+ #
+ # Function: - initialize program
+ # Accepts: - (none)
+ # Returns: - (initializes global variables)
+ #
+ sub init
+ {
+ $this = $0;
+ $this =~ s/^.*\/([^\/]+)$/$1/;
+
+ # listing options
+ #
+ $no_fail =
+ $no_xfail =
+ $no_xpass =
+ $no_unr =
+ $no_ski =
+ $no_other = 0;
+
+ # space delimited list of run passes to provide summary on
+ #
+ $runpass = '';
+
+ # gdb.sum ids
+ #
+ @sum_ids = ();
+
+ # gdb.sum file pathnames
+ #
+ @sum_files = ();
+
+ # test result anchor
+ #
+ $anchor = '=== gdb Summary ===';
+
+ # error message buffer
+ #
+ $errbuf = '';
+
+ # diff'd output?
+ #
+ $do_diff = 0;
+
+ # array of all multipass passes seen
+ # indexed by <sum_seq>
+ # value is space-delimited string of pass names
+ @passes = ();
+
+ # array of all tests seen
+ # indexed by '<sum_seq>'
+ # value is space-delimited list of test pathnames
+ #
+ @tests = ();
+
+ # hash of test point entries and outcome
+ # keyed by '<sum_seq> <pass> <test>'
+ # value is '<pcode> <entry_name>'
+ #
+ %entries = ();
+
+ # hash of unclassifiable errors
+ # keyed by '<sum_seq> <pass> <test>'
+ # value is multiline output
+ #
+ %others = ();
+
+ # hash of test point entries and outcome for all passes
+ # keyed by '<test>'
+ # value is '(<pcode> )+<entry_name>'
+ #
+ %allentries = ();
+
+ # array of all multipass passes seen in all runs
+ #
+ @allpasses = ();
+
+ # array of all tests seen in all runs
+ #
+ @alltests = ();
+
+ # array of counts of all PASS for a gdb.sum file
+ # indexed by '<sum_seq>'
+ # value is count
+ #
+ @p_cnts = ();
+
+ # array of counts of all FAIL for a gdb.sum file
+ # indexed by '<sum_seq>'
+ # value is count
+ #
+ @f_cnts = ();
+
+ # array of counts of all XPASS for a gdb.sum file
+ # indexed by '<sum_seq>'
+ # value is count
+ #
+ @xpa_cnts = ();
+
+ # array of counts of all XFAIL for a gdb.sum file
+ # indexed by '<sum_seq>'
+ # value is count
+ #
+ @xfa_cnts = ();
+
+ # array of counts of all UNRESOLVED tests for a gdb.sum file
+ # indexed by '<sum_seq>'
+ # value is count
+ #
+ @unr_cnts = ();
+
+ # array of counts of all skipped (unsupported) tests for a gdb.sum file
+ # indexed by '<sum_seq>'
+ # value is count
+ #
+ @ski_cnts = ();
+ }
+
+ #
+ # parseCmdLine
+ #
+ # Function: - parse program command line arguments
+ # Accepts: - (none)
+ # Returns: - (none)
+ #
+ sub parseCmdLine
+ {
+ local($arg, $arg2, $seen_quick);
+
+ &usage('') if ($#ARGV < 1);
+
+ $seen_quick = 0;
+
+ while ($#ARGV >= 0) {
+ $arg = shift @ARGV;
+
+ if ($arg =~ /^-no-f/) {
+ if ($seen_quick) {
+ $errbuf .= "(warning): -quick overrides -no-fail\n";
+ } else {
+ $no_fail = 1;
+ }
+ } elsif ($arg =~ /^-no-xfa/) {
+ $no_xfail = 1;
+ } elsif ($arg =~ /^-no-xpa/) {
+ $no_xpass = 1;
+ } elsif ($arg =~ /^-no-unr/) {
+ $no_unr = 1;
+ } elsif ($arg =~ /^-no-ski/) {
+ $no_ski = 1;
+ } elsif ($arg =~ /^-no-oth/) {
+ $no_other = 1;
+ } elsif ($arg =~ /^-quick/) {
+ $errbuf .= "(warning): -quick overrides -no-fail\n"
+ if ($no_fail);
+ $no_fail = 0;
+ $no_xfail = $no_xpass = $no_unr = $no_ski = $no_other = 1;
+ $seen_quick = 1;
+ } elsif ($arg eq '-runpass') {
+ &usage("(error): need -runpass <string> argument pair")
+ if ($#ARGV == -1);
+ $runpass = shift @ARGV;
+ $runpass = ' '.$runpass.' ';
+ } else {
+ &usage("(error): need <string label> <gdb.sum> argument pair")
+ if ($#ARGV == -1);
+ $arg2 = shift @ARGV;
+ &usage("(error): cannot read gdb.sum file '$arg2'")
+ if ((! -f $arg2) || (! -r $arg2));
+ system("grep -q '$anchor' $arg2");
+ $rc = ($? >> 8);
+ if ($rc) {
+ $errbuf .= "(warning): $arg2 ($arg) incomplete, skipped ...\n";
+ } else {
+ push(@sum_ids, $arg);
+ push(@sum_files, $arg2);
+ }
+ }
+ }
+
+ &usage("$errbuf"."(error): need <string label> <gdb.sum> argument pair")
+ if ($#sum_ids == -1);
+
+ print STDERR "$this $errbuf" if ($errbuf ne '');
+ }
+
+
+ #
+ # parseSumFile
+ #
+ # Function: - parse a gdb.sum file and stuff test result info into
+ # hash tables
+ # Accepts: - sum_seq: sequence number of gdb.sum file
+ # - sum_file: pathname of gdb.sum file
+ # Returns: - (modifies global hash tables)
+ #
+ sub parseSumFile
+ {
+ local($sum_seq, $sum_file) = @_;
+ local($pass, $test, $testname, $entry);
+ local($skippass);
+
+ open(SUMFILE, $sum_file) ||
+ die "$this: (error): cannot read gdb.sum file '$sum_file'";
+
+ # initialize counts
+ #
+ $p_cnts[$sum_seq] =
+ $f_cnts[$sum_seq] =
+ $xpa_cnts[$sum_seq] =
+ $xfa_cnts[$sum_seq] =
+ $unr_cnts[$sum_seq] =
+ $ski_cnts[$sum_seq] = 0;
+
+ # skip the gdb.sum header
+ #
+ $pass = ' ';
+ $test = '';
+ $skippass = 0;
+
+ while (<SUMFILE>) {
+ chop;
+
+ if (/^Running pass \`(.+)\'\s+\.\.\.\s*$/) {
+ # seen a multipass pass
+ # record the pass
+ #
+ if (($runpass ne '') && (index($runpass, " $1 ") == -1)) {
+ $skippass = 1;
+ next;
+ } else {
+ $skippass = 0;
+ $pass = $1;
+ $passes[$sum_seq] = $pass;
+ }
+ } elsif ($skippass) {
+ # we've seen a multipass pass, and we skipped it since it's
+ # not what the user wanted us to summarize on.
+ # skip all info associated with the skipped pass until we
+ # see the next pass.
+ #
+ next;
+ } elsif (/^Running.+testsuite\/(.+)\s+\.\.\.\s*$/) {
+ # seen the first test
+ # $test is pathname of test under testsuite/
+ #
+ $test = $1;
+
+ # $testname is what runtest spits out, e.g.
+ # if $test is gdb.hp/gdb.base-hp/foo.exp,
+ # $testname is gdb.base-hp/foo.exp
+ # (this is to handle the unexpected case where
+ # a directory's basename is the same as another directory's)
+ #
+ $testname = $test;
+ $testname = $1 if ($testname =~ /\/([^\/]+\/[^\/]+)$/);
+ #
+ # escape regexp characters in $testname, e.g. c++ -> c\+\+,
+ # since we need to use $testname in regexp pattern matching later on
+ #
+ $testname =~ s/\+/\\+/g;
+ $testname =~ s/\?/\\?/g;
+ $testname =~ s/\*/\\*/g;
+
+ # break out of loop since we've found the starting location
+ # of test result summary
+ last;
+ }
+ }
+
+ # if we didn't find any test, just return
+ #
+ return if ($test eq '');
+
+ # if we've skipped to the end of the file without finding
+ # any pass to start on, just return.
+ #
+ return if ($skippass);
+ $skippass = 0;
+
+ # for no-multipass add the '1' pass identifier
+ # (this is to handle the case when multipass is not defined for the test run)
+ #
+ if ($pass eq ' ') {
+ $pass = '1';
+ $passes[$sum_seq] = $pass;
+ }
+
+ # initialize %tests, %entries, and %others hash table entries
+ #
+ &initTestInfo($sum_seq, $pass, $test);
+
+ # parse each line of test result summary until done
+ #
+ while (<SUMFILE>) {
+ chop;
+
+ if (/^\s*$/) {
+ # empty line, ignore
+ #
+ next;
+ } elsif (/^Running pass \`(.+)\'\s+\.\.\.\s*$/) {
+ # another multipass pass
+ #
+
+ # chop the last \n from %entries and %others hash table entries
+ # (this is for the last test in this multipass pass)
+ # ... we must only do this once
+ #
+ if (! $skippass) {
+ if (defined($entries{"$sum_seq $pass $test"})) {
+ chop $entries{"$sum_seq $pass $test"};
+ $ski_cnts[$sum_seq]++
+ if ($entries{"$sum_seq $pass $test"} eq '');
+ }
+
+ if (defined($others{"$sum_seq $pass $test"})) {
+ chop $others{"$sum_seq $pass $test"};
+ }
+ }
+
+ if (($runpass ne '') && (index($runpass, " $1 ") == -1)) {
+ $skippass = 1;
+ next;
+ } else {
+ $skippass = 0;
+ $pass = $1;
+ $passes[$sum_seq] .= ' '.$pass;
+ }
+ } elsif (/^\s+$anchor\s*$/) {
+ # end of test result body
+ #
+
+ # chop the last \n from %entries and %others hash table entries
+ # (this is for the last test in the whole test run)
+ # ... we must only do this once
+ #
+ if (! $skippass) {
+ if (defined($entries{"$sum_seq $pass $test"})) {
+ chop $entries{"$sum_seq $pass $test"};
+ $ski_cnts[$sum_seq]++
+ if ($entries{"$sum_seq $pass $test"} eq '');
+ }
+
+ if (defined($others{"$sum_seq $pass $test"})) {
+ chop $others{"$sum_seq $pass $test"};
+ }
+ }
+
+ # we're done parsing this file
+ #
+ last;
+ } elsif ($skippass) {
+ # we've seen a multipass pass, and we skipped it since it's
+ # not what the user wanted us to summarize on.
+ # skip all info associated with the skipped pass until we
+ # see the next pass.
+ #
+ next;
+ } elsif (/^Running.+testsuite\/(.+)\s+\.\.\.\s*$/) {
+ # another test
+ #
+
+ # chop the last \n from %entries and %others hash table entries
+ #
+ if (defined($entries{"$sum_seq $pass $test"})) {
+ chop $entries{"$sum_seq $pass $test"};
+ $ski_cnts[$sum_seq]++
+ if ($entries{"$sum_seq $pass $test"} eq '');
+ }
+
+ if (defined($others{"$sum_seq $pass $test"})) {
+ chop $others{"$sum_seq $pass $test"};
+ }
+
+ # setup for the next test
+ #
+ $test = $1;
+
+ $testname = $test;
+ $testname = $1 if ($testname =~ /\/([^\/]+\/[^\/]+)$/);
+ $testname =~ s/\+/\\+/g;
+ $testname =~ s/\?/\\?/g;
+ $testname =~ s/\*/\\*/g;
+
+ &initTestInfo($sum_seq, $pass, $test);
+ } elsif (/^PASS:.*$testname: (.+)\s*$/) {
+ # PASS
+ #
+ $p_cnts[$sum_seq]++;
+ $entry = $1;
+ $entry =~ s/\s+/ /g;
+ $entries{"$sum_seq $pass $test"} .= "P $entry\n";
+ } elsif (/^FAIL:.*$testname: (.+)\s*$/) {
+ # FAIL
+ #
+ $f_cnts[$sum_seq]++;
+ $entry = $1;
+ $entry =~ s/\s+/ /g;
+ if ($entry =~ /\(timeout\)/) {
+ $entry =~ s/ \(timeout\)//;
+ $entry =~ s/\(timeout\) //;
+ $entries{"$sum_seq $pass $test"} .= "FTM $entry\n";
+ } else {
+ $entries{"$sum_seq $pass $test"} .= "F $entry\n";
+ }
+ } elsif (/^XFAIL:.*$testname: (.+)\s*$/) {
+ # XFAIL
+ #
+ $xfa_cnts[$sum_seq]++;
+ $entry = $1;
+ $entry =~ s/\s+/ /g;
+ $entry =~ s/ \(PRMS [^\)]+\)//;
+ $entry =~ s/\(PRMS [^\)]+\) //;
+ if ($entry =~ /\(timeout\)/) {
+ $entry =~ s/ \(timeout\)//;
+ $entry =~ s/\(timeout\) //;
+ $entries{"$sum_seq $pass $test"} .= "XTM $entry\n";
+ } else {
+ $entries{"$sum_seq $pass $test"} .= "XFA $entry\n";
+ }
+ } elsif (/^XPASS:.*$testname: (.+)\s*$/) {
+ # XPASS
+ #
+ $xpa_cnts[$sum_seq]++;
+ $entry = $1;
+ $entry =~ s/\s+/ /g;
+ $entry =~ s/ \(PRMS [^\)]+\)//;
+ $entry =~ s/\(PRMS [^\)]+\) //;
+ $entries{"$sum_seq $pass $test"} .= "XPA $entry\n";
+ } elsif (/^UNRESOLVED:.*$testname: (.+)\s*$/) {
+ # UNRESOLVED
+ #
+ $unr_cnts[$sum_seq]++;
+ $entry = $1;
+ $entry =~ s/\s+/ /g;
+ $entries{"$sum_seq $pass $test"} .= "UNR $entry\n";
+ } else {
+ # other unclassifiable line, e.g.
+ # verbose compilation failures, ERRORs, etc.
+ #
+ if (defined($others{"$sum_seq $pass $test"})) {
+ # only interested in specific test related junks,
+ # in case junks are seen after a multipass id line but
+ # before a test id line
+ #
+ $others{"$sum_seq $pass $test"} .= $_."\n";
+ }
+ }
+ }
+
+ close(SUMFILE);
+ }
+
+
+ #
+ # initTestInfo
+ #
+ # Function: - initialize test information variables
+ # Accepts: - sum_seq: gdb.sum file sequence number
+ # - pass: multipass pass id
+ # - test: test pathname
+ # Returns: - (modifies global hash tables)
+ #
+ sub initTestInfo
+ {
+ local($sum_seq, $pass, $test) = @_;
+
+ if (! defined($tests[$sum_seq])) {
+ $tests[$sum_seq] = $test;
+ } else {
+ $tests[$sum_seq] .= ' '.$test
+ if (index(" $tests[$sum_seq] ", " $test ") == -1);
+ }
+
+ $entries{"$sum_seq $pass $test"} = '';
+ $others{"$sum_seq $pass $test"} = '';
+ }
+
+
+ #
+ # printOthers
+ #
+ # Function: - prints unclassifiable test result output
+ # Accepts: - (none)
+ # Returns: - (none)
+ #
+ sub printOthers
+ {
+ local($sum_seq);
+ local($seen, $othcnt, $prevbuf, $printed, $pass, $indent);
+
+ $indent = 8;
+ $indent += 5 if ($do_diff);
+
+ &printHeader("Unknown", '');
+ foreach $test (@alltests) {
+ $printed = 0;
+ foreach $pass (@allpasses) {
+ $seen = 0;
+ $othcnt = 0;
+ $prevbuf = '';
+ for ($sum_seq = 0; $sum_seq <= $#sum_ids; $sum_seq++) {
+ if (defined($others{"$sum_seq $pass $test"})
+ &&
+ ($others{"$sum_seq $pass $test"} ne '')) {
+ $othcnt++;
+ if ($sum_seq == 0) {
+ $prevbuf = $others{"$sum_seq $pass $test"};
+ } elsif ($prevbuf ne $others{"$sum_seq $pass $test"}) {
+ $seen = 1;
+ last;
+ }
+ }
+ }
+ $seen = 1 if (($othcnt != 0)
+ &&
+ ((! $do_diff) || ($othcnt != $#sum_ids+1)));
+ if ($seen) {
+ print "$test\n" if (! $printed);
+ $printed = 1;
+ for ($sum_seq = 0; $sum_seq <= $#sum_ids; $sum_seq++) {
+ if (! defined($others{"$sum_seq $pass $test"})) {
+ if ($do_diff) {
+ print " <$sum_seq>:";
+ print &listPcodes($pass);
+ print "<TEST NOT RUN>\n";
+ }
+ } elsif ($others{"$sum_seq $pass $test"} eq '') {
+ if ($do_diff) {
+ print " <$sum_seq>:";
+ print &listPcodes($pass);
+ print "<NO UNKNOWN OUTPUT>\n";
+ }
+ } else {
+ print " <$sum_seq>:" if ($do_diff);
+ print &listPcodes($pass);
+ print join("\n".(' 'x$indent),
+ split(/\n+/, $others{"$sum_seq $pass $test"}));
+ print "\n";
+ }
+ }
+ print "\n";
+ }
+ }
+ }
+ }
+
+
+ #
+ # printEntries
+ #
+ # Function: - prints FAIL/XFAIL/XPASS/UNRESOLVED test entries
+ # Accepts: - (none)
+ # Returns: - (none)
+ #
+ sub printEntries
+ {
+ local($tmp, $test);
+
+ $tmp = '';
+ $tmp .= 'FAIL / ' if (! $no_fail);
+ $tmp .= 'XFAIL / ' if (! $no_xfail);
+ $tmp .= 'XPASS / ' if (! $no_xpass);
+ $tmp .= 'UNRESOLVED / ' if (! $no_xpass);
+ chop $tmp; chop $tmp; chop $tmp;
+
+ &printHeader($tmp,
+ '(P:PASS F:FAIL FTM:FAIL/timeout XFA:XFAIL XTM:XFAIL/timeout XPA:XPASS UNR:UNRESOLVED SKI:skipped \'-\':not run)');
+
+ &mergeEntries;
+
+ foreach $test (@alltests) {
+ if ((defined($allentries{$test})) && ($allentries{$test} ne '')) {
+ print "$test\n$allentries{$test}";
+ print "\n" if (! $do_diff);
+ }
+ }
+ }
+
+
+ #
+ # printSkipped
+ #
+ # Function: - prints skipped tests
+ # Accepts: - (none)
+ # Returns: - (none)
+ #
+ sub printSkipped
+ {
+ local($sum_seq);
+ local($seen, $skicnt, $pass, @pcodes);
+
+ &printHeader('Skipped', 'ran:Tested SKI:Skipped \'-\':Not run');
+ foreach $test (@alltests) {
+ $seen = 0;
+ foreach $pass (@allpasses) {
+ $skicnt = 0;
+ for ($sum_seq = 0; $sum_seq <= $#sum_ids; $sum_seq++) {
+ if ((defined($entries{"$sum_seq $pass $test"}))
+ &&
+ ($entries{"$sum_seq $pass $test"} eq '')) {
+ $skicnt++;
+ }
+ }
+ $seen = 1 if (($skicnt != 0)
+ &&
+ ((! $do_diff) || ($skicnt != $#sum_ids+1)));
+ last if ($seen);
+ }
+ if ($seen) {
+ print "$test\n";
+ for ($sum_seq = 0; $sum_seq <= $#sum_ids; $sum_seq++) {
+ @pcodes = ();
+ foreach $pass (@allpasses) {
+ if (! defined($entries{"$sum_seq $pass $test"})) {
+ push(@pcodes, '-');
+ } elsif ($entries{"$sum_seq $pass $test"} eq '') {
+ push(@pcodes, 'SKI');
+ } else {
+ push(@pcodes, 'ran');
+ }
+ }
+ print " <$sum_seq>:" if ($do_diff);
+ print &listPcodes(@pcodes);
+ print "\n";
+ }
+ print "\n";
+ }
+ }
+ }
+
+
+ #
+ # mergeEntries
+ #
+ # Function: - merge %entries hash into %allentries hash for print
+ # Accepts: - (none)
+ # Returns: - (modifies global hash tables)
+ #
+ sub mergeEntries
+ {
+ local($sum_seq, $test, $pass);
+ local(@allentrynames);
+ local(@entrybufs, $pcode, $entryname);
+ local(%entrylookup);
+ local($failcnt, $failtmcnt, $xfailcnt, $xpasscnt, $unrcnt);
+ local($i, $j, $seen);
+ local(@pcodes);
+
+ foreach $test (@alltests) {
+ $allentries{$test} = '';
+
+ # merge all entry names from all passes of all runs for $test
+ #
+ @allentrynames = ();
+ for ($sum_seq = 0; $sum_seq <= $#sum_ids; $sum_seq++) {
+ foreach $pass (@allpasses) {
+ @allentrynames = &mergeEntryNames($sum_seq, $pass, $test,
+ @allentrynames);
+ }
+ }
+ next if ($#allentrynames == -1);
+
+ # create entryname -> pcode lookup hash grouped by sum_seq and pass id
+ #
+ %entrylookup = ();
+ for ($sum_seq = 0; $sum_seq <= $#sum_ids; $sum_seq++) {
+ foreach $pass (@allpasses) {
+ if (! defined($entries{"$sum_seq $pass $test"})) {
+ for ($i = 0; $i <= $#allentrynames; $i++) {
+ $entrylookup{"$sum_seq $pass $i $allentrynames[$i]"} = '-';
+ }
+ } elsif ($entries{"$sum_seq $pass $test"} eq '') {
+ for ($i = 0; $i <= $#allentrynames; $i++) {
+ $entrylookup{"$sum_seq $pass $i $allentrynames[$i]"} = 'SKI';
+ }
+ } else {
+ @entrybufs = split(/\n+/, $entries{"$sum_seq $pass $test"});
+ $i = $j = 0;
+ while ($i <= $#allentrynames) {
+ if ($j > $#entrybufs) {
+ $entrylookup{"$sum_seq $pass $i $allentrynames[$i]"} = '-';
+ } else {
+ $entrybufs[$j] =~ /^([PFXATMUNRSKI\-]+) (.+)$/;
+ $pcode = $1;
+ $entryname = $2;
+ if ($entryname ne $allentrynames[$i]) {
+ $entrylookup{"$sum_seq $pass $i $allentrynames[$i]"} = '-';
+ } else {
+ $entrylookup{"$sum_seq $pass $i $allentrynames[$i]"} = $pcode;
+ $j++;
+ }
+ }
+ $i++;
+ }
+ }
+ }
+ }
+
+ # filter entries
+ #
+ for ($i = 0; $i <= $#allentrynames; $i++) {
+ $seen = 0;
+ foreach $pass (@allpasses) {
+ $failcnt = $failtmcnt =
+ $xfailcnt = $xfailtmcnt =
+ $xpasscnt = $unrcnt = 0;
+ for ($sum_seq = 0; $sum_seq <= $#sum_ids; $sum_seq++) {
+ $pcode = $entrylookup{"$sum_seq $pass $i $allentrynames[$i]"};
+ if ($pcode eq 'F') {
+ $failcnt++;
+ } elsif ($pcode eq 'FTM') {
+ $failtmcnt++;
+ } elsif ($pcode eq 'XFA') {
+ $xfailcnt++;
+ } elsif ($pcode eq 'XFM') {
+ $xfailtmcnt++;
+ } elsif ($pcode eq 'XPA') {
+ $xpasscnt++;
+ } elsif ($pcode eq 'UNR') {
+ $unrcnt++;
+ }
+ }
+ if ((!$no_fail) && ($failcnt || $failtmcnt)) {
+ $seen = 1 if ((! $do_diff)
+ ||
+ ($failcnt != 0) && ($failcnt != $#sum_ids+1)
+ ||
+ ($failtmcnt != 0) && ($failtmcnt != $#sum_ids+1));
+ }
+ if ((!$no_xfail) && ($xfailcnt || $xfailtmcnt)) {
+ $seen = 1 if ((! $do_diff)
+ ||
+ ($xfailcnt != 0) && ($xfailcnt != $#sum_ids+1)
+ ||
+ ($xfailtmcnt != 0) && ($xfailtmcnt != $#sum_ids+1));
+ }
+ if ((!$no_xpass) && $xpasscnt) {
+ $seen = 1 if ((! $do_diff) || ($xpasscnt != $#sum_ids+1));
+ }
+ if ((!$no_unr) && $unrcnt) {
+ $seen =1 if ((! $do_diff) || ($unrcnt != $#sum_ids+1));
+ }
+ last if ($seen);
+ }
+
+ # merge pass
+ #
+ if ($seen) {
+ for ($sum_seq = 0; $sum_seq <= $#sum_ids; $sum_seq++) {
+ $allentries{$test} .= " <$sum_seq>:" if ($do_diff);
+ @pcodes = ();
+ foreach $pass (@allpasses) {
+ push(@pcodes, $entrylookup{"$sum_seq $pass $i $allentrynames[$i]"});
+ }
+ $allentries{$test} .= &listPcodes(@pcodes);
+ $allentries{$test} .= "$allentrynames[$i]\n";
+ }
+ $allentries{$test} .= "\n" if ($do_diff);
+ }
+ }
+ }
+ }
+
+
+ #
+ # mergeEntryNames
+ #
+ # Function: - merge two list of entries into one
+ # Accepts: - sum_seq: gdb.sum sequence number
+ # - pass: multipass pass id
+ # - test: test pathname
+ # - allentrynames: array to merge into
+ # Returns: - array of merged entry names
+ #
+ sub mergeEntryNames
+ {
+ local($sum_seq, $pass, $test, @allentrynames) = @_;
+ local(@entrynames, $entryname);
+ local($insidx, $srcidx, $dstidx, $seen);
+
+ @entrynames = split(/\n+/, $entries{"$sum_seq $pass $test"});
+ foreach $entryname (@entrynames) {
+ $entryname =~ s/^[^ ]+ //;
+ }
+
+ # initial insertion point is end of @allentrynames
+ #
+ $insidx = $#allentrynames + 1;
+
+ # seek insertion starting from the end of @entrynames
+ #
+ for ($srcidx = $#entrynames; $srcidx >= 0; $srcidx--) {
+ $seen = 0;
+
+ # look for the same item in @allentrynames
+ # starting from the last insertion point moving towards the array head
+ #
+ for ($dstidx = $insidx - 1; $dstidx >= 0; $dstidx--) {
+ if ($entrynames[$srcidx] eq $allentrynames[$dstidx]) {
+ # seen the same item, skip insertion
+ # move insertion point forward to this item
+ #
+ $seen = 1;
+ $insidx = $dstidx;
+ last;
+ }
+ }
+
+ if (! $seen) {
+ # no match
+ # put the entry just ahead of the last inserted item
+ #
+ splice(@allentrynames, $insidx, 0, $entrynames[$srcidx]);
+ }
+ }
+
+ return(@allentrynames);
+ }
+
+
+ #
+ # printHeader
+ #
+ # Function: - print filter output header
+ # Accepts: - section name
+ # - legend string
+ # Returns: - (none)
+ #
+ sub printHeader
+ {
+ local($what, $legend) = @_;
+ local($indent, $str, $i);
+
+ print '-'x72, "\n", " Section - $what\n";
+
+ $indent = 1;
+ print ' ';
+ if ($do_diff) {
+ $indent += 5;
+ print ' ';
+ }
+
+ if ($legend) {
+ for ($i = 0; $i <= $#allpasses; $i++) {
+ print '|'.(' 'x((3-length($allpasses[$i]))/2)).$allpasses[$i].
+ (' 'x((3-length($allpasses[$i]))/2));
+ $indent += 4;
+ }
+ print '|: ';
+ $indent += 3;
+ while ((length($legend) + $indent) > 72) {
+ $str = substr($legend, 0, 72 - $indent);
+ $legend = substr($legend, 72 - $indent);
+ $i = rindex($str, ' ');
+ if (($i != -1) && ($i != length($str) - 1)) {
+ $legend = substr($str, $i+1).$legend;
+ $str = substr($str, 0, $i);
+ }
+ print "$str\n";
+ print ' 'x($indent+1);
+ }
+ print $legend;
+ } else {
+ print '|<pass_id>|: <multi-line output>';
+ }
+
+ print "\n", '-'x72, "\n";
+ }
+
+
+ #
+ # listPcodes
+ #
+ # Function: - lists passcodes prefix
+ # Accepts: - pass codes
+ # Returns: - prefix string
+ #
+ sub listPcodes
+ {
+ local(@pcodes) = @_;
+ local($pcode);
+ local($buf);
+
+ $buf = ' ';
+ foreach $pcode (@pcodes) {
+ $buf .= '|'.(' 'x((3-length($pcode))/2)).$pcode.
+ (' 'x((3-length($pcode))/2));
+ }
+ $buf .= '|: ';
+
+ return($buf);
+ }
+
+
+ #
+ # usage
+ #
+ # Function: - prints tool usage and exits
+ # Accepts: - error message
+ # Returns: - exits program
+ #
+ sub usage
+ {
+ local($msg) = @_;
+
+ print STDERR "$this: $msg\n" if ($msg);
+
+ print STDERR "
+ $this - smart gdb.sum filter / merge / diff utility
+
+ Command line:
+ $this [<options>] <string label> <gdb.sum> [<string label> <gdb.sum> ...]
+
+ <options>:
+ [-no-f/ail] don't list FAIL
+ [-no-xfa/il] don't list XFAIL
+ [-no-xpa/ss] don't list XPASS
+ [-no-unr/esolved] don't list UNRESOLVED
+ [-no-ski/pped] don't list skipped tests
+ [-no-oth/er] don't list unclassfiable outputs
+ [-quick] list FAIL only.
+
+ [-runpass <string>] list only the run pass(es) specified in the
+ space delimited run pass list (string).
+
+ - the default is to list all
+
+ <string label> identifier for the run corresponding to <gdb.sum>
+ <gdb.sum> pathname of gdb.sum file
+
+ - if one <string label> <gdb.sum> pair specified, produce filtered test
+ result to stdout
+ - if multiple <string label> <gdb.sum> pairs specified, produce a
+ difference of filtered test result to stdout
+
+ ";
+
+ exit 1;
+ }