| #! /bin/sh | 
 |  | 
 | # For a specified tool and optional list of test variants, extract | 
 | # test results from one or more test summary (.sum) files and combine | 
 | # the results into a new test summary file, sent to the standard output. | 
 | # The resulting file can be used with test result comparison scripts for | 
 | # results from tests that were run in parallel.  See usage() below. | 
 |  | 
 | # Copyright (C) 2008-2025 Free Software Foundation, Inc. | 
 | # Contributed by Janis Johnson <janis187@us.ibm.com> | 
 | # | 
 | # This file is part of GCC. | 
 | # | 
 | # GCC 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, or (at your option) | 
 | # any later version. | 
 | # | 
 | # GCC 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 GCC; see the file COPYING.  If not, write to | 
 | # the Free Software Foundation, 51 Franklin Street, Fifth Floor, | 
 | # Boston, MA 02110-1301, USA. | 
 |  | 
 | PROGNAME=dg-extract-results.sh | 
 |  | 
 | # Try to use the python version if possible, since it tends to be faster and | 
 | # produces more stable results. | 
 | PYTHON_VER=`echo "$0" | sed 's/sh$/py/'` | 
 | for python in python3 python python2 ; do | 
 |   if test "$PYTHON_VER" != "$0" && | 
 |      test -f "$PYTHON_VER" && | 
 |      ${python} -c 'import sys, getopt, re, io, datetime, operator; sys.exit (0 if sys.version_info >= (2, 6) else 1)' \ | 
 |        > /dev/null 2> /dev/null; then | 
 |     exec ${python} $PYTHON_VER "$@" | 
 |   fi | 
 | done | 
 |  | 
 | usage() { | 
 |   cat <<EOF >&2 | 
 | Usage: $PROGNAME [-t tool] [-l variant-list] [-L] sum-file ... | 
 |  | 
 |     tool           The tool (e.g. g++, libffi) for which to create a | 
 |                    new test summary file.  If not specified then all | 
 |                    specified sum files must be for the same tool. | 
 |     variant-list   One or more test variant names.  If the list is | 
 |                    not specified then one is constructed from all | 
 |                    variants in the files for <tool>. | 
 |     sum-file       A test summary file with the format of those | 
 |                    created by runtest from DejaGnu. | 
 |     If -L is used, merge *.log files instead of *.sum.  In this | 
 |     mode the exact order of lines may not be preserved, just different | 
 |     Running *.exp chunks should be in correct order. | 
 | EOF | 
 | } | 
 |  | 
 | # Write a message to the standard error. | 
 |  | 
 | msg() { | 
 |   echo "$@" >&2 | 
 | } | 
 |  | 
 | # Parse the command-line options. | 
 |  | 
 | VARIANTS="" | 
 | TOOL="" | 
 | MODE="sum" | 
 |  | 
 | while getopts "l:t:L" ARG; do | 
 |   case $ARG in | 
 |   l)  VARIANTS="${VARIANTS} ${OPTARG}";; | 
 |   t)  test -z "$TOOL" || (msg "${PROGNAME}: only one tool can be specified"; exit 1); | 
 |       TOOL="${OPTARG}";; | 
 |   L)  MODE="log";; | 
 |   \?) usage; exit 0;; | 
 |   esac | 
 | done | 
 | shift `expr ${OPTIND} - 1` | 
 |  | 
 | if test $# -lt 1 ; then | 
 |   usage | 
 |   exit 1 | 
 | fi | 
 |  | 
 | TMPDIR=${TMPDIR-/tmp} | 
 | SUM_FILES="$@" | 
 | FIRST_SUM=$1 | 
 | TMP= | 
 | trap 'EXIT_STATUS=$?; rm -rf $TMP && exit $EXIT_STATUS' 0 | 
 | # Create a (secure) tmp directory for tmp files. | 
 | { | 
 |   TMP=`(umask 077 && mktemp -d -q "${TMPDIR}/dg-combine-results-$$-XXXXXX") 2>/dev/null` && | 
 |   test -n "$TMP" && test -d "$TMP" | 
 | } || | 
 | { | 
 |   TMP=${TMPDIR}/dg-combine-results-$$-$RANDOM | 
 |   (umask 077 && mkdir $TMP) | 
 | } || | 
 | { | 
 |   msg "${PROGNAME}: cannot create a temporary directory" | 
 |   { (exit 1); exit 1; } | 
 | } | 
 |  | 
 | # Find a good awk. | 
 |  | 
 | if test -z "$AWK" ; then | 
 |   for AWK in gawk nawk awk | 
 |   do | 
 |     if type $AWK 2>&1 | grep 'not found' > /dev/null 2>&1 ; then | 
 |       : | 
 |     else | 
 |       break | 
 |     fi | 
 |   done | 
 | fi | 
 |  | 
 | # Verify that the specified summary files exist. | 
 |  | 
 | ERROR=0 | 
 | for FILE in $SUM_FILES | 
 | do | 
 |   if ! test -f $FILE ; then | 
 |     msg "${PROGNAME}: file $FILE does not exist." | 
 |     ERROR=1 | 
 |   fi | 
 | done | 
 | test $ERROR -eq 0 || exit 1 | 
 |  | 
 | # Test if grep supports the '--text' option | 
 |  | 
 | GREP=grep | 
 |  | 
 | if echo -e '\x00foo\x00' | $GREP --text foo > /dev/null 2>&1 ; then | 
 |   GREP="grep --text" | 
 | else | 
 |   # Our grep does not recognize the '--text' option.  We have to | 
 |   # treat our files in order to remove any non-printable character. | 
 |   for file in $SUM_FILES ; do | 
 |     mv $file ${file}.orig | 
 |     cat -v ${file}.orig > $file | 
 |   done | 
 | fi | 
 |  | 
 | if [ -z "$TOOL" ]; then | 
 |   # If no tool was specified, all specified summary files must be for | 
 |   # the same tool. | 
 |  | 
 |   CNT=`$GREP '=== .* tests ===' $SUM_FILES | $AWK '{ print $3 }' | sort -u | wc -l` | 
 |   if [ $CNT -eq 1 ]; then | 
 |     TOOL=`$GREP '=== .* tests ===' $FIRST_SUM | $AWK '{ print $2 }'` | 
 |   else | 
 |     msg "${PROGNAME}: sum files are for multiple tools, specify a tool" | 
 |     msg "" | 
 |     usage | 
 |     exit 1 | 
 |   fi | 
 | else | 
 |   # Ignore the specified summary files that are not for this tool.  This | 
 |   # should keep the relevant files in the same order. | 
 |  | 
 |   SUM_FILES=`$GREP -l "=== $TOOL" $SUM_FILES` | 
 |   if test -z "$SUM_FILES" ; then | 
 |     msg "${PROGNAME}: none of the specified files are results for $TOOL" | 
 |     exit 1 | 
 |   fi | 
 | fi | 
 |  | 
 | if [ "$TOOL" = acats ]; then | 
 |   # Acats *.sum or *.log files aren't dejagnu generated, and they have | 
 |   # somewhat different format. | 
 |   ACATS_AWK=${TMP}/acats.awk | 
 |   cat <<EOF > $ACATS_AWK | 
 | BEGIN { | 
 |   print_prologue=1; curfile=""; insummary=0 | 
 |   passcnt=0; failcnt=0; unsupcnt=0; failures="" | 
 | } | 
 | /^[ \t]*=== acats configuration ===/ { | 
 |   insummary=0 | 
 |   if (print_prologue) print | 
 |   next | 
 | } | 
 | /^[ \t]*=== acats tests ===/ { | 
 |   if (print_prologue) print | 
 |   print_prologue=0 | 
 |   next | 
 | } | 
 | /^Running chapter / { | 
 |   if (curfile) close (curfile) | 
 |   curfile="${TMP}/chapter-"\$3 | 
 |   print >> curfile | 
 |   next | 
 | } | 
 | /^[ \t]*=== acats Summary ===/ { | 
 |   if (curfile) close (curfile) | 
 |   curfile="" | 
 |   insummary=1 | 
 |   next | 
 | } | 
 | /^# of expected passes/		{ if (insummary == 1) passcnt += \$5; next; } | 
 | /^# of unexpected failures/	{ if (insummary == 1) failcnt += \$5; next; } | 
 | /^# of unsupported tests/	{ if (insummary == 1) unsupcnt += \$5; next; } | 
 | /^\*\*\* FAILURES: / { | 
 |   if (insummary == 1) { | 
 |     if (failures) sub(/^\*\*\* FAILURES:/,"") | 
 |     failures=failures""\$0 | 
 |   } | 
 | } | 
 | { | 
 |   if (print_prologue) { print; next } | 
 |   if (curfile) print >> curfile | 
 | } | 
 | END { | 
 |   system ("cat ${TMP}/chapter-*") | 
 |   print "		=== acats Summary ===" | 
 |   print "# of expected passes		" passcnt | 
 |   print "# of unexpected failures	" failcnt | 
 |   if (unsupcnt) print "# of unsupported tests		" unsupcnt | 
 |   if (failures) print failures | 
 | } | 
 | EOF | 
 |  | 
 |   rm -f ${TMP}/chapter-* | 
 |   $AWK -f $ACATS_AWK $SUM_FILES | 
 |   exit 0 | 
 | fi | 
 |  | 
 | # If no variants were specified, find all variants in the remaining | 
 | # summary files.  Otherwise, ignore specified variants that aren't in | 
 | # any of those summary files. | 
 |  | 
 | if test -z "$VARIANTS" ; then | 
 |   VAR_AWK=${TMP}/variants.awk | 
 |   cat <<EOF > $VAR_AWK | 
 | /^Schedule of variations:/      { in_vars=1; next } | 
 | /^$/                            { in_vars=0 } | 
 | /^Running target/               { exit } | 
 | { if (in_vars==1) print \$1; else next } | 
 | EOF | 
 |  | 
 |   touch ${TMP}/varlist | 
 |   for FILE in $SUM_FILES; do | 
 |     $AWK -f $VAR_AWK $FILE >> ${TMP}/varlist | 
 |   done | 
 |   VARIANTS="`sort -u ${TMP}/varlist`" | 
 | else | 
 |   VARS="$VARIANTS" | 
 |   VARIANTS="" | 
 |   for VAR in $VARS | 
 |   do | 
 |     $GREP "Running target $VAR" $SUM_FILES > /dev/null && VARIANTS="$VARIANTS $VAR" | 
 |   done | 
 | fi | 
 |  | 
 | # Find out if we have more than one variant, or any at all. | 
 |  | 
 | VARIANT_COUNT=0 | 
 | for VAR in $VARIANTS | 
 | do | 
 |   VARIANT_COUNT=`expr $VARIANT_COUNT + 1` | 
 | done | 
 |  | 
 | if test $VARIANT_COUNT -eq 0 ; then | 
 |   msg "${PROGNAME}: no file for $TOOL has results for the specified variants" | 
 |   exit 1 | 
 | fi | 
 |  | 
 | cat $SUM_FILES \ | 
 |   | $AWK '/^Running/ { if ($2 != "target" && $3 == "...") print "EXPFILE: "$2 } ' \ | 
 |   | sort -u > ${TMP}/expfiles | 
 |  | 
 | # Write the begining of the combined summary file. | 
 |  | 
 | head -n 3 $FIRST_SUM | 
 | echo | 
 | echo "		=== $TOOL tests ===" | 
 | echo | 
 | echo "Schedule of variations:" | 
 | for VAR in $VARIANTS | 
 | do | 
 |   echo "    $VAR" | 
 | done | 
 | echo | 
 |  | 
 | # For each test variant for the tool, copy test reports from each of the | 
 | # summary files.  Set up two awk scripts from within the loop to | 
 | # initialize VAR and TOOL with the script, rather than assuming that the | 
 | # available version of awk can pass variables from the command line. | 
 |  | 
 | for VAR in $VARIANTS | 
 | do | 
 |   GUTS_AWK=${TMP}/guts.awk | 
 |   cat << EOF > $GUTS_AWK | 
 | BEGIN { | 
 |   variant="$VAR" | 
 |   firstvar=1 | 
 |   expfileno=1 | 
 |   cnt=0 | 
 |   print_using=0 | 
 |   need_close=0 | 
 |   has_timeout=0 | 
 |   timeout_cnt=0 | 
 | } | 
 | /^EXPFILE: / { | 
 |   expfiles[expfileno] = \$2 | 
 |   expfilesr[\$2] = expfileno | 
 |   expfileno = expfileno + 1 | 
 | } | 
 | /^Running target / { | 
 |   curvar = \$3 | 
 |   if (variant == curvar && firstvar == 1) { print; print_using=1; firstvar = 0 } | 
 |   next | 
 | } | 
 | /^Using / { | 
 |   if (variant == curvar && print_using) { print; next } | 
 | } | 
 | /^Running .*\\.exp \\.\\.\\./ { | 
 |   print_using=0 | 
 |   if (variant == curvar) { | 
 |     if (need_close) close(curfile) | 
 |     curfile="${TMP}/list"expfilesr[\$2] | 
 |     expfileseen[\$2]=expfileseen[\$2] + 1 | 
 |     need_close=0 | 
 |     testname="00" | 
 |     next | 
 |   } | 
 | } | 
 | /^\t\t=== .* ===$/ { curvar = ""; next } | 
 | /^(PASS|XPASS|FAIL|XFAIL|UNRESOLVED|WARNING|ERROR|UNSUPPORTED|UNTESTED|KFAIL|KPASS|PATH|DUPLICATE):/ { | 
 |   testname=\$2 | 
 |   # Ugly hack for gfortran.dg/dg.exp | 
 |   if ("$TOOL" == "gfortran" && testname ~ /^gfortran.dg\/g77\//) | 
 |     testname="h"testname | 
 |   if ("$MODE" == "sum") { | 
 |     if (\$0 ~ /^WARNING: program timed out/) { | 
 |       has_timeout=1 | 
 |       timeout_cnt=cnt+1 | 
 |     } else { | 
 |       # Prepare timeout replacement message in case it's needed | 
 |       timeout_msg=\$0 | 
 |       sub(\$1, "WARNING:", timeout_msg) | 
 |     } | 
 |   } | 
 | } | 
 | /^$/ { if ("$MODE" == "sum") next } | 
 | { if (variant == curvar && curfile) { | 
 |     if ("$MODE" == "sum") { | 
 |       # Do not print anything if the current line is a timeout | 
 |       if (has_timeout == 0) { | 
 | 	# If the previous line was a timeout, | 
 | 	# insert the full current message without keyword | 
 | 	if (timeout_cnt != 0) { | 
 | 	  printf "%s %08d|%s program timed out.\n", testname, timeout_cnt-1, timeout_msg >> curfile | 
 | 	  timeout_cnt = 0 | 
 | 	  cnt = cnt + 1 | 
 | 	} | 
 | 	printf "%s %08d|", testname, cnt >> curfile | 
 | 	cnt = cnt + 1 | 
 | 	filewritten[curfile]=1 | 
 | 	need_close=1 | 
 | 	print >> curfile | 
 |       } | 
 |       has_timeout=0 | 
 |     } else { | 
 |       filewritten[curfile]=1 | 
 |       need_close=1 | 
 |       print >> curfile | 
 |     } | 
 |   } else { | 
 |     has_timeout=0 | 
 |     timeout_cnt=0 | 
 |     next | 
 |   } | 
 | } | 
 | END { | 
 |   n=1 | 
 |   while (n < expfileno) { | 
 |     if (expfileseen[expfiles[n]]) { | 
 |       print "Running "expfiles[n]" ..." | 
 |       if (filewritten["${TMP}/list"n]) { | 
 | 	if (expfileseen[expfiles[n]] == 1) | 
 | 	  cmd="cat" | 
 | 	else | 
 | 	  cmd="LC_ALL=C sort" | 
 | 	if ("$MODE" == "sum") | 
 | 	  system (cmd" ${TMP}/list"n" | sed -n 's/^[^ ]* [^ |]*|//p'") | 
 | 	else | 
 | 	  system ("cat ${TMP}/list"n) | 
 |       } | 
 |     } | 
 |     n = n + 1 | 
 |   } | 
 | } | 
 | EOF | 
 |  | 
 |   SUMS_AWK=${TMP}/sums.awk | 
 |   rm -f $SUMS_AWK | 
 |   cat << EOF > $SUMS_AWK | 
 | BEGIN { | 
 |   variant="$VAR" | 
 |   tool="$TOOL" | 
 |   passcnt=0; failcnt=0; untstcnt=0; xpasscnt=0; xfailcnt=0; kpasscnt=0; kfailcnt=0; unsupcnt=0; unrescnt=0; dgerrorcnt=0; | 
 |   pathcnt=0; dupcnt=0 | 
 |   curvar=""; insummary=0 | 
 | } | 
 | /^Running target /		{ curvar = \$3; next } | 
 | /^ERROR: \(DejaGnu\)/		{ if (variant == curvar) dgerrorcnt += 1 } | 
 | /^# of /			{ if (variant == curvar) insummary = 1 } | 
 | /^# of expected passes/		{ if (insummary == 1) passcnt += \$5; next; } | 
 | /^# of unexpected successes/	{ if (insummary == 1) xpasscnt += \$5; next; } | 
 | /^# of unexpected failures/	{ if (insummary == 1) failcnt += \$5; next; } | 
 | /^# of expected failures/	{ if (insummary == 1) xfailcnt += \$5; next; } | 
 | /^# of unknown successes/	{ if (insummary == 1) kpasscnt += \$5; next; } | 
 | /^# of known failures/		{ if (insummary == 1) kfailcnt += \$5; next; } | 
 | /^# of untested testcases/	{ if (insummary == 1) untstcnt += \$5; next; } | 
 | /^# of unresolved testcases/	{ if (insummary == 1) unrescnt += \$5; next; } | 
 | /^# of unsupported tests/	{ if (insummary == 1) unsupcnt += \$5; next; } | 
 | /^# of paths in test names/	{ if (insummary == 1) pathcnt += \$7; next; } | 
 | /^# of duplicate test names/	{ if (insummary == 1) dupcnt += \$6; next; } | 
 | /^$/				{ if (insummary == 1) | 
 | 				    { insummary = 0; curvar = "" } | 
 | 				  next | 
 | 				} | 
 | { next } | 
 | END { | 
 |   printf ("\t\t=== %s Summary for %s ===\n\n", tool, variant) | 
 |   if (dgerrorcnt != 0) printf ("# of DejaGnu errors\t\t%d\n", dgerrorcnt) | 
 |   if (passcnt != 0) printf ("# of expected passes\t\t%d\n", passcnt) | 
 |   if (failcnt != 0) printf ("# of unexpected failures\t%d\n", failcnt) | 
 |   if (xpasscnt != 0) printf ("# of unexpected successes\t%d\n", xpasscnt) | 
 |   if (xfailcnt != 0) printf ("# of expected failures\t\t%d\n", xfailcnt) | 
 |   if (kpasscnt != 0) printf ("# of unknown successes\t\t%d\n", kpasscnt) | 
 |   if (kfailcnt != 0) printf ("# of known failures\t\t%d\n", kfailcnt) | 
 |   if (untstcnt != 0) printf ("# of untested testcases\t\t%d\n", untstcnt) | 
 |   if (unrescnt != 0) printf ("# of unresolved testcases\t%d\n", unrescnt) | 
 |   if (unsupcnt != 0) printf ("# of unsupported tests\t\t%d\n", unsupcnt) | 
 |   if (pathcnt != 0) printf ("# of paths in test names\t%d\n", pathcnt) | 
 |   if (dupcnt != 0) printf ("# of duplicate test names\t%d\n", dupcnt) | 
 | } | 
 | EOF | 
 |  | 
 |   PVAR=`echo $VAR | sed 's,/,.,g'` | 
 |   TMPFILE=${TMP}/var-$PVAR | 
 |   rm -f $TMPFILE | 
 |   rm -f ${TMP}/list* | 
 |   cat ${TMP}/expfiles $SUM_FILES | $AWK -f $GUTS_AWK | 
 |   cat $SUM_FILES | $AWK -f $SUMS_AWK > $TMPFILE | 
 |   # If there are multiple variants, output the counts for this one; | 
 |   # otherwise there will just be the final counts at the end. | 
 |   test $VARIANT_COUNT -eq 1 || cat $TMPFILE | 
 | done | 
 |  | 
 | # Set up an awk script to get the combined summary counts for the tool. | 
 |  | 
 | TOTAL_AWK=${TMP}/total.awk | 
 | cat << EOF > $TOTAL_AWK | 
 | BEGIN { | 
 |   tool="$TOOL" | 
 |   passcnt=0; failcnt=0; untstcnt=0; xpasscnt=0; xfailcnt=0; kfailcnt=0; unsupcnt=0; unrescnt=0; dgerrorcnt=0 | 
 |   pathcnt=0; dupcnt=0 | 
 | } | 
 | /^# of DejaGnu errors/		{ dgerrorcnt += \$5 } | 
 | /^# of expected passes/		{ passcnt += \$5 } | 
 | /^# of unexpected failures/	{ failcnt += \$5 } | 
 | /^# of unexpected successes/	{ xpasscnt += \$5 } | 
 | /^# of expected failures/	{ xfailcnt += \$5 } | 
 | /^# of unknown successes/	{ kpasscnt += \$5 } | 
 | /^# of known failures/		{ kfailcnt += \$5 } | 
 | /^# of untested testcases/	{ untstcnt += \$5 } | 
 | /^# of unresolved testcases/	{ unrescnt += \$5 } | 
 | /^# of unsupported tests/	{ unsupcnt += \$5 } | 
 | /^# of paths in test names/	{ pathcnt += \$7 } | 
 | /^# of duplicate test names/	{ dupcnt += \$6 } | 
 | END { | 
 |   printf ("\n\t\t=== %s Summary ===\n\n", tool) | 
 |   if (dgerrorcnt != 0) printf ("# of DejaGnu errors\t\t%d\n", dgerrorcnt) | 
 |   if (passcnt != 0) printf ("# of expected passes\t\t%d\n", passcnt) | 
 |   if (failcnt != 0) printf ("# of unexpected failures\t%d\n", failcnt) | 
 |   if (xpasscnt != 0) printf ("# of unexpected successes\t%d\n", xpasscnt) | 
 |   if (xfailcnt != 0) printf ("# of expected failures\t\t%d\n", xfailcnt) | 
 |   if (kpasscnt != 0) printf ("# of unknown successes\t\t%d\n", kpasscnt) | 
 |   if (kfailcnt != 0) printf ("# of known failures\t\t%d\n", kfailcnt) | 
 |   if (untstcnt != 0) printf ("# of untested testcases\t\t%d\n", untstcnt) | 
 |   if (unrescnt != 0) printf ("# of unresolved testcases\t%d\n", unrescnt) | 
 |   if (unsupcnt != 0) printf ("# of unsupported tests\t\t%d\n", unsupcnt) | 
 |   if (pathcnt != 0) printf ("# of paths in test names\t%d\n", pathcnt) | 
 |   if (dupcnt != 0) printf ("# of duplicate test names\t%d\n", dupcnt) | 
 | } | 
 | EOF | 
 |  | 
 | # Find the total summaries for the tool and add to the end of the output. | 
 | cat ${TMP}/var-* | $AWK -f $TOTAL_AWK | 
 |  | 
 | # This is ugly, but if there's version output from the compiler under test | 
 | # at the end of the file, we want it.  The other thing that might be there | 
 | # is the final summary counts. | 
 | tail -2 $FIRST_SUM | $GREP '^#' > /dev/null || tail -2 $FIRST_SUM | 
 |  | 
 | exit 0 |