-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathcoverage-object.adb
269 lines (210 loc) · 8.43 KB
/
coverage-object.adb
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
------------------------------------------------------------------------------
-- --
-- GNATcoverage --
-- --
-- Copyright (C) 2009-2024, AdaCore --
-- --
-- GNATcoverage is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This software is distributed in the hope that it will be useful --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY 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 distributed with this software; see file --
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy --
-- of the license. --
------------------------------------------------------------------------------
with Ada.Containers.Ordered_Sets;
with Interfaces;
package body Coverage.Object is
------------------------
-- Compute_Line_State --
------------------------
procedure Compute_Line_State (Line : Line_Info_Access) is
State : Line_State := No_Code;
begin
if Line.Obj_Infos /= null then
for El of Line.Obj_Infos.all loop
State := State * El.State;
end loop;
end if;
if Enabled (Branch) then
Line.State (Coverage_Level_To_Cell (Branch)) := State;
else
Line.State (Coverage_Level_To_Cell (Insn)) := State;
end if;
end Compute_Line_State;
type Address_Range is record
First, Last : Pc_Type;
end record;
function "<" (Left, Right : Address_Range) return Boolean;
-- Lexicographical order
---------
-- "<" --
---------
function "<" (Left, Right : Address_Range) return Boolean is
use Interfaces;
begin
if Left.First = Right.First then
return Left.Last < Right.Last;
else
return Left.First < Right.First;
end if;
end "<";
package Address_Range_Sets is new Ada.Containers.Ordered_Sets
(Element_Type => Address_Range);
function PC_Range_Covered
(Ranges : Address_Range_Sets.Set;
First, Last : Pc_Type) return Boolean;
-- Returns whether for every address in First .. Last, there is one
-- Address_Range of Ranges that contains it.
----------------------
-- PC_Range_Covered --
----------------------
function PC_Range_Covered
(Ranges : Address_Range_Sets.Set;
First, Last : Pc_Type) return Boolean
is
use Interfaces;
use Address_Range_Sets;
use Ada.Containers;
Cur : Cursor;
Current_Range : Address_Range;
begin
if Ranges.Is_Empty then
return False;
elsif Ranges.Length = 1 then
-- If there is only one trace entry, simply check that it covers the
-- entire range of interest.
return Ranges.First_Element.First <= First
and then Ranges.First_Element.Last >= Last;
else
-- Ranges are sorted. Quick check on the extremes first.
if First < Ranges.First_Element.First
or else Last > Ranges.Last_Element.Last
then
return False;
end if;
-- Check that every PC in the range of interest is covered by one of
-- the trace entries.
Cur := Ranges.First;
for PC in First .. Last loop
-- See if we can find a range for the current PC. Ranges
-- previously deemed invalid for a PC can't be valid for a
-- subsequent PC, so we can just resume our iteration from the
-- last (or original) position in the set of ranges.
loop
pragma Assert (Has_Element (Cur));
Current_Range := Element (Cur);
-- If the current PC is before the start of our current range,
-- PC is not in and no subsequent range would include it either
-- because .First are increasing.
if Current_Range.First > PC then
return False;
end if;
-- PC >= Current_Range.First here. If current PC is within the
-- range, move to the next PC.
exit when PC <= Current_Range.Last;
-- Otherwise, try the next range. There has to be one at this
-- spot as the early test on extreme bounds ensures that even
-- the highest PC is <= Last_Range.Last.
Next (Cur);
end loop;
-- Reach here as soon as we found a range for the current PC.
-- Loop over to the following one.
end loop;
-- Reaching here, we found a range for every PC
return True;
end if;
end PC_Range_Covered;
--------------------
-- Get_Line_State --
--------------------
function Get_Line_State
(Base : Traces_Base;
First : Pc_Type;
Last : Pc_Type) return Line_State
is
use Interfaces;
Result : Line_State := No_Code;
It : Entry_Iterator;
T : Trace_Entry;
Ranges : Address_Range_Sets.Set;
begin
Init_Post (Base, It, First);
-- Find all trace entries that intersect the address range First .. Last
-- and update the coverage result according to the coverage state of
-- each trace entry.
loop
Get_Next_Trace (T, It);
exit when T = Bad_Trace or else T.First > Last;
Update_Line_State (Result, T.State);
Ranges.Include ((First => T.First, Last => T.Last));
end loop;
-- If there is no trace entry for this instruction range, this can only
-- mean that it is not covered.
if Result = No_Code then
return Not_Covered;
end if;
-- We just found all trace entries that intersect the address range
-- First .. Last. We now need to check that for every address in this
-- range, there is actually a trace entry that covers it. Otherwise this
-- means that there is at least one instruction that is not covered.
if not PC_Range_Covered (Ranges, First, Last) then
Update_Line_State (Result, Not_Covered);
end if;
return Result;
end Get_Line_State;
-----------------------
-- Update_Line_State --
-----------------------
procedure Update_Line_State
(L : in out Line_State;
I : Known_Insn_State)
is
begin
case L is
when Not_Covered =>
if I = Not_Covered then
L := Not_Covered;
else
L := Partially_Covered;
end if;
when Partially_Covered =>
null;
when Covered =>
if I = Covered or else I = Both_Taken then
L := Covered;
else
L := Partially_Covered;
end if;
when No_Code =>
if I = Covered or else I = Both_Taken then
L := Covered;
elsif I = Not_Covered then
L := Not_Covered;
else
L := Partially_Covered;
end if;
when Not_Coverable =>
-- Line can't be marked as not coverable, since there *is* an
-- associated instruction.
raise Program_Error with
"Attempting to set an instruction state to Not_Coverable,"
& " but the instruction comes from an executable.";
when Undetermined_Coverage =>
-- Line can't (at the moment) be marked as undetermined line state
-- when not using source traces.
raise Program_Error with
"Undetermined_Coverage line state reserved for source"
& " coverage";
when Disabled_Coverage =>
-- Line can't be marked as disabled coverage line state when not
-- using source traces.
raise Program_Error with
"Disabled_Coverage line state reserved for source"
& " coverage";
end case;
end Update_Line_State;
end Coverage.Object;