Skip to content

Commit 8699438

Browse files
authored
Merge pull request #6839 from tautschnig/feature/block-coverage-lines
Report basic block lines in a reliably parseable form
2 parents 8a41865 + 2307b37 commit 8699438

File tree

16 files changed

+240
-134
lines changed

16 files changed

+240
-134
lines changed

doc/assets/xml_spec.xsd

+32
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,37 @@
173173
</xs:complexType>
174174
</xs:element>
175175

176+
<xs:element name="line">
177+
<xs:complexType>
178+
<xs:simpleContent>
179+
<xs:extension base="xs:string">
180+
<xs:attribute name="file" type="xs:string"/>
181+
<xs:attribute name="function" type="xs:string"/>
182+
</xs:extension>
183+
</xs:simpleContent>
184+
</xs:complexType>
185+
</xs:element>
186+
187+
<xs:element name="basic_block_lines">
188+
<xs:complexType>
189+
<xs:sequence>
190+
<xs:element ref="line" minOccurs="0"/>
191+
</xs:sequence>
192+
</xs:complexType>
193+
</xs:element>
194+
195+
<xs:element name="goal">
196+
<xs:complexType>
197+
<xs:sequence>
198+
<xs:element ref="location" minOccurs="0"/>
199+
<xs:element ref="basic_block_lines" minOccurs="0"/>
200+
</xs:sequence>
201+
<xs:attribute name="description" type="xs:string"/>
202+
<xs:attribute name="id" type="xs:string"/>
203+
<xs:attribute name="status" type="xs:string"/>
204+
</xs:complexType>
205+
</xs:element>
206+
176207
<xs:element name="message">
177208
<xs:complexType>
178209
<xs:sequence>
@@ -217,6 +248,7 @@
217248
<xs:element ref="result"/>
218249
<xs:element ref="refinement-iteration"/>
219250
<xs:element ref="fault-localization"/>
251+
<xs:element ref="goal"/>
220252
</xs:choice>
221253
<xs:element ref="cprover-status" minOccurs="0"/>
222254
</xs:sequence>

