-
Notifications
You must be signed in to change notification settings - Fork 284
Add a structured verification results class in the C++ API #7538
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
Add a structured verification results class in the C++ API #7538
Conversation
18a949c to
d047179
Compare
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## develop #7538 +/- ##
===========================================
- Coverage 78.51% 78.37% -0.15%
===========================================
Files 1671 1674 +3
Lines 191848 191938 +90
===========================================
- Hits 150621 150422 -199
- Misses 41227 41516 +289
... and 23 files with indirect coverage changes Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report in Codecov by Sentry. |
0590884 to
397531d
Compare
martin-cs
left a comment
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 hate being That Guy but...
How does this fit with the goto_verifier / properties interface?
https://github.com/diffblue/cbmc/blob/develop/src/goto-checker/goto_verifier.h
https://github.com/diffblue/cbmc/blob/develop/src/goto-checker/properties.h
I know @peterschrammel did some work on setting this up as the future of "unified verification results". I did some work on getting the abstract interpreter to fit with it:
https://github.com/diffblue/cbmc/blob/develop/src/goto-analyzer/static_verifier.h
but it needs more work.
|
Hi Martin, @martin-cs The motivation for this is to have an interface from which the API clients will be able to get the results of a run from the verification engine and inspect them.
I need to sort out CI here first and then I will clean up the commit history and also provide some review guidance in the description above as to what changes were made and why. Update: I have crossed out the part of the text that contains some invalid assumptions of mine after some discussion with Peter. The code will need some rework, so as it stands right now it doesn't reflect the final design. |
|
Update: I had a discussion with @peterschrammel offline about parts of the design and it contains some defects that I was oblivious too, which renders my previous comment invalid. This is a change only for I will rework the API to move the |
|
Fixes #7499 |
2f63c98 to
a79cff4
Compare
a79cff4 to
bb88d95
Compare
thomasspriggs
left a comment
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.
Some initial comments rather than a complete review, given that this likely needs substantial work to deal with this comment -
- 🚫 API headers must not include internal headers, because this exposes implementation details as part of the public API.
src/libcprover-cpp/api.cpp
Outdated
| return; | ||
| // TODO: Make more robust. | ||
| // return; | ||
| INVARIANT(false, "process_goto_program failed"); |
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 think return; on failure was already the correct behaviour, as errors are supposed to be reported via the log variable.
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.
🤨 You gave this a thumbs up but haven't actioned it?
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.
Using INVARIANT here is actually a really bad thing in terms of an API. This is because if the INVARIANT fails it will either abort the whole process which is undesirable if the consumer on the API can recover or it will throw an exception which may not be supported by the ABI being used to interface with the API.
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 no longer relevant, as marked outdated by the GitHub UI itself.
This change has been rolled back in b9b6cc4 - b9b6cc4#diff-122d9d2e65523b48e18239983a025b547f9509d43e638fea6acc71a926daed69R155
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 still needs fixing in the produce_results function. This comment was in the verify_model function but the code is duplicated, so the fix needs doing in two places.
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 catch - my bad. I did a pattern replacement to avoid this issue, but for a strange kind of fashion this was missed.
| /// @brief Copy constructor for the goto_trace_storaget class. | ||
| /// \warning Be advised that goto_traces can be large objects, so | ||
| /// copying is not recommended, unless you know what you're doing. | ||
| goto_trace_storaget(const goto_trace_storaget &) = default; |
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 am not sure this is a good idea as it could lead to accidental copy construction of goto_trace_storaget which can be an extremely large data structure. Think multiple gigabytes...
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.
See if I can change the class that manages this to be holding a unique pointer which can then be moved.
src/libcprover-cpp/api.cpp
Outdated
| implementation->message_handler = | ||
| util_make_unique<null_message_handlert>(null_message_handlert{}); | ||
| implementation->options = options.to_engine_options(); | ||
| implementation->options->set_option("trace", true); |
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.
⛏️ It might be worth adding a comment above this that all_properties_verifier_with_trace_storaget doesn't store traces without this.
🤔 Ultimately, all_properties_verifier_with_trace_storaget is a pretty small class which doesn't fit the needs of the API very well. So we are most likely better off writing our own version of it.
| return options; | ||
| } | ||
|
|
||
| verification_resultt run_verification_engine(goto_modelt &model) |
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 doesn't use this the API function to test?
| verifier.report(); | ||
| } | ||
|
|
||
| // TODO: This is a temporary function - it's basically `verify_model`, tweaked |
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 prefer documentation in the .h file because this is where the interface is defined.
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 an actual TODO comment rather than documentation. It is explaining the need for clean-up rather than documenting what the function is supposed to be for. Therefore I would argue that this information belongs in the .cpp rather the header. There is a separate argument that there should be a doxygen block in the header which deprecates this function, explains the difference from the The is already a doxygen block in the header.verify_model function and that the verify_model function should be used in preference.
unit/libcprover-cpp/CMakeLists.txt
Outdated
| target_include_directories(lib-unit | ||
| PUBLIC | ||
| ${CBMC_SOURCE_DIR}/libcprover-cpp | ||
| ${CBMC_SOURCE_DIR}/unit/testing-utils |
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.
See if it is to be removed - we don't want to expose symbols from the libcproverunit binary.
bd6f91c to
dad5fc7
Compare
5acbb39 to
b9b6cc4
Compare
| class dstringt; | ||
| struct property_infot; | ||
| using propertiest = std::map<dstringt, property_infot>; | ||
| enum class resultt; |
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.
⛏️ Seems like quite a generic symbol to introduce into the public global namespace.
| verifier.report(); | ||
| } | ||
|
|
||
| // TODO: This is a temporary function - it's basically `verify_model`, tweaked |
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 an actual TODO comment rather than documentation. It is explaining the need for clean-up rather than documenting what the function is supposed to be for. Therefore I would argue that this information belongs in the .cpp rather the header. There is a separate argument that there should be a doxygen block in the header which deprecates this function, explains the difference from the The is already a doxygen block in the header.verify_model function and that the verify_model function should be used in preference.
src/libcprover-cpp/api.cpp
Outdated
| return; | ||
| // TODO: Make more robust. | ||
| // return; | ||
| INVARIANT(false, "process_goto_program failed"); |
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.
🤨 You gave this a thumbs up but haven't actioned it?
|
|
||
| #include "bmc_util.h" | ||
| #include "goto_trace_storage.h" | ||
| #include "goto_verifier.h" |
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 reformatting appears to be the result of adding and removing things in the process of work on this PR. If you removed all the changes to this file, then you would not need as many code-owners to approve...
thomasspriggs
left a comment
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 think we should fix the inclusion of private headers in the API unit test before merging as a continuation of the fix to avoid including private headers in the public API headers.
| catch | ||
| goto-programs | ||
| langapi | ||
| libcprover-cpp |
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 think the functionality related to testing src/libcprover-cpp should live in unit/libcprover-cpp not unit/testing-utils. This is particularly the case with the API, as it is desirable to be able to unit test the API without linking the API unit test binary to the rest of the code base on a #include level.
| auto dstringt_property = dstringt(property_id); | ||
| switch(_impl->get_properties().at(dstringt_property).status) | ||
| { | ||
| case property_statust::ERROR: |
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 suggest making the error case the default case. This will avoid compile errors about reaching the end of non-void functions.
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 prefer to keep the UNREACHABLE - this is to communicate intent about this being an exhaustive list.
It also guards against a new enum member being added but not being matched against properly, as it will fail fast and loud.
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.
As I'd previously pointed out in one of my other comments on this PR, the standard INVARIANT / UNREACHABLE macros don't work in a useful fashion in this context. Therefore an alternative means of reporting the error is required. I suggest using a comment if you feel the need to explain the the default case should be unreachable and is therefore an error.
833c276 to
c3b1c4d
Compare
| { | ||
| } | ||
|
|
||
| verification_resultt::~verification_resultt() |
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 should be = default.
| #include <string> | ||
| #include <vector> | ||
|
|
||
| class verification_resultt::verification_result_implt |
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 think you can just make this class a struct, delete all the member functions and just use the fields directly. It would be far more straight-forward.
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 rather keep the API here as it stands - to be a bit more defensive in case tighter access control (say, some form of translation of the types, or validation) is required in the future (which can be implemented transparently through the property methods).
If that ends up not being needed, the accessor methods should be able to be removed with little effort.
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 disagree with your reasoning. "impl" means implementation detail; it is in the name. It shouldn't be intended to be part of the exposed API. Therefore the using a class to add accessibility restrictions to something which is already private by virtue of being declared in the .cpp only is overkill.
This is not a blocking comment, just an observation. So it can be left as-is for the moment if you prefer.
75392a9 to
7c82b84
Compare
7c82b84 to
35bf812
Compare
One-sentence Description
This PR is adding the capacity to the C++ API to provide the end-results of a verification engine run to a client in a structured format.
Review Guidance
libcprover-cpp/verification_result.hfile, along with the associated infrastructure inall_properties_verifier_with_trace_storagetclass (moving the traces from the verifier at the end of the verification engine run).src/goto-checker/, but offline discussion with @peterschrammel identified that as a potentially disruptive change. It was then decided to move this closer to more experimental code in the C++ api, rather than keep it close to the solvers.testing-utils/run_verification_engine.h- it clones the functionality of the C++ APIverify_model()function with the aim to quickly do a verification engine run in a convenient package and produce back the verification results that we expect from the engine.Checklist