8000 Add an optional extra level of checking: ASSUME(...) - an opt-in side-effect safe assert(...) by practicalswift · Pull Request #16136 · bitcoin/bitcoin · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add an optional extra level of checking: ASSUME(...) - an opt-in side-effect safe assert(...) #16136

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

Closed
wants to merge 1 commit into from

Conversation

practicalswift
Copy link
Contributor
@practicalswift practicalswift commented Jun 2, 2019

As suggested by sipa in the open issue #4576 (2014) -- "[discussion] Dealing with assertions and optional consistency checking" -- as an alternative to assert(…) in situations where assert(…) is not appropriate:


What I want is more of these checks, more as a way for the programmer to say "this is what I assume here", more than "if this doesn't hold here, we're in BIG trouble". It makes the code clearer, and simultaneously verifies that such assumptions hold. But only in cases where we're not at risk of hurting the network by dying.

ASSUME(expression) works like this:

  • If compiled with -DABORT_ON_FAILED_ASSUME (set by --enable-debug and/or --enable-fuzz): Evaluate expression and abort if expression is false.
  • If compiled without -DABORT_ON_FAILED_ASSUME: Evaluate expression and continue execution.

Example:

int main(void) {
    ASSUME(IsFoo());
     ...
}

If !IsFoo() and -DABORT_ON_FAILED_ASSUME, then:

    filename.cpp:123: main: ASSUME(IsFoo()) failed.
    Aborted

Otherwise the execution continues.

Resolves #4576.

@ajtowns
Copy link
Contributor
ajtowns commented Jun 3, 2019

If you're running the CHECK no matter what, could consider making the failure configurable at runtime (could default to not abort on mainnet, but to abort on testnet/regtest maybe?). Something like:

do {
     if (g_abort_on_check_fail) {
          fprintf(stderr, ...); std::abort();
     } else {
          LogPrintf(...);
     }
} while(0)

@practicalswift
Copy link
Contributor Author

@ajtowns I like the idea of making the default depend on the network. I'll let others chime in regarding how to choose the defaults and how to override it and then update based on the feedback :-)

@sdaftuar
Copy link
Member
sdaftuar commented Jun 6, 2019

Concept ACK -- I often want something that's like an assert but wouldn't cause cascading failure on the network if I'm wrong.

@morcos
Copy link
Contributor
morcos commented Jun 7, 2019

Concept ACK, but I also really like the idea of LogPrinting these (either no matter what or at least if some level of debug logging is on). Part of the benefit would be in catching rare conditions that might not occur in regular testing but would show up occasionally among thousands of real world users. The LogPrint could direct the user to report the error on GitHub.

@maflcko
Copy link
Member
maflcko commented Jun 7, 2019

The link could even prefill all the additional debug information. E.g. https://github.com/bitcoin/bitcoin/issues/new?title=file_name.cpp:LINE_NR%20CHECK%20failed%20bla&body=more%20bla

@Empact
Copy link
Contributor
Empact commented Jun 10, 2019

Concept ACK

Copy link
Member
@maflcko maflcko left a comment

Choose a reason for hiding this comment

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

Concept ACK if done right (with proper examples and documentation), but currently it seems to be confusing assert with CHECK.

And maybe it should be called ASSUME?

@ryanofsky
Copy link
Contributor

This change seems good, but I'd suggest calling the macro DCHECK instead of CHECK because:

  • It would suggest that the macro has different behavior in debug vs release builds.
  • It would allow us to create a corresponding CHECK macro in the future to replace our current non-standard usages of assert (in standard c++, asserts are compiled out of release builds, but not in bitcoin c++).
  • It would be more consistent with other support libraries (glog, folly) which use CHECK to abort unconditionally and DCHECK to only abort in debug builds.

For reference:

Condition is compiled Condition is evaluated Program is aborted
C++ standard assert if NDEBUG is unset if NDEBUG is unset if NDEBUG is unset
Bitcoin assert always always always
glog CHECK always always always
glog DCHECK always if NDEBUG is unset if NDEBUG is unset
folly XCHECK always always always
folly XDCHECK if NDEBUG is unset if NDEBUG is unset if NDEBUG is unset

Re: #16136 (comment) and #16136 (comment) I think what Marco and Alex are asking for is useful but out of scope for an assert or check macro. I think asserts and checks are best for succinctly conveying assumptions made by the code author that in no cases should ever be violated, and conveying at a very coarse level how crucial those assumptions are (abort-worthy or not). The main point of asserts and checks should be to make code more readable and communicate how to think about it.

Once you're in the realm of conditions that you think should never happen, but are more complicated and might end up happening anyway, you are really better off writing an actual log print with text that would put the error into context. You may also want to abort in these cases, which is why logging frameworks support FATAL and DFATAL severity levels.

@practicalswift practicalswift changed the title Add an optional extra level of checking: CHECK(...) - an opt-in side-effect safe assert(...) Add an optional extra level of checking: DCHECK(...) - an opt-in side-effect safe assert(...) Jun 13, 2019
@practicalswift practicalswift force-pushed the check branch 2 times, most recently from 02fd0cc to a32e448 Compare June 13, 2019 20:43
@maflcko
Copy link
Member
maflcko commented Oct 10, 2019

Why was this closed?

@practicalswift
Copy link
Contributor Author

@MarcoFalke I try to limit the number of PR:s I have opened and it seemed like this had a low probability of being merged. Seems I was wrong: now re-opened :)

