Skip to content

Commit a4605cd

Browse files
author
Remi Delmas
committed
Adds support for "checked:named-check" pragmas to avoid redudant instrumentation.
When several invocations to goto-instrument/cmbc are performed with named-check flags, the same assertions were generated several times, and assertions generated by a check A in some pass would be instrumented by a check B in some ulterior pass. "checked" pragmas allow to keep track of which checks have been performed on which instructions and are saved in the goto binaries, hence surviving across invocations of the tools. The presente of a "checked" pragma for a given check on an instruction prevents future invocations of the tools to generate again the assertions of that check. We also tag all assertions generated by goto_check with "disable" pragmas to avoid instrumenting instrumentation assertions in ulterior invocations of the tool.
1 parent 7b40362 commit a4605cd

File tree

1 file changed

+157
-64
lines changed

1 file changed

+157
-64
lines changed

src/analyses/goto_check_c.cpp

Lines changed: 157 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Author: Daniel Kroening, [email protected]
1212
#include "goto_check_c.h"
1313

1414
#include <algorithm>
15+
#include <optional>
1516

1617
#include <util/arith_tools.h>
1718
#include <util/array_name.h>
@@ -278,6 +279,22 @@ class goto_check_ct
278279
bool enable_assumptions;
279280
bool enable_pointer_primitive_check;
280281

282+
/// Maps a named-check name to the corresponding boolean flag.
283+
std::map<irep_idt, bool *> name_to_flag{
284+
{"bounds-check", &enable_bounds_check},
285+
{"pointer-check", &enable_pointer_check},
286+
{"memory-leak-check", &enable_memory_leak_check},
287+
{"div-by-zero-check", &enable_div_by_zero_check},
288+
{"enum-range-check", &enable_enum_range_check},
289+
{"signed-overflow-check", &enable_signed_overflow_check},
290+
{"unsigned-overflow-check", &enable_unsigned_overflow_check},
291+
{"pointer-overflow-check", &enable_pointer_overflow_check},
292+
{"conversion-check", &enable_conversion_check},
293+
{"undefined-shift-check", &enable_undefined_shift_check},
294+
{"float-overflow-check", &enable_float_overflow_check},
295+
{"nan-check", &enable_nan_check},
296+
{"pointer-primitive-check", &enable_pointer_primitive_check}};
297+
281298
typedef optionst::value_listt error_labelst;
282299
error_labelst error_labels;
283300

@@ -288,6 +305,36 @@ class goto_check_ct
288305
allocationst allocations;
289306

290307
irep_idt mode;
308+
309+
/// \brief Adds "checked" pragmas for all currently active named checks
310+
/// on the given source location.
311+
void add_active_named_check_pragmas(source_locationt &source_location) const;
312+
313+
/// \brief Adds "disable" pragmas for all named checks
314+
/// on the given source location.
315+
void
316+
add_all_disable_named_check_pragmas(source_locationt &source_location) const;
317+
318+
/// activation statuses for named checks
319+
typedef enum
320+
{
321+
ENABLE,
322+
DISABLE,
323+
CHECKED
324+
} check_statust;
325+
326+
/// optional (named-check, status) pair
327+
using named_check_statust = optionalt<std::pair<irep_idt, check_statust>>;
328+
329+
/// Matches a named-check string of the form
330+
///
331+
/// ```
332+
/// ("enable"|"disable"|"checked") ":" <named-check>
333+
/// ```
334+
///
335+
/// \returns a pair (name, status) if the match succeeds
336+
/// and the name is known, nothing otherwise.
337+
named_check_statust match_named_check(const irep_idt &named_check) const;
291338
};
292339

293340
void goto_check_ct::collect_allocations(const goto_functionst &goto_functions)
@@ -1566,6 +1613,8 @@ void goto_check_ct::add_guarded_property(
15661613
t->source_location_nonconst().set_comment(
15671614
comment + " in " + source_expr_string);
15681615
t->source_location_nonconst().set_property_class(property_class);
1616+
1617+
add_all_disable_named_check_pragmas(t->source_location_nonconst());
15691618
}
15701619
}
15711620

@@ -1840,11 +1889,13 @@ optionalt<exprt> goto_check_ct::rw_ok_check(exprt expr)
18401889
return {};
18411890
}
18421891

