1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
|
# Copyright (C) 1997-2022 Free Software Foundation, Inc.
# This program 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 GCC; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
# Verify various kinds of gcov output: line counts, branch percentages,
# and call return percentages. None of this is language-specific.
load_lib "target-supports.exp"
global GCOV
#
# clean-gcov-file -- delete a working file the compiler creates for gcov
#
# TESTCASE is the name of the test.
# SUFFIX is file suffix
proc clean-gcov-file { testcase suffix } {
set basename [file tail $testcase]
set base [file rootname $basename]
remote_file host delete $base.$suffix
}
#
# clean-gcov -- delete the working files the compiler creates for gcov
#
# TESTCASE is the name of the test.
#
proc clean-gcov { testcase } {
clean-gcov-file $testcase "gcno"
clean-gcov-file $testcase "gcda"
clean-gcov-file $testcase "h.gcov"
remote_file host delete "$testcase.gcov"
}
#
# verify-lines -- check that line counts are as expected
#
# TESTNAME is the name of the test, including unique flags.
# TESTCASE is the name of the test file.
# FILE is the name of the gcov output file.
#
proc verify-lines { testname testcase file } {
#send_user "verify-lines\n"
global subdir
set failed 0
set fd [open $file r]
while { [gets $fd line] >= 0 } {
# We want to match both "-" and "#####" as count as well as numbers,
# since we want to detect lines that shouldn't be marked as covered.
if [regexp "^ *(\[^:]*): *(\[0-9\\-#]+):.*count\\((\[0-9\\-#=\\.kMGTPEZY\*]+)\\)(.*)" \
"$line" all is n shouldbe rest] {
if [regexp "^ *{(.*)}" $rest all xfailed] {
switch [dg-process-target $xfailed] {
"N" { continue }
"F" { setup_xfail "*-*-*" }
}
}
if { $is == "" } {
fail "$testname line $n: no data available"
incr failed
} elseif { $is != $shouldbe } {
fail "$testname line $n: is $is:should be $shouldbe"
incr failed
} else {
pass "$testname count for line $n"
}
}
}
close $fd
return $failed
}
#
# verify-branches -- check that branch percentages are as expected
#
# TESTNAME is the name of the test, including unique flags.
# TESTCASE is the name of the test file.
# FILE is the name of the gcov output file.
#
# Checks are based on comments in the source file. This means to look for
# branch percentages 10 or 90, 20 or 80, and # 70 or 30:
# /* branch(10, 20, 70) */
# This means that all specified percentages should have been seen by now:
# /* branch(end) */
# All specified percentages must also be seen by the next branch(n) or
# by the end of the file.
#
# Each check depends on the compiler having generated the expected
# branch instructions. Don't check for branches that might be
# optimized away or replaced with predicated instructions.
#
proc verify-branches { testname testcase file } {
#send_user "verify-branches\n"
set failed 0
set shouldbe ""
set fd [open $file r]
set n 0
while { [gets $fd line] >= 0 } {
regexp "^\[^:\]+: *(\[0-9\]+):" "$line" all n
if [regexp "branch" $line] {
verbose "Processing branch line $n: $line" 3
if [regexp "branch\\((\[0-9 \]+)\\)" "$line" all new_shouldbe] {
# All percentages in the current list should have been seen.
if {[llength $shouldbe] != 0} {
fail "$testname line $n: expected branch percentages not found: $shouldbe"
incr failed
set shouldbe ""
}
set shouldbe $new_shouldbe
#send_user "$n: looking for: $shouldbe\n"
# Record the percentages to check for. Replace percentage
# n > 50 with 100-n, since block ordering affects the
# direction of a branch.
for {set i 0} {$i < [llength $shouldbe]} {incr i} {
set num [lindex $shouldbe $i]
if {$num > 50} {
set shouldbe [lreplace $shouldbe $i $i [expr 100 - $num]]
}
}
} elseif [regexp "branch +\[0-9\]+ taken (-\[0-9\]+)%" "$line" \
all taken] {
# Percentages should never be negative.
fail "$testname line $n: negative percentage: $taken"
incr failed
} elseif [regexp "branch +\[0-9\]+ taken (\[0-9\]+)%" "$line" \
all taken] {
#send_user "$n: taken = $taken\n"
# Percentages should never be greater than 100.
if {$taken > 100} {
fail "$testname line $n: branch percentage greater than 100: $taken"
incr failed
}
if {$taken > 50} {
set taken [expr 100 - $taken]
}
# If this percentage is one to check for then remove it
# from the list. It's normal to ignore some reports.
set i [lsearch $shouldbe $taken]
if {$i != -1} {
set shouldbe [lreplace $shouldbe $i $i]
}
} elseif [regexp "branch\\(end\\)" "$line"] {
# All percentages in the list should have been seen by now.
if {[llength $shouldbe] != 0} {
fail "$testname line n: expected branch percentages not found: $shouldbe"
incr failed
}
set shouldbe ""
}
}
}
# All percentages in the list should have been seen.
if {[llength $shouldbe] != 0} {
fail "$testname line $n: expected branch percentages not found: $shouldbe"
incr failed
}
close $fd
return $failed
}
#
# verify-calls -- check that call return percentages are as expected
#
# TESTNAME is the name of the test, including unique flags.
# TESTCASE is the name of the test file.
# FILE is the name of the gcov output file.
#
# Checks are based on comments in the source file. This means to look for
# call return percentages 50, 20, 33:
# /* returns(50, 20, 33) */
# This means that all specified percentages should have been seen by now:
# /* returns(end) */
# All specified percentages must also be seen by the next returns(n) or
# by the end of the file.
#
# Each check depends on the compiler having generated the expected
# call instructions. Don't check for calls that are inserted by the
# compiler or that might be inlined.
#
proc verify-calls { testname testcase file } {
#send_user "verify-calls\n"
set failed 0
set shouldbe ""
set fd [open $file r]
set n 0
while { [gets $fd line] >= 0 } {
regexp "^\[^:\]+: *(\[0-9\]+):" "$line" all n
if [regexp "return" $line] {
verbose "Processing returns line $n: $line" 3
if [regexp "returns\\((\[0-9 \]+)\\)" "$line" all new_shouldbe] {
# All percentages in the current list should have been seen.
if {[llength $shouldbe] != 0} {
fail "$testname line $n: expected return percentages not found: $shouldbe"
incr failed
set shouldbe ""
}
# Record the percentages to check for.
set shouldbe $new_shouldbe
} elseif [regexp "call +\[0-9\]+ returned (-\[0-9\]+)%" "$line" \
all returns] {
# Percentages should never be negative.
fail "$testname line $n: negative percentage: $returns"
incr failed
} elseif [regexp "call +\[0-9\]+ returned (\[0-9\]+)%" "$line" \
all returns] {
# For branches we check that percentages are not greater than
# 100 but call return percentages can be, as for setjmp(), so
# don't count that as an error.
#
# If this percentage is one to check for then remove it
# from the list. It's normal to ignore some reports.
set i [lsearch $shouldbe $returns]
if {$i != -1} {
set shouldbe [lreplace $shouldbe $i $i]
}
} elseif [regexp "returns\\(end\\)" "$line"] {
# All percentages in the list should have been seen by now.
if {[llength $shouldbe] != 0} {
fail "$testname line $n: expected return percentages not found: $shouldbe"
incr failed
}
set shouldbe ""
}
}
}
# All percentages in the list should have been seen.
if {[llength $shouldbe] != 0} {
fail "$testname line $n: expected return percentages not found: $shouldbe"
incr failed
}
close $fd
return $failed
}
proc gcov-pytest-format-line { args } {
global subdir
set testcase [lindex $args 0]
set pytest_script [lindex $args 1]
set output_line [lindex $args 2]
set index [string first "::" $output_line]
set test_output [string range $output_line [expr $index + 2] [string length $output_line]]
return "$subdir/$testcase ${pytest_script}::${test_output}"
}
# Call by dg-final to run gcov --json-format which produces a JSON file
# that is later analysed by a pytest Python script.
# We pass filename of a test via GCOV_PATH environment variable.
proc run-gcov-pytest { args } {
global GCOV
global srcdir subdir
# Extract the test file name from the arguments.
set testcase [lindex $args 0]
verbose "Running $GCOV $testcase in $srcdir/$subdir" 2
set testcase [remote_download host $testcase]
set result [remote_exec host $GCOV "$testcase -i"]
set pytest_script [lindex $args 1]
if { ![check_effective_target_pytest3] } {
unsupported "$pytest_script pytest python3 is missing"
return
}
setenv GCOV_PATH $testcase
spawn -noecho python3 -m pytest --color=no -rap -s --tb=no $srcdir/$subdir/$pytest_script
set prefix "\[^\r\n\]*"
expect {
-re "FAILED($prefix)\[^\r\n\]+\r\n" {
set output [gcov-pytest-format-line $testcase $pytest_script $expect_out(1,string)]
fail $output
exp_continue
}
-re "ERROR($prefix)\[^\r\n\]+\r\n" {
set output [gcov-pytest-format-line $testcase $pytest_script $expect_out(1,string)]
fail $output
exp_continue
}
-re "PASSED($prefix)\[^\r\n\]+\r\n" {
set output [gcov-pytest-format-line $testcase $pytest_script $expect_out(1,string)]
pass $output
exp_continue
}
}
clean-gcov $testcase
}
# Called by dg-final to run gcov and analyze the results.
#
# ARGS consists of the optional strings "branches" and/or "calls",
# (indicating that these things should be verified) followed by a
# list of arguments to provide to gcov, including the name of the
# source file.
proc run-gcov { args } {
global GCOV
global srcdir subdir
set gcov_args ""
set gcov_verify_calls 0
set gcov_verify_branches 0
set gcov_verify_lines 1
set gcov_verify_intermediate 0
set gcov_remove_gcda 0
set xfailed 0
foreach a $args {
if { $a == "calls" } {
set gcov_verify_calls 1
} elseif { $a == "branches" } {
set gcov_verify_branches 1
} elseif { $a == "intermediate" } {
set gcov_verify_intermediate 1
set gcov_verify_calls 0
set gcov_verify_branches 0
set gcov_verify_lines 0
} elseif { $a == "remove-gcda" } {
set gcov_remove_gcda 1
} elseif { $gcov_args == "" } {
set gcov_args $a
} else {
switch [dg-process-target $a] {
"N" { return }
"F" { set xfailed 1 }
}
}
}
set testname [testname-for-summary]
# Extract the test file name from the arguments.
set testcase [lindex $gcov_args end]
if { $gcov_remove_gcda } {
verbose "Removing $testcase.gcda"
clean-gcov-file $testcase "gcda"
}
verbose "Running $GCOV $testcase" 2
set testcase [remote_download host $testcase]
set result [remote_exec host $GCOV $gcov_args]
if { [lindex $result 0] != 0 } {
if { $xfailed } {
setup_xfail "*-*-*"
}
fail "$testname gcov failed: [lindex $result 1]"
clean-gcov $testcase
return
}
set builtin_index [string first "File '<built-in>'" $result]
if { $builtin_index != -1 } {
fail "$testname gcov failed: <built-in>.gcov should not be created"
clean-gcov $testcase
return
}
# Get the gcov output file after making sure it exists.
set files [glob -nocomplain $testcase.gcov]
if { $files == "" } {
if { $xfailed } {
setup_xfail "*-*-*"
}
fail "$testname gcov failed: $testcase.gcov does not exist"
clean-gcov $testcase
return
}
remote_upload host $testcase.gcov $testcase.gcov
# Check that line execution counts are as expected.
if { $gcov_verify_lines } {
# Check that line execution counts are as expected.
set lfailed [verify-lines $testname $testcase $testcase.gcov]
} else {
set lfailed 0
}
# If requested via the .x file, check that branch and call information
# is correct.
if { $gcov_verify_branches } {
set bfailed [verify-branches $testname $testcase $testcase.gcov]
} else {
set bfailed 0
}
if { $gcov_verify_calls } {
set cfailed [verify-calls $testname $testcase $testcase.gcov]
} else {
set cfailed 0
}
if { $gcov_verify_intermediate } {
# Check that intermediate format has the expected format
set ifailed [verify-intermediate $testname $testcase $testcase.gcov]
} else {
set ifailed 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 + $cfailed + $ifailed]
if { $xfailed } {
setup_xfail "*-*-*"
}
if { $tfailed > 0 } {
fail "$testname gcov: $lfailed failures in line counts, $bfailed in branch percentages, $cfailed in return percentages, $ifailed in intermediate format"
if { $xfailed } {
clean-gcov $testcase
}
} else {
pass "$testname gcov"
clean-gcov $testcase
}
}
|