-
Notifications
You must be signed in to change notification settings - Fork 273
Non-deterministic initialisation of structs, pointers and globals in function call harness #4181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Non-deterministic initialisation of structs, pointers and globals in function call harness #4181
Conversation
NlightNFotis
commented
Feb 13, 2019
- Each commit message has a non-empty body, explaining why the change was made.
- Methods or procedures I have added are documented, following the guidelines provided in CODING_STANDARD.md.
- The feature or user visible behaviour I have added or modified has been documented in the User Guide in doc/cprover-manual/
- Regression or unit tests are included, or existing tests cover the modified code (in this case I have detailed which ones those are in the commit message).
- My commit message includes data points confirming performance improvements (if claimed).
- My PR is restricted to a single feature or bugfix.
- White-space or formatting changes outside the feature-related changed lines are in commits of their own.
Basically, this PR is intended to re-implement (and ultimately replace) the changes made in #3251, except in an external tool rather than within CBMC EDIT: The diff of the refactoring PR is kind of hopeless, mostly what's going on there is that we break up the generate function into multiple utility functions |
{ | ||
const auto &symbol = symbol_table_entry.second; | ||
if( | ||
symbol.is_static_lifetime && symbol.is_lvalue && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We think this means "global", but we weren't entirely sure. This is basically reverse engineered from a --show-symbol-table
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me.
0159b21
to
7eff3f5
Compare
/// exist already. | ||
symbol_exprt get_malloc_function(); | ||
|
||
void get_struct_tag_initializer( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these called get_*
if they don't return?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, we'll change it
exprt choice; | ||
if( | ||
depth > initialization_config.min_null_tree_depth && | ||
depth < initialization_config.max_nondet_tree_depth) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about wrapping these conditions in something a bit more descriptive?, e.g.: should_null_initialize(depth)
, where
bool should_null_initialize(size_t depth) { return depth >= initialization_config.min_null_tree_depth; }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll consider it, I think a function might be a bit overkill though. A local variable might be an improvement here though!
{ | ||
auto const value = require_exactly_one_value(option, values); | ||
p_impl->recursive_initialization_config.min_null_tree_depth = | ||
std::stoul(value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we have some facility to extract numbers from string safely via std::optional
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably do, we'll see how we can improve these
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I still think this would be a good idea but we can’t find it. There are the safe_
conversion functions, but those just assert that the conversion succeeds rather than returning an optionalt
. Writing new converters on the other hand seems a bit out of scope for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well stoul
is safe: throwing exceptions on failure. May want to catch those.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd use safe_string2size_t
here, unless you actually want to throw an exception.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tautschnig Considering this is user input I think throwing an exception here would be appropriate. I don't think asserting that user input is always correct is appropriate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xbauch @tautschnig I added #4192, which adds a more convenient way to convert strings to integers and handling failures locally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's great, indeed we should not be failing an assertion when reporting invalid user input.
recursive_initialization->initialize(lhs, 0, block); | ||
} | ||
|
||
void function_call_harness_generatort::validate_options() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this one called anywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is called by the harness generator factory
void function_call_harness_generatort::implt:: | ||
add_harness_function_to_goto_model(code_blockt function_body) | ||
{ | ||
const auto &function_to_call = symbol_table->lookup_ref(function); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use lookup_function_to_call
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good.
cac8026
to
365a914
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚫
This PR failed Diffblue compatibility checks (cbmc commit: cac8026).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/100932721
Status will be re-evaluated on next push.
Please contact @peterschrammel, @thk123, or @allredj for support.
Common spurious failures:
- the cbmc commit has disappeared in the mean time (e.g. in a force-push)
- the author is not in the list of contributors (e.g. first-time contributors).
The incompatibility may have been introduced by an earlier PR. In that case merging this
PR should be avoided unless it fixes the current incompatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
Passed Diffblue compatibility checks (cbmc commit: 365a914).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/100935342
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we actually get rid of goto-instrument/nondet_static.cpp
with this code? I think that code might be a bit cleaner when it comes to choosing what (not) to initialise, but isn't very useful when it comes to objects with pointers and such.
{ | ||
const auto &symbol = symbol_table_entry.second; | ||
if( | ||
symbol.is_static_lifetime && symbol.is_lvalue && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, though you may want to check Michael's comment about using safe_string2size_t
allocate_objects.allocate_automatic_local_object(type.subtype(), "pointee"); | ||
allocate_objects.declare_created_symbols(body); | ||
body.add(code_assignt{lhs, null_pointer_exprt{type}}); | ||
if(depth < initialization_config.max_nondet_tree_depth) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changes the behavior compared to the existing c_nondet_symbol_factory()
. Previously we would only stop initializing when a recursion on a struct was reached (i.e., on a pointer chain the same struct appeared a second time) and the depth was greater or equal to max_nondet_tree_depth
. This was done via recording the struct tags in the object recursion_set
. All pointer dereferences would increase the depth but only when we see a recursion we would stop initializing. Would it be possible to replicate this behavior here? The intention was to be consistent with how the initialization is done in the Java object factory (to facilitate future refactorings and code sharing between the two).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah right, I figured there was something we forgot! Good point, we will implement this.
|
||
code_function_callt::operandst arguments{}; | ||
arguments.reserve(parameters.size()); | ||
void function_call_harness_generatort::implt::generate_nondet_globals( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's also --nondet-static
(using nondet_static()
) in cbmc and goto-instrument. I'm not sure how exactly it compares to this functionality but maybe the two could be merged at some point.
Important refactoring to make implementation of later features easier. Co-authored-by: Fotis Koutoulakis <[email protected]> Co-authored-by: Hannes Steffenhagen <[email protected]>
Add ability to non-deterministically initialise globals in the function call harness generator. Co-authored-by: Fotis Koutoulakis <[email protected]> Co-authored-by: Hannes Steffenhagen <[email protected]>
Recursively initialise non-deterministically struct components and pointers. Co-authored-by: Fotis Koutoulakis <[email protected]> Co-authored-by: Hannes Steffenhagen <[email protected]>
Now the depth limit only applies to structs if we have seen this struct before (i.e. the initialisation chain is recursive)
365a914
to
550e6ab
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
Passed Diffblue compatibility checks (cbmc commit: 550e6ab).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/101132607