jbmc/regression/jdiff/java-properties/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ old.jar --json-ui --show-properties --cover location
55
activate-multi-line-match
66
^EXIT=0$
77
^SIGNAL=0$
8-
"deletedFunctions": \[\n \{\n "name": "java::Test.obsolete:\(\)V",\n "properties": \[\n \{\n "class": "coverage",\n "coveredLines": "18",\n "description": "block 1 \(lines Test.java:java::Test.obsolete:\(\)V:18\)",\n "expression": "false",\n "name": "java::Test.obsolete:\(\)V.coverage.1",\n "sourceLocation": \{\n "bytecodeIndex": "0",\n "file": "Test.java",\n "function": "java::Test.obsolete:\(\)V",\n "line": "18"\n }\n }\n ],\n "sourceLocation": \{\n "file": "Test.java",\n "function": "java::Test.obsolete:\(\)V",\n "line": "18"\n }\n }\n ],\n "modifiedFunctions": \[\n \{\n "name": "java::Test.<init>:\(\)V",\n "properties": \[\n \{\n "class": "coverage",\n "coveredLines": "6",\n "description": "block 2 \(lines Test.java:java::Test.<init>:\(\)V:6\)",\n "expression": "false",\n "name": "java::Test.<init>:\(\)V.coverage.1",\n "sourceLocation": \{\n "bytecodeIndex": "1",\n "file": "Test.java",\n "function": "java::Test.<init>:\(\)V",\n "line": "6"\n }\n },\n \{\n "class": "coverage",\n "coveredLines": "4",\n "description": "block 3 \(lines Test.java:java::Test.<init>:\(\)V:4\)",\n "expression": "false",\n "name": "java::Test.<init>:\(\)V.coverage.2",\n "sourceLocation": \{\n "bytecodeIndex": "3",\n "file": "Test.java",\n "function": "java::Test.<init>:\(\)V",\n "line": "4"\n }\n },\n \{\n "class": "coverage",\n "coveredLines": "4",\n "description": "block 4 \(lines Test.java:java::Test.<init>:\(\)V:4\)",\n "expression": "false",\n "name": "java::Test.<init>:\(\)V.coverage.3",\n "sourceLocation": \{\n "bytecodeIndex": "5",\n "file": "Test.java",\n "function": "java::Test.<init>:\(\)V",\n "line": "4"\n }\n },\n \{\n "class": "coverage",\n "coveredLines": "4",\n "description": "block 5 \(lines Test.java:java::Test.<init>:\(\)V:4\)",\n "expression": "false",\n "name": "java::Test.<init>:\(\)V.coverage.4",\n "sourceLocation": \{\n "bytecodeIndex": "6",\n "file": "Test.java",\n "function": "java::Test.<init>:\(\)V",\n "line": "4"\n }\n },\n \{\n "class": "coverage",\n "coveredLines": "7",\n "description": "block 6 \(lines Test.java:java::Test.<init>:\(\)V:7\)",\n "expression": "false",\n "name": "java::Test.<init>:\(\)V.coverage.5",\n "sourceLocation": \{\n "bytecodeIndex": "7",\n "file": "Test.java",\n "function": "java::Test.<init>:\(\)V",\n "line": "7"\n }\n }\n ],\n "sourceLocation": \{\n "file": "Test.java",\n "function": "java::Test.<init>:\(\)V",\n "line": "6"\n }\n },\n \{\n "name": "java::Test.<clinit>:\(\)V",\n "properties": \[\n \{\n "class": "coverage",\n "coveredLines": "3",\n "description": "block 1 \(lines Test.java:java::Test.<clinit>:\(\)V:3\)",\n "expression": "false",\n "name": "java::Test.<clinit>:\(\)V.coverage.1",\n "sourceLocation": \{\n "bytecodeIndex": "1",\n "file": "Test.java",\n "function": "java::Test.<clinit>:\(\)V",\n "line": "3"\n }\n }\n ],\n "sourceLocation": \{\n "file": "Test.java",\n "function": "java::Test.<clinit>:\(\)V",\n "line": "3"\n }\n }\n ],\n "newFunctions": \[\n \{\n "name": "java::Test.newfun:\(\)V",\n "properties": \[\n \{\n "class": "coverage",\n "coveredLines": "18",\n "description": "block 1 \(lines Test.java:java::Test.newfun:\(\)V:18\)",\n "expression": "false",\n "name": "java::Test.newfun:\(\)V.coverage.1",\n "sourceLocation": \{\n "bytecodeIndex": "0",\n "file": "Test.java",\n "function": "java::Test.newfun:\(\)V",\n "line": "18"\n }\n }\n ],\n "sourceLocation": \{\n "file": "Test.java",\n "function": "java::Test.newfun:\(\)V",\n "line": "18"\n }\n }\n ],\n
8+
"deletedFunctions": \[\n \{\n "name": "java::Test.obsolete:\(\)V",\n "properties": \[\n \{\n "basicBlockLines": \{\n "Test.java": \{\n "java::Test.obsolete:\(\)V": "18"\n \}\n \},\n "class": "coverage",\n "description": "block 1 \(lines Test.java:java::Test.obsolete:\(\)V:18\)",\n "expression": "false",\n "name": "java::Test.obsolete:\(\)V.coverage.1",\n "sourceLocation": \{\n "bytecodeIndex": "0",\n "file": "Test.java",\n "function": "java::Test.obsolete:\(\)V",\n "line": "18"\n }\n }\n ],\n "sourceLocation": \{\n "file": "Test.java",\n "function": "java::Test.obsolete:\(\)V",\n "line": "18"\n }\n }\n ],\n "modifiedFunctions": \[\n \{\n "name": "java::Test.<init>:\(\)V",\n "properties": \[\n \{\n "basicBlockLines": \{\n "Test.java": \{\n "java::Test.<init>:\(\)V": "6"\n \}\n \},\n "class": "coverage",\n "description": "block 2 \(lines Test.java:java::Test.<init>:\(\)V:6\)",\n "expression": "false",\n "name": "java::Test.<init>:\(\)V.coverage.1",\n "sourceLocation": \{\n "bytecodeIndex": "1",\n "file": "Test.java",\n "function": "java::Test.<init>:\(\)V",\n "line": "6"\n }\n },\n \{\n "basicBlockLines": \{\n "Test.java": \{\n "java::Test.<init>:\(\)V": "4"\n \}\n \},\n "class": "coverage",\n "description": "block 3 \(lines Test.java:java::Test.<init>:\(\)V:4\)",\n "expression": "false",\n "name": "java::Test.<init>:\(\)V.coverage.2",\n "sourceLocation": \{\n "bytecodeIndex": "3",\n "file": "Test.java",\n "function": "java::Test.<init>:\(\)V",\n "line": "4"\n }\n },\n \{\n "basicBlockLines": \{\n "Test.java": \{\n "java::Test.<init>:\(\)V": "4"\n \}\n \},\n "class": "coverage",\n "description": "block 4 \(lines Test.java:java::Test.<init>:\(\)V:4\)",\n "expression": "false",\n "name": "java::Test.<init>:\(\)V.coverage.3",\n "sourceLocation": \{\n "bytecodeIndex": "5",\n "file": "Test.java",\n "function": "java::Test.<init>:\(\)V",\n "line": "4"\n }\n },\n \{\n "basicBlockLines": \{\n "Test.java": \{\n "java::Test.<init>:\(\)V": "4"\n \}\n \},\n "class": "coverage",\n "description": "block 5 \(lines Test.java:java::Test.<init>:\(\)V:4\)",\n "expression": "false",\n "name": "java::Test.<init>:\(\)V.coverage.4",\n "sourceLocation": \{\n "bytecodeIndex": "6",\n "file": "Test.java",\n "function": "java::Test.<init>:\(\)V",\n "line": "4"\n }\n },\n \{\n "basicBlockLines": \{\n "Test.java": \{\n "java::Test.<init>:\(\)V": "7"\n \}\n \},\n "class": "coverage",\n "description": "block 6 \(lines Test.java:java::Test.<init>:\(\)V:7\)",\n "expression": "false",\n "name": "java::Test.<init>:\(\)V.coverage.5",\n "sourceLocation": \{\n "bytecodeIndex": "7",\n "file": "Test.java",\n "function": "java::Test.<init>:\(\)V",\n "line": "7"\n }\n }\n ],\n "sourceLocation": \{\n "file": "Test.java",\n "function": "java::Test.<init>:\(\)V",\n "line": "6"\n }\n },\n \{\n "name": "java::Test.<clinit>:\(\)V",\n "properties": \[\n \{\n "basicBlockLines": \{\n "Test.java": \{\n "java::Test.<clinit>:\(\)V": "3"\n \}\n \},\n "class": "coverage",\n "description": "block 1 \(lines Test.java:java::Test.<clinit>:\(\)V:3\)",\n "expression": "false",\n "name": "java::Test.<clinit>:\(\)V.coverage.1",\n "sourceLocation": \{\n "bytecodeIndex": "1",\n "file": "Test.java",\n "function": "java::Test.<clinit>:\(\)V",\n "line": "3"\n }\n }\n ],\n "sourceLocation": \{\n "file": "Test.java",\n "function": "java::Test.<clinit>:\(\)V",\n "line": "3"\n }\n }\n ],\n "newFunctions": \[\n \{\n "name": "java::Test.newfun:\(\)V",\n "properties": \[\n \{\n "basicBlockLines": \{\n "Test.java": \{\n "java::Test.newfun:\(\)V": "18"\n \}\n \},\n "class": "coverage",\n "description": "block 1 \(lines Test.java:java::Test.newfun:\(\)V:18\)",\n "expression": "false",\n "name": "java::Test.newfun:\(\)V.coverage.1",\n "sourceLocation": \{\n "bytecodeIndex": "0",\n "file": "Test.java",\n "function": "java::Test.newfun:\(\)V",\n "line": "18"\n }\n }\n ],\n "sourceLocation": \{\n "file": "Test.java",\n "function": "java::Test.newfun:\(\)V",\n "line": "18"\n }\n }\n ],\n
99
--
1010
^warning: ignoring

