Skip to content

Commit b4c3a12

Browse files
authored
Merge pull request #6329 from NlightNFotis/assertions_before_assume
`--cover assume`: Add assert statements before assume to check for coverage of assume statements
2 parents f0ab57e + f869882 commit b4c3a12

File tree

18 files changed

+175
-2
lines changed

18 files changed

+175
-2
lines changed

doc/cprover-manual/modeling-assumptions.md

+34
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,37 @@ int main() {
7171
This code fails, as there is a choice of x and y which results in a counterexample
7272
(any choice in which x and y are different).
7373

74+
## Coverage
75+
76+
You can ask CBMC to give coverage information regarding `__CPROVER_assume` statements.
77+
This is useful when you need, for example, to check which assume statements may have
78+
led to an emptying of the search state space, resulting in `assert` statements being
79+
vaccuously passed.
80+
81+
To use that invoke CBMC with the `--cover assume` option. For example, for a file:
82+
83+
```c
84+
#include <assert.h>
85+
86+
int main()
87+
{
88+
int x;
89+
__CPROVER_assume(x > 0);
90+
__CPROVER_assume(x < 0);
91+
assert(0 == 1);
92+
}
93+
```
94+
95+
CBMC invoked with `cbmc --cover assume test.c` will report:
96+
97+
```sh
98+
[main.1] file assume_assert.c line 6 function main assert(false) before assume(x > 0): SATISFIED
99+
[main.2] file assume_assert.c line 6 function main assert(false) after assume(x > 0): SATISFIED
100+
[main.3] file assume_assert.c line 7 function main assert(false) before assume(x < 0): SATISFIED
101+
[main.4] file assume_assert.c line 7 function main assert(false) after assume(x < 0): FAILED
102+
```
103+
104+
When an `assert(false)` statement before the assume has the property status `SATISFIED`,
105+
but is followed by an `assert(false)` statement *after* the assume statement that has status
106+
`FAILED`, this is an indication that this specific assume statement (on the line reported)
107+
is one that is emptying the search space for model checking.

doc/cprover-manual/test-suite.md

+1
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ The table below summarizes the coverage criteria that CBMC supports.
213213
Criterion |Definition
214214
----------|----------
215215
assertion |For every assertion, generate a test that reaches it
216+
assume |For every assume, generate tests before and after the assume statement to indicate coverage before and after it
216217
location |For every location, generate a test that reaches it
217218
branch |Generate a test for every branch outcome
218219
decision |Generate a test for both outcomes of every Boolean expression that is not an operand of a propositional connective

jbmc/src/jbmc/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ OBJ += ../$(CPROVER_DIR)/src/ansi-c/ansi-c$(LIBEXT) \
2424
../$(CPROVER_DIR)/src/goto-instrument/cover$(OBJEXT) \
2525
../$(CPROVER_DIR)/src/goto-instrument/cover_basic_blocks$(OBJEXT) \
2626
../$(CPROVER_DIR)/src/goto-instrument/cover_filter$(OBJEXT) \
27+
../$(CPROVER_DIR)/src/goto-instrument/cover_instrument_assume$(OBJEXT) \
2728
../$(CPROVER_DIR)/src/goto-instrument/cover_instrument_branch$(OBJEXT) \
2829
../$(CPROVER_DIR)/src/goto-instrument/cover_instrument_condition$(OBJEXT) \
2930
../$(CPROVER_DIR)/src/goto-instrument/cover_instrument_decision$(OBJEXT) \

jbmc/src/jdiff/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ OBJ += ../$(CPROVER_DIR)/src/ansi-c/ansi-c$(LIBEXT) \
1818
../$(CPROVER_DIR)/src/goto-instrument/cover$(OBJEXT) \
1919
../$(CPROVER_DIR)/src/goto-instrument/cover_basic_blocks$(OBJEXT) \
2020
../$(CPROVER_DIR)/src/goto-instrument/cover_filter$(OBJEXT) \
21+
../$(CPROVER_DIR)/src/goto-instrument/cover_instrument_assume$(OBJEXT) \
2122
../$(CPROVER_DIR)/src/goto-instrument/cover_instrument_branch$(OBJEXT) \
2223
../$(CPROVER_DIR)/src/goto-instrument/cover_instrument_condition$(OBJEXT) \
2324
../$(CPROVER_DIR)/src/goto-instrument/cover_instrument_decision$(OBJEXT) \

jbmc/unit/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ CPROVER_LIBS =../src/java_bytecode/java_bytecode$(LIBEXT) \
105105
$(CPROVER_DIR)/src/goto-instrument/cover$(OBJEXT) \
106106
$(CPROVER_DIR)/src/goto-instrument/cover_basic_blocks$(OBJEXT) \
107107
$(CPROVER_DIR)/src/goto-instrument/cover_filter$(OBJEXT) \
108+
$(CPROVER_DIR)/src/goto-instrument/cover_instrument_assume$(OBJEXT) \
108109
$(CPROVER_DIR)/src/goto-instrument/cover_instrument_branch$(OBJEXT) \
109110
$(CPROVER_DIR)/src/goto-instrument/cover_instrument_condition$(OBJEXT) \
110111
$(CPROVER_DIR)/src/goto-instrument/cover_instrument_decision$(OBJEXT) \
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <assert.h>
2+
3+
int main()
4+
{
5+
int x;
6+
__CPROVER_assume(x > 0);
7+
__CPROVER_assume(x < 0);
8+
assert(0 == 1);
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
assume_assert.c
3+
--cover assume
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.coverage.1\] file assume_assert.c line \d function main assert\(false\) before assume\(x > 0\): SATISFIED$
7+
^\[main.coverage.2\] file assume_assert.c line \d function main assert\(false\) after assume\(x > 0\): SATISFIED$
8+
^\[main.coverage.3\] file assume_assert.c line \d function main assert\(false\) before assume\(x < 0\): SATISFIED$
9+
^\[main.coverage.4\] file assume_assert.c line \d function main assert\(false\) after assume\(x < 0\): FAILED$
10+
--
11+
^warning: ignoring
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <assert.h>
2+
3+
int main(int argc, char *argv[])
4+
{
5+
int a;
6+
7+
if(a > 0)
8+
{
9+
assert(a > 0);
10+
}
11+
else if(a < 0)
12+
{
13+
__CPROVER_assume(a >= 0);
14+
assert(a < 0);
15+
}
16+
else
17+
{
18+
assert(a == 0);
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
assume_assert.c
3+
--cover assume
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.coverage.1\] file assume_assert.c line \d+ function main assert\(false\) before assume\(a >= 0\): SATISFIED$
7+
^\[main.coverage.2\] file assume_assert.c line \d+ function main assert\(false\) after assume\(a >= 0\): FAILED$
8+
--
9+
^warning: ignoring

src/cbmc/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ OBJ += ../ansi-c/ansi-c$(LIBEXT) \
2626
../goto-instrument/cover$(OBJEXT) \
2727
../goto-instrument/cover_basic_blocks$(OBJEXT) \
2828
../goto-instrument/cover_filter$(OBJEXT) \
29+
../goto-instrument/cover_instrument_assume$(OBJEXT) \
2930
../goto-instrument/cover_instrument_branch$(OBJEXT) \
3031
../goto-instrument/cover_instrument_condition$(OBJEXT) \
3132
../goto-instrument/cover_instrument_decision$(OBJEXT) \

src/goto-checker/properties.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Author: Daniel Kroening, Peter Schrammel
1212
#ifndef CPROVER_GOTO_CHECKER_PROPERTIES_H
1313
#define CPROVER_GOTO_CHECKER_PROPERTIES_H
1414

15-
#include <unordered_map>
15+
#include <map>
1616

1717
#include <goto-programs/goto_program.h>
1818

@@ -73,7 +73,7 @@ struct property_infot
7373
};
7474

7575
/// A map of property IDs to property infos
76-
typedef std::unordered_map<irep_idt, property_infot> propertiest;
76+
typedef std::map<irep_idt, property_infot> propertiest;
7777

7878
/// Returns the properties in the goto model
7979
propertiest initialize_properties(const abstract_goto_modelt &);

src/goto-diff/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ OBJ += ../ansi-c/ansi-c$(LIBEXT) \
1818
../goto-instrument/cover$(OBJEXT) \
1919
../goto-instrument/cover_basic_blocks$(OBJEXT) \
2020
../goto-instrument/cover_filter$(OBJEXT) \
21+
../goto-instrument/cover_instrument_assume$(OBJEXT) \
2122
../goto-instrument/cover_instrument_branch$(OBJEXT) \
2223
../goto-instrument/cover_instrument_condition$(OBJEXT) \
2324
../goto-instrument/cover_instrument_decision$(OBJEXT) \

src/goto-instrument/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ SRC = accelerate/accelerate.cpp \
2424
cover.cpp \
2525
cover_basic_blocks.cpp \
2626
cover_filter.cpp \
27+
cover_instrument_assume.cpp \
2728
cover_instrument_branch.cpp \
2829
cover_instrument_condition.cpp \
2930
cover_instrument_decision.cpp \

src/goto-instrument/cover.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ void cover_instrumenterst::add_from_criterion(
9999
case coverage_criteriont::COVER:
100100
instrumenters.push_back(
101101
util_make_unique<cover_cover_instrumentert>(symbol_table, goal_filters));
102+
break;
103+
case coverage_criteriont::ASSUME:
104+
instrumenters.push_back(
105+
util_make_unique<cover_assume_instrumentert>(symbol_table, goal_filters));
102106
}
103107
}
104108

