CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
complexity_limiter.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Symbolic Execution
4
5Author: John Dumbell
6
7\*******************************************************************/
8
10
11#include <util/options.h>
12
13#include "goto_symex_state.h"
14
15#include <cmath>
16
18 message_handlert &message_handler,
19 const optionst &options)
20 : log(message_handler)
21{
22 std::size_t limit = options.get_signed_int_option("symex-complexity-limit");
23 if((complexity_active = limit > 0))
24 {
25 // This gives a curve that allows low limits to be rightly restrictive,
26 // while larger numbers are very large.
27 max_complexity = static_cast<std::size_t>((limit * limit) * 25);
28
29 const std::size_t failed_child_loops_limit = options.get_signed_int_option(
30 "symex-complexity-failed-child-loops-limit");
31 const std::size_t unwind = options.get_signed_int_option("unwind");
32
33 // If we have complexity enabled, try to work out a failed_children_limit.
34 // In order of priority:
35 // * explicit limit
36 // * inferred limit from unwind
37 // * best limit we can apply with very little information.
40 else if(unwind > 0)
41 max_loops_complexity = std::max(static_cast<int>(floor(unwind / 3)), 1);
42 else
44 }
45}
46
49{
50 for(auto frame_iter = current_call_stack.rbegin();
52 ++frame_iter)
53 {
54 // As we're walking bottom-up, the first frame we find with active loops
55 // is our closest active one.
56 if(!frame_iter->active_loops.empty())
57 {
58 return &frame_iter->active_loops.back();
59 }
60 }
61
62 return {};
63}
64
68{
69 for(auto frame_iter = current_call_stack.rbegin();
71 ++frame_iter)
72 {
73 for(auto &loop_iter : frame_iter->active_loops)
74 {
75 for(auto &blacklisted_loop : loop_iter.blacklisted_loops)
76 {
77 if(blacklisted_loop.get().contains(instr))
78 {
79 return true;
80 }
81 }
82 }
83 }
84
85 return false;
86}
87
90{
91 std::size_t sum_complexity = 0;
92
93 // This will walk all currently active loops, from inner-most to outer-most,
94 // and sum the times their branches have failed.
95 //
96 // If we find that this sum is higher than our max_loops_complexity we take
97 // note of the loop that happens in and then cause every parent still
98 // executing that loop to blacklist it.
99 //
100 // This acts as a context-sensitive loop cancel, so if we've got unwind 20
101 // and find out at the 3rd iteration a particular nested loop is too
102 // complicated, we make sure we don't execute it the other 17 times. But as
103 // soon as we're running the loop again via a different context it gets a
104 // chance to redeem itself.
105 lexical_loopst::loopt *loop_to_blacklist = nullptr;
106 for(auto frame_iter = current_call_stack.rbegin();
108 ++frame_iter)
109 {
110 for(auto it = frame_iter->active_loops.rbegin();
111 it != frame_iter->active_loops.rend();
112 it++)
113 {
114 auto &loop_info = *it;
115
116 // Because we're walking in reverse this will only be non-empty for
117 // parents of the loop that's been blacklisted. We then add that to their
118 // internal lists of blacklisted children.
120 {
121 loop_info.blacklisted_loops.emplace_back(*loop_to_blacklist);
122 }
123 else
124 {
125 sum_complexity += loop_info.children_too_complex;
127 {
129 }
130 }
131 }
132 }
133
134 return !loop_to_blacklist;
135}
136
139{
140 if(!complexity_limits_active() || !state.reachable)
142
143 std::size_t complexity =
145 if(complexity == 1)
147
148 auto &current_call_stack = state.call_stack();
149
150 // Check if this branch is too complicated to continue.
153 {
154 // If we're too complex, add a counter to the current loop we're in and
155 // check if we've violated child-loop complexity limits.
156 if(active_loop != nullptr)
157 {
158 active_loop->children_too_complex++;
159
160 // If we're considered too complex, cancel branch.
162 {
163 log.warning()
164 << "[symex-complexity] Loop operations considered too complex"
165 << (state.source.pc->source_location().is_not_nil()
166 ? " at: " + state.source.pc->source_location().as_string()
167 : ", location number: " +
168 std::to_string(state.source.pc->location_number) + ".")
169 << messaget::eom;
170
172 }
173 }
174
175 log.warning() << "[symex-complexity] Branch considered too complex"
176 << (state.source.pc->source_location().is_not_nil()
177 ? " at: " +
178 state.source.pc->source_location().as_string()
179 : ", location number: " +
180 std::to_string(state.source.pc->location_number) +
181 ".")
182 << messaget::eom;
183
184 // Then kill this branch.
186 }
187
188 // If we're not in any loop, return with no violation.
189 if(!active_loop)
191
192 // Check if we've entered a loop that has been previously black-listed, and
193 // if so then cancel before we go any further.
195 {
196 log.warning() << "[symex-complexity] Trying to enter blacklisted loop"
197 << (state.source.pc->source_location().is_not_nil()
198 ? " at: " +
199 state.source.pc->source_location().as_string()
200 : ", location number: " +
201 std::to_string(state.source.pc->location_number) +
202 ".")
203 << messaget::eom;
204
206 }
207
209}
210
221
226static std::size_t
227bounded_expr_size(const exprt &expr, std::size_t count, std::size_t limit)
228{
229 const auto &ops = expr.operands();
230 count += ops.size();
231 for(const auto &op : ops)
232 {
233 if(count >= limit)
234 {
235 return count;
236 }
237 count = bounded_expr_size(op, count, limit);
238 }
239 return count;
240}
241
242std::size_t
244{
245 return ::bounded_expr_size(expr, 1, limit);
246}
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
complexity_limitert(message_handlert &logger, const optionst &options)
complexity_violationt check_complexity(goto_symex_statet &state)
Checks the passed-in state to see if its become too complex for us to deal with, and if so set its gu...
bool complexity_limits_active()
Is the complexity module active?
static std::size_t bounded_expr_size(const exprt &expr, std::size_t limit)
Amount of nodes in expr approximately bounded by limit.
bool complexity_active
Is the complexity module active, usually coincides with a max_complexity value above 0.
static bool in_blacklisted_loop(const call_stackt &current_call_stack, const goto_programt::const_targett &instr)
Checks whether we're in a loop that is currently considered blacklisted, and shouldn't be executed.
std::size_t max_complexity
The max complexity rating that a branch can be before it's abandoned.
std::vector< symex_complexity_limit_exceeded_actiont > violation_transformations
Functions called when the heuristic has been violated.
std::size_t max_loops_complexity
The amount of branches that can fail within the scope of a loops execution before the entire loop is ...
static framet::active_loop_infot * get_current_active_loop(call_stackt &current_call_stack)
Returns inner-most currently active loop.
symex_complexity_limit_exceeded_actiont default_transformation
Default heuristic transformation. Sets state as unreachable.
void run_transformations(complexity_violationt complexity_violation, goto_symex_statet &current_state)
Runs a suite of transformations on the state and symex executable, performing whatever transformation...
bool are_loop_children_too_complicated(call_stackt &current_call_stack)
Checks whether the current loop execution stack has violated max_loops_complexity.
Base class for all expressions.
Definition expr.h:56
operandst & operands()
Definition expr.h:94
instructionst::const_iterator const_targett
guardt guard
Definition goto_state.h:58
bool reachable
Is this code reachable? If not we can take shortcuts such as not entering function calls,...
Definition goto_state.h:62
Central data structure: state.
call_stackt & call_stack()
symex_targett::sourcet source
exprt as_expr() const
Definition guard_expr.h:46
mstreamt & warning() const
Definition message.h:396
static eomt eom
Definition message.h:289
signed int get_signed_int_option(const std::string &option) const
Definition options.cpp:50
virtual void transform(const complexity_violationt heuristic_result, goto_symex_statet &current_state)
static std::size_t bounded_expr_size(const exprt &expr, std::size_t count, std::size_t limit)
Amount of nodes expr contains, with a bound on how far to search.
Symbolic Execution.
complexity_violationt
What sort of symex-complexity violation has taken place.
Symbolic Execution.
double floor(double x)
Definition math.c:1323
double log(double x)
Definition math.c:2449
Options.
goto_programt::const_targett pc