jbmc/unit/java_bytecode/java_virtual_functions/virtual_functions.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,12 @@ SCENARIO(
159159
// instruction with a new bytecode index begins with ASSERT
160160
REQUIRE(it->type() == goto_program_instruction_typet::ASSERT);
161161
// the assertion corresponds to a line coverage goal
162-
REQUIRE_FALSE(loc.get_basic_block_covered_lines().empty());
162+
REQUIRE_FALSE(loc.get_basic_block_source_lines().is_nil());
163163
}
164164
else
165165
{
166166
// there is no line coverage goal in the middle of a block
167-
REQUIRE(loc.get_basic_block_covered_lines().empty());
167+
REQUIRE(loc.get_basic_block_source_lines().is_nil());
168168
}
169169

170170
++it;

regression/cbmc-cover/json-goals1/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ main.c
44
activate-multi-line-match
55
^EXIT=0$
66
^SIGNAL=0$
7-
\{\n\s*"goals": \[\n\s*\{\n\s*"description": ".*",\n\s*"goal": "main\.coverage\.[1-4]",\n\s*"sourceLocation": \{(\n.+){5}\},\n\s*"status": "satisfied"\n\s*\},\n\s*\{\n\s*"description": ".*",\n\s*"goal": "main\.coverage\.[1-4]",\n\s*"sourceLocation": \{(\n.+){5}\},\n\s*"status": "satisfied"\n\s*\},\n\s*\{\n\s*"description": ".*",\n\s*"goal": "main\.coverage\.[1-4]",\n\s*"sourceLocation": \{(\n.+){5}\},\n\s*"status": "satisfied"\n\s*\},\n\s*\{\n\s*"description": ".*",\n\s*"goal": "main\.coverage\.[1-4]",\n\s*"sourceLocation": \{(\n.+){5}\},\n\s*"status": "satisfied"\n\s*\}\n\s*\],\n\s*"goalsCovered": 4,\n\s*"totalGoals": 4\n\s*\}
7+
\{\n\s*"goals": \[\n\s*\{\n\s*"basicBlockLines": \{\n\s*"main.c": \{\n\s*"main": "\d+"\n\s*\}\n\s*\},\n\s*"description": "block \d+ \(lines main.c:main:\d+\)",\n\s*"goal": "main\.coverage\.[1-4]",\n\s*"sourceLocation": \{(\n.+){5}\},\n\s*"status": "satisfied"\n\s*\},\n\s*\{\n\s*"basicBlockLines": \{\n\s*"main.c": \{\n\s*"main": "\d+"\n\s*\}\n\s*\},\n\s*"description": "block \d+ \(lines main.c:main:\d+\)",\n\s*"goal": "main\.coverage\.[1-4]",\n\s*"sourceLocation": \{(\n.+){5}\},\n\s*"status": "satisfied"\n\s*\},\n\s*\{\n\s*"basicBlockLines": \{\n\s*"main.c": \{\n\s*"main": "\d+"\n\s*\}\n\s*\},\n\s*"description": "block \d+ \(lines main.c:main:\d+\)",\n\s*"goal": "main\.coverage\.[1-4]",\n\s*"sourceLocation": \{(\n.+){5}\},\n\s*"status": "satisfied"\n\s*\},\n\s*\{\n\s*"basicBlockLines": \{\n\s*"main.c": \{\n\s*"main": "\d+"\n\s*\}\n\s*\},\n\s*"description": "block \d+ \(lines main.c:main:\d+\)",\n\s*"goal": "main\.coverage\.[1-4]",\n\s*"sourceLocation": \{(\n.+){5}\},\n\s*"status": "satisfied"\n\s*\}\n\s*\],\n\s*"goalsCovered": 4,\n\s*"totalGoals": 4\n\s*\}
88
--
99
^warning: ignoring

