aboutsummaryrefslogtreecommitdiff
path: root/gcc/testsuite/lib/gcov.exp
blob: c4a5c8c90dfeb10c8d55be4b768b85801733f5d1 (plain)
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
#   Copyright (C) 1997, 2001 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 2 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  

# Verify various kinds of gcov output: line counts, branch percentages,
# and call return percentages.  None of this is language-specific.

global GCOV

#
# clean-gcov -- delete the working files the compiler creates for gcov
#
# TESTCASE is the name of the test.
#
proc clean-gcov { testcase } {
    set basename [file tail $testcase]
    set base [file rootname $basename]
    remote_file host delete $base.gcno $base.gcda $basename.gcov
}

#
# verify-lines -- check that line counts are as expected
#
# TESTCASE is the name of the test.
# FILE is the name of the gcov output file.
#
proc verify-lines { testcase file } {
    #send_user "verify-lines\n"
    set failed 0
    set fd [open $file r]
    while { [gets $fd line] >= 0 } {
	if [regexp "^ *(\[^:]*): *(\[0-9\]+):.*count\\((\[0-9\]+)\\)" \
		"$line" all is n shouldbe] {
	    if { $is == "" } {
		fail "$n:no data available for this line"
		incr failed
	    } elseif { $is != $shouldbe } {
		fail "$n:is $is:should be $shouldbe"
		incr failed
	    }
	}
    }
    return $failed
}

#
# verify-branches -- check that branch percentages are as expected
#
# TESTCASE is the name of the test.
# 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 { 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 "$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 "$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 "$n: 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 "$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 "$n: expected branch percentages not found: $shouldbe"
	incr failed
    }
    close $fd
    return $failed
}

#
# verify-calls -- check that call return percentages are as expected
#
# TESTCASE is the name of the test.
# 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 { 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 "$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 "$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 "$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 "$n: expected return percentages not found: $shouldbe"
	incr failed
    }
    close $fd
    return $failed
}

# 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 [lindex $args end]

    set gcov_verify_calls 0
    set gcov_verify_branches 0
    set gcov_execute_xfail ""
    set gcov_verify_xfail ""

    foreach a $args {
	if { $a == "calls" } {
	  set gcov_verify_calls 1
	} elseif { $a == "branches" } {
	  set gcov_verify_branches 1
	}
    }

    # Extract the test name from the arguments.
    set testcase [lindex $gcov_args end]

    if { $gcov_execute_xfail != "" } {
	eval setup_xfail [split $gcov_execute_xfail]
    }

    verbose "Running $GCOV $testcase" 2
    set testcase [remote_download host $testcase];
    set result [remote_exec host $GCOV $gcov_args];
    if { [lindex $result 0] != 0 } {
	fail "$subdir/$testcase gcov failed: [lindex $result 1]"
	clean-gcov $testcase
	return
    }

    # Get the gcov output file after making sure it exists.
    set files [glob -nocomplain $testcase.gcov]
    if { $files == "" } {
        fail "$subdir/$testcase gcov failed: $testcase.gcov does not exist"
        clean-gcov $testcase
        return;
    }
    remote_upload host $testcase.gcov $testcase.gcov;

    if { $gcov_verify_xfail != "" } {
	eval setup_xfail [split $gcov_verify_xfail]
    }

    # Check that line execution counts are as expected.
    set lfailed [verify-lines $testcase $testcase.gcov]

    # If requested via the .x file, check that branch and call information
    # is correct.
    if { $gcov_verify_branches } {
	set bfailed [verify-branches $testcase $testcase.gcov]
    } else {
	set bfailed 0
    }
    if { $gcov_verify_calls } {
	set cfailed [verify-calls $testcase $testcase.gcov]
    } else {
	set cfailed 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]
    if { $tfailed > 0 } {
	fail "$subdir/$testcase gcov: $lfailed failures in line counts, $bfailed in branch percentages, $cfailed in return percentages"
    } else {
	pass "$subdir/$testcase gcov"
	clean-gcov $testcase
    }
}