Skip to content

Cleanup error handling of cbmc/ folder #2703

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

Merged

Conversation

NlightNFotis
Copy link
Contributor

Perform various cleanups of the error handling mechanisms in the src/cbmc/ folder. These include turning asserts into INVARIANTs and PRECONDITIONs as well as a new custom exception class.

Copy link
Collaborator

@martin-cs martin-cs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve subject to addressing the comments.


// identify the file name, inlined functions aren't properly
// accounted for
goto_programt::const_targett end_function=
--gf_it->second.body.instructions.end();
assert(end_function->is_end_function());
INVARIANT(end_function->is_end_function(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DATA_INVARIANT I think.

file_name=end_function->source_location.get_file();
assert(!file_name.empty());
INVARIANT(!file_name.empty(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DATA_INVARIANT

catch(invalid_user_input_exceptiont &e)
{
std::cerr << e.what() << "\n";
return CPROVER_EXIT_EXCEPTION;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPROVER_EXIT_USAGE_ERROR ?

@@ -241,7 +241,8 @@ bool bmc_covert::operator()()
cover_goals.add(l);
}

assert(cover_goals.size()==goal_map.size());
INVARIANT(cover_goals.size()==goal_map.size(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apply clang-format


int res=parse_options.main();
int res = parse_options.main();

#ifdef IREP_HASH_STATS
std::cout << "IREP_HASH_CNT=" << irep_hash_cnt << '\n';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation

std::string what() const noexcept
{
std::string res;
res += "\nInvalid User Input Exception\n";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd remove the word Exception.

error() << "sorry, this solver does not support beautification" << eom;
throw 0;
throw invalid_user_input_exceptiont(
"Sorry, this solver does not support beautification",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this solver -> the chosen solver ? (same below)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And drop "sorry" - we don't apologise elsewhere either.

}
catch(invalid_user_input_exceptiont &e)
{
std::cerr << e.what() << "\n";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. This must go into a message stream.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was like this in the original design, but then I saw during rebasing on develop, that recently a similar change was eliminated, so I thought that it must have been that this is more appropriate. If I am also to do that now, I will need to add a messaget into parse_options_baset, which will require changes in various places

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found exactly what I was referring to. So parse_options_baset doesn't seem to inherit from messaget and doesn't contain a reference to a messaget. If I do this, then I get loads of compilation errors regarding ambiguous calls to things like status(), get_message_handler(), etc.

It's also consistent with the rest of the file. parse_options_baset::usage_error(), parse_options_baset::unknown_options_msg() seem to be following the same pattern of using std::cerr

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the risk of contradicting @peterschrammel: I don't think a message stream can be used here, because setting up the desired one requires successful parsing of command-line options (picking, e.g., JSON output with a verbosity of 5). Hence I'm with @NlightNFotis that using std::cerr is correct here.

@@ -41,11 +41,9 @@ int wmain(int argc, const wchar_t **argv_wide)
int main(int argc, const char **argv)
{
#endif
try
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please squash this with the first commit that introduce the change that you undo here.

Copy link
Collaborator

@tautschnig tautschnig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a good direction of work to me, but I'd suggest to make this as clean as possible unless there is a need to rush it in? If so, please break it apart into separate pull requests as the need for further discussion varies widely among the commits.

@@ -241,7 +241,8 @@ bool bmc_covert::operator()()
cover_goals.add(l);
}

assert(cover_goals.size()==goal_map.size());
INVARIANT(cover_goals.size()==goal_map.size(),
"We add coverage for each goal");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The coding standard says that error messages start with a lower-case letter.

assert(cov.second.num_executions>0);
INVARIANT(cov.second.num_executions>0,
// FIXME I don't know what this means
"Number of executions most be positive");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be a PRECONDITION. A coverage entry can only exist when a location has actually been covered in at least one execution.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a problem with that, in that the PRECONDITION according to documentation should only contain unmodified arguments to a function, but I can change it, since it makes it easier to drop the message which appears incorrect as well.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might use a DATA_INVARIANT, paraphrasing in the message what I said above (coverage entries can only exist with at least one execution).

catch(invalid_user_input_exceptiont &e)
{
std::cerr << e.what() << std::endl;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Design note: I don't think such an exception should propagate out of parse_options for the key task of parse_options is to parse options? If anything goes wrong it should and does return an error code. Having both error codes and exceptions seems wrong to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a comment on outdated code. The error handling is now being contained in parse_optionst::main(), and doesn't escape that context. parse_optionst::main() will indeed return an error code, that is is being handled in cbmc_main

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, it also appears that parse_optionst was mainly useful during the evolution of the code, but now that has been rebased, its usefulness has diminished, so I am trying to eliminate it.

{
}

std::string what() const noexcept
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be a header-only class. Add a cpp file and move the implementation of at least this function over there. No need to inline it everywhere, will just unnecessarily increase the size of the binary.

@@ -235,20 +239,38 @@ std::unique_ptr<cbmc_solverst::solvert> cbmc_solverst::get_smt2(

void cbmc_solverst::no_beautification()
{
if(options.get_bool_option("beautify"))
bool beautify = options.get_bool_option("beautify");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this temporary introduced?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember I had a reason in the beginning but I may have refactored it enough that it's now unneeded.

error() << "sorry, this solver does not support beautification" << eom;
throw 0;
throw invalid_user_input_exceptiont(
"Sorry, this solver does not support beautification",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And drop "sorry" - we don't apologise elsewhere either.

if(options.get_bool_option("all-properties") ||
options.get_option("cover")!="" ||
options.get_option("incremental-check")!="")
bool all_properties = options.get_bool_option("all-properties");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const (here and below)

options.get_option("cover")!="" ||
options.get_option("incremental-check")!="")
bool all_properties = options.get_bool_option("all-properties");
bool cover = options.get_option("cover") != "";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you're cleaning this up: use .is_set instead of get_option(...) != ""

@@ -35,6 +35,17 @@ class parse_options_baset
bool parse_result;
};

class parse_optionst : public parse_options_baset
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we need this derived class: can't it just be changed in parse_options_baset and make everyone benefit without changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed anymore. I have eliminated it.

res += "Option: " + option + "\n";
res += "Reason: " + reason;
// Print an optional correct usage message assuming correct input parameters have been passed
res += correct_input.empty() ? "\n" : " Try: " + correct_input + "\n";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Try: ..." portion should better be on a new line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tautschnig Suggested to drop the Try altogether, and just add in the various constructor callsites so that it reads more like a sentence.

@NlightNFotis NlightNFotis force-pushed the develop_cbmc_cleanup_rebase branch 2 times, most recently from 3c9225c to f06cbea Compare August 24, 2018 10:39
@NlightNFotis
Copy link
Contributor Author

@peterschrammel You have a red (blocker) cross applied to this. All your points have now been addressed. Can you take a look now and tell me if there's anything more you would want explained or changed?

@NlightNFotis
Copy link
Contributor Author

@tautschnig Hi, can you also take a look to see if it's closer to what you expect? It's the same as before, but cleaned up and the changes suggested applied. The only extra is the last commit, which I thought makes sense to not be squashed, as it is a good atomic change (it replaces to_integer with numeric_cast).

@@ -41,9 +41,11 @@ int wmain(int argc, const wchar_t **argv_wide)
int main(int argc, const char **argv)
{
#endif
cbmc_parse_optionst parse_options(argc, argv);
try
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is undone two commits later. Why is this change made at all?


#include "exception_utils.h"

std::string invalid_user_input_exceptiont::what() const noexcept
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, there should also be a version hat can serialise into json and XML in a more structure way (in a future PR).

}
DATA_INVARIANT(
expr.operands().size() != 4,
"waitfor expression is expected to have four operands");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chrisr-diffblue, @martin-cs, the source location is lost here. Would it be possible to have variants of the INVARIANT macros that take a source location explicitly so that it can be output in a structured way?

@peterschrammel
Copy link
Member

@NlightNFotis, the changes in f257667 that are later undone in 017c1b0 need to be removed.

@tautschnig
Copy link
Collaborator

Could this one please be rebased and cleaned up with regard to the comments raised by @peterschrammel - then it might be good to go.

@NlightNFotis
Copy link
Contributor Author

@tautschnig Yes, this is waiting for us to decide on a clean way to create a more structured, "variadic" invariant, that can take a source_locationt and anything else we might want to decide to pass to it in the future - it's the last comment Peter made on his last review.

@tautschnig
Copy link
Collaborator

Yes, this is waiting for us to decide on a clean way to create a more structured, "variadic" invariant, that can take a source_locationt and anything else we might want to decide to pass to it in the future - it's the last comment Peter made on his last review.

Apart from the fact that the specific bit of code that @peterschrammel commented on is now gone from the repository: can't we just solve this by doing source_location.as_string()? I'm sure there are more beautiful ways of doing this, but perfection is the enemy of good. And here such decision-taking might just be blocking progress?

@NlightNFotis NlightNFotis force-pushed the develop_cbmc_cleanup_rebase branch from f06cbea to 3bd4322 Compare September 4, 2018 10:51
@NlightNFotis
Copy link
Contributor Author

@tautschnig I have now rebased the PR and I'm waiting for CI to pass. Then it should be good to go. We decided to go ahead with this now and we can implement the structured invariants later.

Copy link
Collaborator

@tautschnig tautschnig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are still changes in the individual commits that just modify what was added in one of the other commits in this series. Some soft reset and the use of git add -p seems to be due.

@@ -68,13 +68,13 @@ class cbmc_solverst:public messaget

prop_convt &prop_conv() const
{
assert(prop_conv_ptr!=nullptr);
PRECONDITION(prop_conv_ptr!=nullptr);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit pick: clang-format.

Copy link
Contributor

@allredj allredj left a 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: 3bd4322).
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).

@NlightNFotis NlightNFotis force-pushed the develop_cbmc_cleanup_rebase branch from 3bd4322 to 2e6d6a0 Compare September 4, 2018 13:45
@NlightNFotis
Copy link
Contributor Author

@tautschnig I have implemented all of the changes suggested in your comments, and Peter's comments (bar the structured invariant). I have also pinged Peter for a re-review. Can you take a look at it and green-light it if you think it's good to go?

return *prop_conv_ptr;
}

propt &prop() const
{
PRECONDITION(prop_ptr!=nullptr);
PRECONDITION(prop_ptr != nullptr);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still in the wrong commit, fixing up a change from an earlier commit.

@@ -43,7 +43,7 @@ int main(int argc, const char **argv)
#endif
cbmc_parse_optionst parse_options(argc, argv);

int res=parse_options.main();
int res = parse_options.main();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary change?

std::string res;
res += "\nInvalid User Input\n";
res += "Option: " + option + "\n";
res += "Reason: " + reason;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wishlist: please add a test for this, i.e., intentionally use CBMC with incorrect options.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need this as part of this PR, or can it go in as part of a follow up PR for example?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up to you, as long as it happens at some point :-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We plan to make some further improvements to user error handling, so we'll definitely be adding suitable test cases as part of that work, so I'd be happy for this PR to go in, with tests following as part of the later improvements.

@NlightNFotis NlightNFotis merged commit b01d603 into diffblue:develop Sep 4, 2018
Copy link
Contributor

@allredj allredj left a 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: 2e6d6a0).
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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants