1 # Copyright (C) 1997-2023 Free Software Foundation, Inc.
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 3 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with GCC; see the file COPYING3. If not see
15 # <http://www.gnu.org/licenses/>.
17 # Verify various kinds of gcov output: line counts, branch percentages,
18 # and call return percentages. None of this is language-specific.
20 load_lib "target-supports.exp"
25 # clean-gcov-file -- delete a working file the compiler creates for gcov
27 # TESTCASE is the name of the test.
28 # SUFFIX is file suffix
30 proc clean-gcov-file { testcase suffix } {
31 set basename [file tail $testcase]
32 set base [file rootname $basename]
33 remote_file host delete $base.$suffix
37 # clean-gcov -- delete the working files the compiler creates for gcov
39 # TESTCASE is the name of the test.
41 proc clean-gcov { testcase } {
42 clean-gcov-file $testcase "gcno"
43 clean-gcov-file $testcase "gcda"
44 clean-gcov-file $testcase "h.gcov"
45 remote_file host delete "$testcase.gcov"
49 # verify-lines -- check that line counts are as expected
51 # TESTNAME is the name of the test, including unique flags.
52 # TESTCASE is the name of the test file.
53 # FILE is the name of the gcov output file.
55 proc verify-lines { testname testcase file } {
56 #send_user "verify-lines\n"
61 while { [gets $fd line] >= 0 } {
62 # We want to match both "-" and "#####" as count as well as numbers,
63 # since we want to detect lines that shouldn't be marked as covered.
64 if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=\\.kMGTPEZY\*]+)\\)(.*)" \
65 "$line" all is n shouldbe rest] {
66 if [regexp "^ *{(.*)}" $rest all xfailed] {
67 switch [dg-process-target $xfailed] {
69 "F" { setup_xfail "*-*-*" }
73 fail "$testname line $n: no data available"
75 } elseif { $is != $shouldbe } {
76 fail "$testname line $n: is $is:should be $shouldbe"
79 pass "$testname count for line $n"
89 # verify-branches -- check that branch percentages are as expected
91 # TESTNAME is the name of the test, including unique flags.
92 # TESTCASE is the name of the test file.
93 # FILE is the name of the gcov output file.
95 # Checks are based on comments in the source file. This means to look for
96 # branch percentages 10 or 90, 20 or 80, and # 70 or 30:
97 # /* branch(10, 20, 70) */
98 # This means that all specified percentages should have been seen by now:
100 # All specified percentages must also be seen by the next branch(n) or
101 # by the end of the file.
103 # Each check depends on the compiler having generated the expected
104 # branch instructions. Don't check for branches that might be
105 # optimized away or replaced with predicated instructions.
107 proc verify-branches { testname testcase file } {
108 #send_user "verify-branches\n"
112 set fd [open $file r]
114 while { [gets $fd line] >= 0 } {
115 regexp "^\[^:\]+: *(\[0-9\]+):" "$line" all n
116 if [regexp "branch" $line] {
117 verbose "Processing branch line $n: $line" 3
118 if [regexp "branch\\((\[0-9 \]+)\\)" "$line" all new_shouldbe] {
119 # All percentages in the current list should have been seen.
120 if {[llength $shouldbe] != 0} {
121 fail "$testname line $n: expected branch percentages not found: $shouldbe"
125 set shouldbe $new_shouldbe
126 #send_user "$n: looking for: $shouldbe\n"
127 # Record the percentages to check for. Replace percentage
128 # n > 50 with 100-n, since block ordering affects the
129 # direction of a branch.
130 for {set i 0} {$i < [llength $shouldbe]} {incr i} {
131 set num [lindex $shouldbe $i]
133 set shouldbe [lreplace $shouldbe $i $i [expr 100 - $num]]
136 } elseif [regexp "branch +\[0-9\]+ taken (-\[0-9\]+)%" "$line" \
138 # Percentages should never be negative.
139 fail "$testname line $n: negative percentage: $taken"
141 } elseif [regexp "branch +\[0-9\]+ taken (\[0-9\]+)%" "$line" \
143 #send_user "$n: taken = $taken\n"
144 # Percentages should never be greater than 100.
146 fail "$testname line $n: branch percentage greater than 100: $taken"
150 set taken [expr 100 - $taken]
152 # If this percentage is one to check for then remove it
153 # from the list. It's normal to ignore some reports.
154 set i [lsearch $shouldbe $taken]
156 set shouldbe [lreplace $shouldbe $i $i]
158 } elseif [regexp "branch\\(end\\)" "$line"] {
159 # All percentages in the list should have been seen by now.
160 if {[llength $shouldbe] != 0} {
161 fail "$testname line n: expected branch percentages not found: $shouldbe"
168 # All percentages in the list should have been seen.
169 if {[llength $shouldbe] != 0} {
170 fail "$testname line $n: expected branch percentages not found: $shouldbe"
178 # verify-calls -- check that call return percentages are as expected
180 # TESTNAME is the name of the test, including unique flags.
181 # TESTCASE is the name of the test file.
182 # FILE is the name of the gcov output file.
184 # Checks are based on comments in the source file. This means to look for
185 # call return percentages 50, 20, 33:
186 # /* returns(50, 20, 33) */
187 # This means that all specified percentages should have been seen by now:
189 # All specified percentages must also be seen by the next returns(n) or
190 # by the end of the file.
192 # Each check depends on the compiler having generated the expected
193 # call instructions. Don't check for calls that are inserted by the
194 # compiler or that might be inlined.
196 proc verify-calls { testname testcase file } {
197 #send_user "verify-calls\n"
201 set fd [open $file r]
203 while { [gets $fd line] >= 0 } {
204 regexp "^\[^:\]+: *(\[0-9\]+):" "$line" all n
205 if [regexp "return" $line] {
206 verbose "Processing returns line $n: $line" 3
207 if [regexp "returns\\((\[0-9 \]+)\\)" "$line" all new_shouldbe] {
208 # All percentages in the current list should have been seen.
209 if {[llength $shouldbe] != 0} {
210 fail "$testname line $n: expected return percentages not found: $shouldbe"
214 # Record the percentages to check for.
215 set shouldbe $new_shouldbe
216 } elseif [regexp "call +\[0-9\]+ returned (-\[0-9\]+)%" "$line" \
218 # Percentages should never be negative.
219 fail "$testname line $n: negative percentage: $returns"
221 } elseif [regexp "call +\[0-9\]+ returned (\[0-9\]+)%" "$line" \
223 # For branches we check that percentages are not greater than
224 # 100 but call return percentages can be, as for setjmp(), so
225 # don't count that as an error.
227 # If this percentage is one to check for then remove it
228 # from the list. It's normal to ignore some reports.
229 set i [lsearch $shouldbe $returns]
231 set shouldbe [lreplace $shouldbe $i $i]
233 } elseif [regexp "returns\\(end\\)" "$line"] {
234 # All percentages in the list should have been seen by now.
235 if {[llength $shouldbe] != 0} {
236 fail "$testname line $n: expected return percentages not found: $shouldbe"
243 # All percentages in the list should have been seen.
244 if {[llength $shouldbe] != 0} {
245 fail "$testname line $n: expected return percentages not found: $shouldbe"
252 proc gcov-pytest-format-line { args } {
255 set testcase [lindex $args 0]
256 set pytest_script [lindex $args 1]
257 set output_line [lindex $args 2]
259 set index [string first "::" $output_line]
260 set test_output [string range $output_line [expr $index + 2] [string length $output_line]]
262 return "$subdir/$testcase ${pytest_script}::${test_output}"
265 # Call by dg-final to run gcov --json-format which produces a JSON file
266 # that is later analysed by a pytest Python script.
267 # We pass filename of a test via GCOV_PATH environment variable.
269 proc run-gcov-pytest { args } {
272 # Extract the test file name from the arguments.
273 set testcase [lindex $args 0]
275 verbose "Running $GCOV $testcase in $srcdir/$subdir" 2
276 set testcase [remote_download host $testcase]
277 set result [remote_exec host $GCOV "$testcase -i"]
279 set pytest_script [lindex $args 1]
280 if { ![check_effective_target_pytest3] } {
281 unsupported "$pytest_script pytest python3 is missing"
285 setenv GCOV_PATH $testcase
286 spawn -noecho python3 -m pytest --color=no -rap -s --tb=no $srcdir/$subdir/$pytest_script
288 set prefix "\[^\r\n\]*"
290 -re "FAILED($prefix)\[^\r\n\]+\r\n" {
291 set output [gcov-pytest-format-line $testcase $pytest_script $expect_out(1,string)]
295 -re "ERROR($prefix)\[^\r\n\]+\r\n" {
296 set output [gcov-pytest-format-line $testcase $pytest_script $expect_out(1,string)]
300 -re "PASSED($prefix)\[^\r\n\]+\r\n" {
301 set output [gcov-pytest-format-line $testcase $pytest_script $expect_out(1,string)]
310 # Called by dg-final to run gcov and analyze the results.
312 # ARGS consists of the optional strings "branches" and/or "calls",
313 # (indicating that these things should be verified) followed by a
314 # list of arguments to provide to gcov, including the name of the
317 proc run-gcov { args } {
322 set gcov_verify_calls 0
323 set gcov_verify_branches 0
324 set gcov_verify_lines 1
325 set gcov_verify_intermediate 0
326 set gcov_remove_gcda 0
330 if { $a == "calls" } {
331 set gcov_verify_calls 1
332 } elseif { $a == "branches" } {
333 set gcov_verify_branches 1
334 } elseif { $a == "intermediate" } {
335 set gcov_verify_intermediate 1
336 set gcov_verify_calls 0
337 set gcov_verify_branches 0
338 set gcov_verify_lines 0
339 } elseif { $a == "remove-gcda" } {
340 set gcov_remove_gcda 1
341 } elseif { $gcov_args == "" } {
344 switch [dg-process-target $a] {
346 "F" { set xfailed 1 }
351 set testname [testname-for-summary]
353 # Extract the test file name from the arguments.
354 set testcase [lindex $gcov_args end]
356 if { $gcov_remove_gcda } {
357 verbose "Removing $testcase.gcda"
358 clean-gcov-file $testcase "gcda"
361 verbose "Running $GCOV $testcase" 2
362 set testcase [remote_download host $testcase]
363 set result [remote_exec host $GCOV $gcov_args]
364 if { [lindex $result 0] != 0 } {
368 fail "$testname gcov failed: [lindex $result 1]"
373 set builtin_index [string first "File '<built-in>'" $result]
374 if { $builtin_index != -1 } {
375 fail "$testname gcov failed: <built-in>.gcov should not be created"
380 # Get the gcov output file after making sure it exists.
381 set files [glob -nocomplain $testcase.gcov]
382 if { $files == "" } {
386 fail "$testname gcov failed: $testcase.gcov does not exist"
390 remote_upload host $testcase.gcov $testcase.gcov
392 # Check that line execution counts are as expected.
393 if { $gcov_verify_lines } {
394 # Check that line execution counts are as expected.
395 set lfailed [verify-lines $testname $testcase $testcase.gcov]
400 # If requested via the .x file, check that branch and call information
402 if { $gcov_verify_branches } {
403 set bfailed [verify-branches $testname $testcase $testcase.gcov]
407 if { $gcov_verify_calls } {
408 set cfailed [verify-calls $testname $testcase $testcase.gcov]
412 if { $gcov_verify_intermediate } {
413 # Check that intermediate format has the expected format
414 set ifailed [verify-intermediate $testname $testcase $testcase.gcov]
419 # Report whether the gcov test passed or failed. If there were
420 # multiple failures then the message is a summary.
421 set tfailed [expr $lfailed + $bfailed + $cfailed + $ifailed]
425 if { $tfailed > 0 } {
426 fail "$testname gcov: $lfailed failures in line counts, $bfailed in branch percentages, $cfailed in return percentages, $ifailed in intermediate format"
431 pass "$testname gcov"