1843-
/// \brief Set a Boolean flag to a new value (via `set_flag`) and restore
1844-
/// the previous value when the entire object goes out of scope.
1892+
/// Allows to:
1893+
/// - override a Boolean flag with a new value via `set_flag`
1894+
/// - set a Boolean flag to false via `disable_flag`, such that
1895+
/// previous `set_flag` are overridden and future `set_flag` are ignored.
18451896
///
1846-
/// \remarks Calls to set_value are tracked to allow detecting doubles sets
1847-
/// with different values and trigger an INVARIANT.
1897+
/// A flag's initial value (before any `set_flag` or `disable_flag`) is restored
1898+
/// when the entire object goes out of scope.
18481899
class flag_resett
18491900
{
18501901
public:
@@ -1856,24 +1907,50 @@ class flag_resett
18561907
/// \brief Store the current value of \p flag and
18571908
/// then set its value to \p new_value.
18581909
///
1859-
/// \remarks an INVARIANT triggers iff the flag is set
1860-
/// more than once with different values.
1910+
/// - calling `set_flag` after `disable_flag` is a no-op
1911+
/// - calling `set_flag` twice triggers an INVARIANT
18611912
void set_flag(bool &flag, bool new_value, const irep_idt &flag_name)
18621913
{
1863-
bool seen = flags_to_reset.find(&flag) != flags_to_reset.end();
1914+
// make this a no-op if the flag is disabled
1915+
if(disabled_flags.find(&flag) != disabled_flags.end())
1916+
return;
1917+
1918+
// detect double sets
18641919
INVARIANT(
1865-
!(seen && flag != new_value),
1866-
"Flag " + id2string(flag_name) +
1867-
" set twice with incompatible values "
1868-
" at \n" +
1920+
flags_to_reset.find(&flag) == flags_to_reset.end(),
1921+
"Flag " + id2string(flag_name) + " set twice at \n" +
18691922
instruction.source_location().pretty());
18701923
if(flag != new_value)
18711924
{
1872-
flags_to_reset.emplace(&flag, flag);
1925+
flags_to_reset[&flag] = flag;
18731926
flag = new_value;
18741927
}
18751928
}
18761929

1930+
/// Sets the given flag to false, overriding any previous value.
1931+
///
1932+
/// - calling `disable_flag` after `set_flag` overrides the set value
1933+
/// - calling `disable_flag` twice triggers an INVARIANT
1934+
void disable_flag(bool &flag, const irep_idt &flag_name)
1935+
{
1936+
INVARIANT(
1937+
disabled_flags.find(&flag) == disabled_flags.end(),
1938+
"Flag " + id2string(flag_name) + " disabled twice at \n" +
1939+
instruction.source_location().pretty());
1940+
1941+
disabled_flags.insert(&flag);
1942+
1943+
// If the flag has not already been set,
1944+
// we store its current value in the reset map.
1945+
// Otherwise, the reset map already holds
1946+
// the initial value we want to reset it to, keep it as is.
1947+
if(flags_to_reset.find(&flag) == flags_to_reset.end())
1948+
flags_to_reset[&flag] = flag;
1949+
1950+
// set the flag to false in all cases.
1951+
flag = false;
1952+
}
1953+
18771954
/// \brief Restore the values of all flags that have been
18781955
/// modified via `set_flag`.
18791956
~flag_resett()
@@ -1885,6 +1962,7 @@ class flag_resett
18851962
private:
18861963
const goto_programt::instructiont &instruction;
18871964
std::map<bool *, bool> flags_to_reset;
1965+
std::set<bool *> disabled_flags;
18881966
};
18891967