@practicalswift practicalswift changed the title Add an optional extra level of checking: DCHECK(...) - an opt-in side-effect safe assert(...) Add an optional extra level of checking: CHECK(...) - an opt-in side-effect safe assert(...) Oct 10, 2019
@practicalswift
Copy link
Contributor Author

Rebased and renamed back from DCHECK to the originally suggested name CHECK (as first suggested by @sipa in #4576).

I find DCHECK somewhat unintuitive: I prefer CHECK or ASSUME. Let's bikeshed! :)

@ryanofsky
Copy link
Contributor

Rebased and renamed back from DCHECK to the originally suggested name CHECK (as first suggested by @sipa in #4576).

I find DCHECK somewhat unintuitive: I prefer CHECK or ASSUME. Let's bikeshed! :)

Not interested in bikeshedding and this is fine if other reviewers want it, but I'll just point out that as of b1eba55 this PR makes assert and CHECK mean the exact opposite things they mean in other C++ codebases, where CHECK aborts in all builds and assert aborts in debug builds only (examples in table above #16136 (comment)).

A good way to provide more clarity, I think, would be to follow convention used in other libraries and add a pair of CHECK/DCHECK macros instead of a single macro, avoiding use of any assert. An advantage of doing this is that both macros could do extra things like log to debug.log or capture stack traces before aborting, which aren't so easily possible with assert.

@practicalswift
Copy link
Contributor Author

@ryanofsky What about ASSUME? :)

@practicalswift practicalswift changed the title Add an optional extra level of checking: CHECK(...) - an opt-in side-effect safe assert(...) Add an optional extra level of checking: ASSUME(...) - an opt-in side-effect safe assert(...) Oct 24, 2019
Copy link
Contributor
@ajtowns ajtowns left a comment

Choose a reason for hiding this comment

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

I like this, but am not sure it makes sense :)

6D40
src/assume.h Outdated
// Bitcoin Core is always compiled with assertions enabled. assert(...)
// is thus only appropriate for documenting critical assumptions
// where a failed assumption should lead to immediate abortion also
// in production environments.
Copy link
Contributor

Choose a reason for hiding this comment

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

It's not really clear to me when you should choose assert vs ASSUME -- neither is any quicker because both always run the test, and if the assumptions you had when writing the code were wrong, how confident can you really be that continuing won't cause horrible corruption to decide that continuing is okay?

@maflcko
Copy link
Member
maflcko commented Oct 28, 2019

Should be moved to the existing util/check.h

@practicalswift practicalswift force-pushed the check branch 2 times, most recently from e006452 to 2812b0d Compare October 28, 2019 23:04
@practicalswift
Copy link
Contributor Author

@MarcoFalke Good idea: addressed! Please re-review.

@maflcko
Copy link
Member
maflcko commented Oct 29, 2019

Would it make sense to add a wrapper around our current assert, which always aborts when false (even in production, with debug disabled)?

We have vague error reports like #17298, that obviously lack details in the debug log, because logging wasn't enabled. Maybe an wrapper around our current assert that still aborts the program, but also dumps a full -debug=1 log for the last n (lets say 10 or 100) lines of debug log?

@practicalswift
Copy link
Contributor Author

@MarcoFalke That could be nifty to have. I would like to keep this PR and ASSUME(…) as simple as possible, so I'll leave that for a future PR.

My suggestion is that we start with something super simple in and then we can iterate from that as we gain experience with the different use cases :)

@maflcko
Copy link
Member
maflcko commented Oct 29, 2019

@MarcoFalke That could be nifty to have. I would like to keep this PR and ASSUME(…) as simple as possible, so I'll leave that for a future PR.

Fine, but at the very least we should decide on naming. I guess, given that this patch does not use the CHECK/DCHECK naming, your proposed naming would be ASSERT/ASSUME?

@practicalswift
Copy link
Contributor Author
practicalswift commented Oct 29, 2019

@MarcoFalke I think ASSERT and ASSUME are fine, but I'm happy to change that to whatever the consensus opinion is :)

I've encountered a few places where ASSUME would be handy when fuzzing: places where I want the program to abort when running under a fuzzer but where the error condition is not severe enough to warrant aborting in production. So for me getting the macro in is the important thing -- I can live with any name :)

@maflcko
Copy link
Member
maflcko commented Oct 30, 2019

ACK 674f9d5, didn't look at the configure changes

@practicalswift
Copy link
Contributor Author

Closing due to lack of interest :)

@ajtowns
Copy link
Contributor
ajtowns commented Mar 12, 2020

We have CHECK_NONFATAL(cond) in util/check.h now, which is always compiled, always evaluated, but only throws an exception rather than aborting the program. Probably best to see how that works for a while

@maflcko
Copy link
Member
maflcko commented Mar 12, 2020

I think ASSUME serves a different use case than CHECK_NONFATAL, but it might be best to introduce it when it is actually used in the code.

@maflcko
Copy link
Member
maflcko commented Oct 12, 2020

Revived in #20138 with an actual use-case (I hope)

@hebasto
Copy link
Member
hebasto commented Oct 30, 2020

Revived in #20138 with an actual use-case (I hope)

Actually, #20255 :)

@practicalswift practicalswift deleted the check branch April 10, 2021 19:39
@bitcoin bitcoin locked and limited conversation to collaborators Aug 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[discussion] Dealing with assertions and optional consistency checking
9 participants
0