src/goto-checker/cover_goals_report_util.cpp

+47-5
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,30 @@ static void output_goals_xml(const propertiest &properties, messaget &log)
7575
: "FAILED"}},
7676
{});
7777

78-
if(property_pair.second.pc->source_location().is_not_nil())
79-
xml_result.new_element() =
80-
xml(property_pair.second.pc->source_location());
78+
const auto &source_location = property_pair.second.pc->source_location();
79+
if(source_location.is_not_nil())
80+
{
81+
xml_result.new_element() = xml(source_location);
82+
83+
const irept &basic_block_lines =
84+
source_location.get_basic_block_source_lines();
85+
if(basic_block_lines.is_not_nil())
86+
{
87+
xmlt basic_block_lines_xml{"basic_block_lines"};
88+
for(const auto &file_entry : basic_block_lines.get_named_sub())
89+
{
90+
for(const auto &lines_entry : file_entry.second.get_named_sub())
91+
{
92+
xmlt line{"line"};
93+
line.set_attribute("file", id2string(file_entry.first));
94+
line.set_attribute("function", id2string(lines_entry.first));
95+
line.data = id2string(lines_entry.second.id());
96+
basic_block_lines_xml.new_element(line);
97+
}
98+
}
99+
xml_result.new_element(basic_block_lines_xml);
100+
}
101+
}
81102

82103
log.result() << xml_result;
83104
}
@@ -103,8 +124,29 @@ static void output_goals_json(
103124
json_goal["goal"] = json_stringt(property_pair.first);
104125
json_goal["description"] = json_stringt(property_info.description);
105126

106-
if(property_info.pc->source_location().is_not_nil())
107-
json_goal["sourceLocation"] = json(property_info.pc->source_location());
127+
const auto &source_location = property_info.pc->source_location();
128+
if(source_location.is_not_nil())
129+
{
130+
json_goal["sourceLocation"] = json(source_location);
131+
132+
const irept &basic_block_lines =
133+
source_location.get_basic_block_source_lines();
134+
if(basic_block_lines.is_not_nil())
135+
{
136+
json_objectt basic_block_lines_json;
137+
for(const auto &file_entry : basic_block_lines.get_named_sub())
138+
{
139+
json_objectt file_lines_json;
140+
for(const auto &lines_entry : file_entry.second.get_named_sub())
141+
{
142+
file_lines_json[id2string(lines_entry.first)] =
143+
json_stringt{lines_entry.second.id()};
144+
}
145+
basic_block_lines_json[id2string(file_entry.first)] = file_lines_json;
146+
}
147+
json_goal["basicBlockLines"] = basic_block_lines_json;
148+
}
149+
}
108150

109151
goals_array.push_back(std::move(json_goal));
110152
}

0 commit comments

Comments
 (0)