18901968
void goto_check_ct::goto_check(
@@ -1915,60 +1993,32 @@ void goto_check_ct::goto_check(
19151993
const auto &pragmas = i.source_location().get_pragmas();
19161994
for(const auto &d : pragmas)
19171995
{
1918-
if(d.first == "disable:bounds-check")
1919-
resetter.set_flag(enable_bounds_check, false, d.first);
1920-
else if(d.first == "disable:pointer-check")
1921-
resetter.set_flag(enable_pointer_check, false, d.first);
1922-
else if(d.first == "disable:memory-leak-check")
1923-
resetter.set_flag(enable_memory_leak_check, false, d.first);
1924-
else if(d.first == "disable:div-by-zero-check")
1925-
resetter.set_flag(enable_div_by_zero_check, false, d.first);
1926-
else if(d.first == "disable:enum-range-check")
1927-
resetter.set_flag(enable_enum_range_check, false, d.first);
1928-
else if(d.first == "disable:signed-overflow-check")
1929-
resetter.set_flag(enable_signed_overflow_check, false, d.first);
1930-
else if(d.first == "disable:unsigned-overflow-check")
1931-
resetter.set_flag(enable_unsigned_overflow_check, false, d.first);
1932-
else if(d.first == "disable:pointer-overflow-check")
1933-
resetter.set_flag(enable_pointer_overflow_check, false, d.first);
1934-
else if(d.first == "disable:float-overflow-check")
1935-
resetter.set_flag(enable_float_overflow_check, false, d.first);
1936-
else if(d.first == "disable:conversion-check")
1937-
resetter.set_flag(enable_conversion_check, false, d.first);
1938-
else if(d.first == "disable:undefined-shift-check")
1939-
resetter.set_flag(enable_undefined_shift_check, false, d.first);
1940-
else if(d.first == "disable:nan-check")
1941-
resetter.set_flag(enable_nan_check, false, d.first);
1942-
else if(d.first == "disable:pointer-primitive-check")
1943-
resetter.set_flag(enable_pointer_primitive_check, false, d.first);
1944-
else if(d.first == "enable:bounds-check")
1945-
resetter.set_flag(enable_bounds_check, true, d.first);
1946-
else if(d.first == "enable:pointer-check")
1947-
resetter.set_flag(enable_pointer_check, true, d.first);
1948-
else if(d.first == "enable:memory_leak-check")
1949-
resetter.set_flag(enable_memory_leak_check, true, d.first);
1950-
else if(d.first == "enable:div-by-zero-check")
1951-
resetter.set_flag(enable_div_by_zero_check, true, d.first);
1952-
else if(d.first == "enable:enum-range-check")
1953-
resetter.set_flag(enable_enum_range_check, true, d.first);
1954-
else if(d.first == "enable:signed-overflow-check")
1955-
resetter.set_flag(enable_signed_overflow_check, true, d.first);
1956-
else if(d.first == "enable:unsigned-overflow-check")
1957-
resetter.set_flag(enable_unsigned_overflow_check, true, d.first);
1958-
else if(d.first == "enable:pointer-overflow-check")
1959-
resetter.set_flag(enable_pointer_overflow_check, true, d.first);
1960-
else if(d.first == "enable:float-overflow-check")
1961-
resetter.set_flag(enable_float_overflow_check, true, d.first);
1962-
else if(d.first == "enable:conversion-check")
1963-
resetter.set_flag(enable_conversion_check, true, d.first);
1964-
else if(d.first == "enable:undefined-shift-check")
1965-
resetter.set_flag(enable_undefined_shift_check, true, d.first);
1966-
else if(d.first == "enable:nan-check")
1967-
resetter.set_flag(enable_nan_check, true, d.first);
1968-
else if(d.first == "enable:pointer-primitive-check")
1969-
resetter.set_flag(enable_pointer_primitive_check, true, d.first);
1996+
// match named-check related pragmas
1997+
auto matched = match_named_check(d.first);
1998+
if(matched.has_value())
1999+
{
2000+
auto named_check = matched.value();
2001+
auto name = named_check.first;
2002+
auto status = named_check.second;
2003+
bool *flag = name_to_flag.find(name)->second;
2004+
switch(status)
2005+
{
2006+
case check_statust::ENABLE:
2007+
resetter.set_flag(*flag, true, name);
2008+
break;
2009+
case check_statust::DISABLE:
2010+
resetter.set_flag(*flag, false, name);
2011+
break;
2012+
case check_statust::CHECKED:
2013+
resetter.disable_flag(*flag, name);
2014+
break;
2015+
}
2016+
}
19702017
}
19712018

2019+
// add checked pragmas for all active checks
2020+
add_active_named_check_pragmas(i.source_location_nonconst());
2021+
19722022
new_code.clear();
19732023

19742024
// we clear all recorded assertions if
@@ -2398,3 +2448,46 @@ void goto_check_c(
23982448
const namespacet ns(goto_model.symbol_table);
23992449
goto_check_c(ns, options, goto_model.goto_functions, message_handler);
24002450
}
2451+
2452+
void goto_check_ct::add_active_named_check_pragmas(
2453+
source_locationt &source_location) const
2454+
{
2455+
for(const auto &entry : name_to_flag)
2456+
if(*(entry.second))
2457+
source_location.add_pragma("checked:" + id2string(entry.first));
2458+
}
2459+
2460+
void goto_check_ct::add_all_disable_named_check_pragmas(
2461+
source_locationt &source_location) const
2462+
{
2463+
for(const auto &entry : name_to_flag)
2464+
source_location.add_pragma("disable:" + id2string(entry.first));
2465+
}
2466+
2467+
goto_check_ct::named_check_statust
2468+
goto_check_ct::match_named_check(const irep_idt &named_check) const
2469+
{
2470+
auto s = id2string(named_check);
2471+
auto col = s.find(":");
2472+
2473+
if(col == std::string::npos)
2474+
return {}; // separator not found
2475+
2476+
auto name = s.substr(col + 1);
2477+
2478+
if(name_to_flag.find(name) == name_to_flag.end())
2479+
return {}; // name unknown
2480+
2481+
check_statust status;
2482+
if(!s.compare(0, 6, "enable"))
2483+
status = check_statust::ENABLE;
2484+
else if(!s.compare(0, 7, "disable"))
2485+
status = check_statust::DISABLE;
2486+
else if(!s.compare(0, 7, "checked"))
2487+
status = check_statust::CHECKED;
2488+
else
2489+
return {}; // prefix unknow
2490+
2491+
// success
2492+
return std::pair<irep_idt, check_statust>{name, status};
2493+
}

0 commit comments

Comments
 (0)