https://gcc.gnu.org/g:a6c53e9b737d57ec2954a2d69690719663be8323
commit r17-4813-ga6c53e9b737d57ec2954a2d69690719663be8323 Author: Jørgen Kvalsvik <[email protected]> Date: Sat Sep 19 21:59:05 2026 +0200 testsuite: Add test for gcov function summaries A couple of recent bugs have made it very clear that a test suite for gcov function summaries is necessary. This is about as simple as I could make it; simply look for a summary header in the test file, record it in a dict, and match it against the function summaries in gcov stdout. I figured that if we do need fancier matching of expected output it's better to work backwards from actual needs than trying to anticipate it now. Function summaries is an old feature, but it hasn't had a test before now. A few recent bugs of mine have shown that some test support would be nice: commit a566f9efe1652b294e424e21e9910de2a011f4f1 Fix function summaries with suppressed blocks commit 9c09713871ad6d05e1bd2bd65cb3c78c823cb42e Compare right field when printing summary gcc/testsuite/ChangeLog: * lib/gcov.exp: Add function summary test function. * gcc.misc-tests/gcov-43.c: New test. Diff: --- gcc/testsuite/gcc.misc-tests/gcov-43.c | 99 +++++++++++++++++++++++ gcc/testsuite/lib/gcov.exp | 141 ++++++++++++++++++++++++++++++++- 2 files changed, 238 insertions(+), 2 deletions(-) diff --git a/gcc/testsuite/gcc.misc-tests/gcov-43.c b/gcc/testsuite/gcc.misc-tests/gcov-43.c new file mode 100644 index 000000000000..0497aea0ed32 --- /dev/null +++ b/gcc/testsuite/gcc.misc-tests/gcov-43.c @@ -0,0 +1,99 @@ +/* { dg-options "-fcondition-coverage -fpath-coverage --coverage" } */ +/* { dg-do run } */ + +int do_something (int x) { return x; } + +/* + Summaries for the same function, with and without suppression. + + Function 'fn1' + Lines executed:88.89% of 9 + Branches executed:100.00% of 4 + Taken at least once:50.00% of 4 + Calls executed:50.00% of 2 + Condition outcomes covered:50.00% of 4 + Prime paths covered:33.33% of 3 + + Function 'fn2' + Lines executed:83.33% of 6 (3 of 9 suppressed) + Branches executed:100.00% of 2 (2 of 4 suppressed) + Taken at least once:50.00% of 2 + Calls executed:0.00% of 1 (1 of 2 suppressed) + Condition outcomes covered:50.00% of 4 + Prime paths covered:0.00% of 1 (2 of 3 suppressed) +*/ + +int +fn1 (int argc) +{ + int b = argc + 1; + + int c; + + int a = argc; + + if (a) + if (b) + { + c = do_something (4); + } + else + c = do_something (1024); + + int d = a + c - 1; +} + +int +fn2 (int argc) +{ +#pragma GCC suppress_coverage begin + int b = argc + 1; +#pragma GCC suppress_coverage end + +#pragma GCC suppress_coverage begin + int c; +#pragma GCC suppress_coverage end + + int a = argc; + + if (a) + if (b) + { +#pragma GCC suppress_coverage begin + c = do_something (4); +#pragma GCC suppress_coverage end + } + else + c = do_something (1024); + +#pragma GCC suppress_coverage begin + int d = a + c - 1; +#pragma GCC suppress_coverage end +} + +/* + This function has neither conditions nor branches, which means quite + different summary output. + + Function 'no_branches' + Lines executed:100.00% of 3 + No branches + No calls + No conditions + Prime paths covered:100.00% of 1 +*/ +int +no_branches (int a) +{ + int b = a * 2; + return b; +} + +int main () +{ + fn1 (1); + fn2 (2); + no_branches (3); +} + +/* { dg-final { run-gcov function-summaries "-fge gcov-43.c" } } */ diff --git a/gcc/testsuite/lib/gcov.exp b/gcc/testsuite/lib/gcov.exp index 94a734817b1b..6d6c6abb9b57 100644 --- a/gcc/testsuite/lib/gcov.exp +++ b/gcc/testsuite/lib/gcov.exp @@ -660,6 +660,134 @@ proc verify-prime-paths { testname testcase file } { return $failed } +# Verify function summaries (--function-summaries/-f). The function +# summaries are printed to stdout, so we take it (or rather, the +# [lindex result 1] from remote_exec in addition to the .gcov file. +# We scan the gcov file for a comment with the expected rows of the +# function summaries output, which should look like this: +# +# Function 'fn1' +# Lines executed:88.89% of 9 +# Branches executed:100.00% of 4 +# Taken at least once:50.00% of 4 +# Calls executed:50.00% of 2 +# Condition outcomes covered:50.00% of 4 +# Prime paths covered:33.33% of 3 +# +# The order of fields is important and should match what gcov prints. +proc verify-function-summaries { testname testcase file stdout } { + set failed 0 + set fd [open $file r] + set expected [dict create] + set funcname "" + + while { [gets $fd line] >= 0 } { + if {[regexp "Function '(.+)'" $line all fname]} { + set funcname $fname + } + if {[regexp "(Lines executed:.*)" $line _ ln]} { + dict set expected $funcname "lines" $ln + } + if {[regexp "(Branches executed:.*)" $line _ ln]} { + dict set expected $funcname "branches" $ln + } + if {[regexp "(No branches)" $line _ ln]} { + dict set expected $funcname "branches" $ln + } + if {[regexp "(Taken at least once:.*)" $line _ ln]} { + dict set expected $funcname "taken" $ln + } + if {[regexp "(Calls executed:.*)" $line _ ln]} { + dict set expected $funcname "calls" $ln + } + if {[regexp "(No calls)" $line _ ln]} { + dict set expected $funcname "calls" $ln + } + if {[regexp "(Condition outcomes covered:.*)" $line _ ln]} { + dict set expected $funcname "conditions" $ln + } + if {[regexp "(No conditions)" $line _ ln]} { + dict set expected $funcname "conditions" $ln + } + if {[regexp "(Prime paths covered:.*)" $line _ ln]} { + dict set expected $funcname "paths" $ln + } + } + + set records [split $stdout "\n"] + for {set i 0} {$i < [llength $records]} {incr i} { + set line [string trimright [lindex $records $i]] + + if {[regexp "Function '(.+)'" $line _ fname]} { + # Skip if we haven't recorded expected summaries for this function + if {![dict exists $expected $fname]} { + continue + } + + set rows [dict get $expected $fname] + dict with rows { + if {[info exists lines]} { + incr i + set line [string trimright [lindex $records $i]] + if {![string equal $lines $line]} { + incr failed + fail "$fname: Expected '$lines', was '$line'" + } + } + if {[info exists branches]} { + incr i + set line [string trimright [lindex $records $i]] + if {![string equal $branches $line]} { + incr failed + fail "$fname: Expected '$branches', was '$line'" + } + } + if {[info exists taken]} { + incr i + set line [string trimright [lindex $records $i]] + if {![string equal $taken $line]} { + incr failed + fail "$fname: Expected '$taken', was '$line'" + } + } + if {[info exists calls]} { + incr i + set line [string trimright [lindex $records $i]] + if {![string equal $calls $line]} { + incr failed + fail "$fname: Expected '$calls', was '$line'" + } + } + if {[info exists conditions]} { + incr i + set line [string trimright [lindex $records $i]] + if {![string equal $conditions $line]} { + incr failed + fail "$fname: Expected '$conditions', was '$line'" + } + } + if {[info exists paths]} { + incr i + set line [string trimright [lindex $records $i]] + if {![string equal $paths $line]} { + incr failed + fail "$fname: Expected '$paths', was '$line'" + } + } + } + dict unset expected $fname + } + } + + if {[dict size $expected] > 0} { + incr failed + fail "Did not see summary for all functions, missing [dict keys $expected]" + } + + close $fd + return $failed +} + proc gcov-pytest-format-line { args } { global subdir @@ -741,6 +869,7 @@ proc run-gcov { args } { set gcov_verify_lines 1 set gcov_verify_intermediate 0 set gcov_verify_filters 0 + set gcov_verify_function_summaries 0 set gcov_remove_gcda 0 set xfailed 0 @@ -757,6 +886,8 @@ proc run-gcov { args } { set verify_filters_unexpected [lindex $a 2] } elseif { $a == "prime-paths" } { set gcov_verify_prime_paths 1 + } elseif { $a == "function-summaries" } { + set gcov_verify_function_summaries 1 } elseif { $a == "intermediate" } { set gcov_verify_intermediate 1 set gcov_verify_calls 0 @@ -764,6 +895,7 @@ proc run-gcov { args } { set gcov_verify_conditions 0 set gcov_verify_lines 0 set gcov_verify_filters 0 + set gcov_verify_function_summaries 0 } elseif { $a == "remove-gcda" } { set gcov_remove_gcda 1 } elseif { $gcov_args == "" } { @@ -866,15 +998,20 @@ proc run-gcov { args } { } else { set ffailed 0 } + if { $gcov_verify_function_summaries } { + set fsfailed [verify-function-summaries $testname $testcase $testcase.gcov [lindex $result 1]] + } else { + set fsfailed 0 + } # Report whether the gcov test passed or failed. If there were # multiple failures then the message is a summary. - set tfailed [expr $lfailed + $bfailed + $cdfailed + $ppfailed + $cfailed + $ifailed + $ffailed] + set tfailed [expr $lfailed + $bfailed + $cdfailed + $ppfailed + $cfailed + $ifailed + $ffailed + $fsfailed] if { $xfailed } { setup_xfail "*-*-*" } if { $tfailed > 0 } { - fail "$testname gcov: $lfailed failures in line counts, $bfailed in branch percentages, $cdfailed in condition/decision, $ppfailed in prime-paths, $cfailed in return percentages, $ifailed in intermediate format, $ffailed failed in filters" + fail "$testname gcov: $lfailed failures in line counts, $bfailed in branch percentages, $cdfailed in condition/decision, $ppfailed in prime-paths, $cfailed in return percentages, $ifailed in intermediate format, $ffailed failed in filters, $fsfailed in summaries" if { $xfailed } { clean-gcov $testcase }