@@ -126,6 +130,8 @@ parse_coverage_criterion(const std::string &criterion_string)
126130
c = coverage_criteriont::MCDC;
127131
else if(criterion_string == "cover")
128132
c = coverage_criteriont::COVER;
133+
else if(criterion_string == "assume")
134+
c = coverage_criteriont::ASSUME;
129135
else
130136
{
131137
std::stringstream s;
@@ -201,6 +207,9 @@ cover_configt get_cover_config(
201207

202208
if(c == coverage_criteriont::ASSERTION)
203209
cover_config.keep_assertions = true;
210+
// Make sure that existing assertions don't get replaced by assumes
211+
else if(c == coverage_criteriont::ASSUME)
212+
cover_config.keep_assertions = true;
204213

205214
instrumenters.add_from_criterion(c, symbol_table, *goal_filters);
206215
}

src/goto-instrument/cover.h

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class optionst;
4141

4242
enum class coverage_criteriont
4343
{
44+
ASSUME,
4445
LOCATION,
4546
BRANCH,
4647
DECISION,

src/goto-instrument/cover_instrument.h

+20
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,24 @@ void cover_instrument_end_of_function(
292292
goto_programt &goto_program,
293293
const cover_instrumenter_baset::assertion_factoryt &);
294294

295+
// assume-instructions instrumenter.
296+
class cover_assume_instrumentert : public cover_instrumenter_baset
297+
{
298+
public:
299+
cover_assume_instrumentert(
300+
const symbol_tablet &_symbol_table,
301+
const goal_filterst &_goal_filters)
302+
: cover_instrumenter_baset(_symbol_table, _goal_filters, "location")
303+
{
304+
}
305+
306+
protected:
307+
void instrument(
308+
const irep_idt &,
309+
goto_programt &,
310+
goto_programt::targett &,
311+
const cover_blocks_baset &,
312+
const assertion_factoryt &) const override;
313+
};
314+
295315
#endif // CPROVER_GOTO_INSTRUMENT_COVER_INSTRUMENT_H
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/// \file cover_instrument_assume.cpp
2+
/// Author: Diffblue Ltd.
3+
/// Coverage Instrumentation for ASSUME instructions.
4+
5+
#include "cover_instrument.h"
6+
7+
#include "ansi-c/expr2c.h"
8+
#include "goto-programs/goto_program.h"
9+
#include "util/std_expr.h"
10+
#include <util/namespace.h>
11+
12+
/// Instrument program to check coverage of assume statements.
13+
/// \param function_id The name of the function under instrumentation.
14+
/// \param goto_program The goto-program (function under instrumentation).
15+
/// \param i_it The current instruction (instruction under instrumentation).
16+
/// \param make_assertion The assertion generator function.
17+
void cover_assume_instrumentert::instrument(
18+
const irep_idt &function_id,
19+
goto_programt &goto_program,
20+
goto_programt::targett &i_it,
21+
const cover_blocks_baset &,
22+
const assertion_factoryt &make_assertion) const
23+
{
24+
if(i_it->is_assume())
25+
{
26+
const auto location = i_it->source_location;
27+
const auto assume_condition =
28+
expr2c(i_it->get_condition(), namespacet{symbol_tablet()});
29+
const auto comment_before =
30+
"assert(false) before assume(" + assume_condition + ")";
31+
const auto comment_after =
32+
"assert(false) after assume(" + assume_condition + ")";
33+
34+
const auto assert_before = make_assertion(false_exprt{}, location);
35+
goto_programt::targett t = goto_program.insert_before(i_it, assert_before);
36+
initialize_source_location(t, comment_before, function_id);
37+
38+
const auto assert_after = make_assertion(false_exprt{}, location);
39+
t = goto_program.insert_after(i_it, assert_after);
40+
initialize_source_location(t, comment_after, function_id);
41+
}
42+
// Otherwise, skip existing assertions.
43+
else if(i_it->is_assert())
44+
{
45+
const auto location = i_it->source_location;
46+
// Filter based on if assertion was added by us as part of instrumentation.
47+
if(location.get_property_class() != "coverage")
48+
{
49+
i_it->turn_into_skip();
50+
}
51+
}
52+
}

unit/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ BMC_DEPS =../src/cbmc/c_test_input_generator$(OBJEXT) \
221221
../src/goto-instrument/cover$(OBJEXT) \
222222
../src/goto-instrument/cover_basic_blocks$(OBJEXT) \
223223
../src/goto-instrument/cover_filter$(OBJEXT) \
224+
../src/goto-instrument/cover_instrument_assume$(OBJEXT) \
224225
../src/goto-instrument/cover_instrument_branch$(OBJEXT) \
225226
../src/goto-instrument/cover_instrument_condition$(OBJEXT) \
226227
../src/goto-instrument/cover_instrument_decision$(OBJEXT) \

0 commit comments

Comments
 (0)