diff mbox series

[PATCHv4,02/16] core: enable 'NDEBUG' unless BR2_ENABLE_RUNTIME_DEBUG is set

Message ID 20210601143422.25064-3-patrickdepinguin@gmail.com
State Accepted
Headers show
Series Introduce BR2_ENABLE_RUNTIME_DEBUG | expand

Commit Message

Thomas De Schampheleire June 1, 2021, 2:34 p.m. UTC
From: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>

The 'assert' statement in glibc honors the 'NDEBUG' preprocessor macro: if
it is set, then the assert statement is compiled away.

Define this 'NDEBUG' macro when BR2_ENABLE_RUNTIME_DEBUG is disabled (the
default case).

Signed-off-by: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
---
v4: reintroduced patch on suggestion of Arnout. I had previously dropped it from
the series based on an incorrect conclusion.


 package/Makefile.in | 3 +++
 1 file changed, 3 insertions(+)

Comments

Yann E. MORIN June 1, 2021, 8:36 p.m. UTC | #1
Thomas, All,

On 2021-06-01 16:34 +0200, Thomas De Schampheleire spake thusly:
> From: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
> 
> The 'assert' statement in glibc honors the 'NDEBUG' preprocessor macro: if
> it is set, then the assert statement is compiled away.
> 
> Define this 'NDEBUG' macro when BR2_ENABLE_RUNTIME_DEBUG is disabled (the
> default case).
> 
> Signed-off-by: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>

Reviewed-by: Yann E. MORIN <yann.morin.1998@free.fr>

However, see a little nit, below...

> ---
> v4: reintroduced patch on suggestion of Arnout. I had previously dropped it from
> the series based on an incorrect conclusion.
> 
> 
>  package/Makefile.in | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/package/Makefile.in b/package/Makefile.in
> index 86db62ba5b..955e6a8e8c 100644
> --- a/package/Makefile.in
> +++ b/package/Makefile.in
> @@ -148,6 +148,9 @@ endif
>  ifeq ($(BR2_DEBUG_3),y)
>  TARGET_DEBUGGING = -g3

Here, we have a simple assignment in a conditional block, while...

>  endif
> +ifeq ($(BR2_ENABLE_RUNTIME_DEBUG),)
> +TARGET_DEBUGGING += -DNDEBUG

... here we have an append-assignment in a conditional.

In this case the order ensures it works, but this is inconsistent and
prone to errors should we need to expand TARGET_DEBUGGING futher...

Regards,
Yann E. MORIN.

> +endif
>  
>  TARGET_LDFLAGS = $(call qstrip,$(BR2_TARGET_LDFLAGS))
>  
> -- 
> 2.26.3
> 
> _______________________________________________
> buildroot mailing list
> buildroot@busybox.net
> http://lists.busybox.net/mailman/listinfo/buildroot
D. Olsson June 5, 2021, 4:50 p.m. UTC | #2
Hi Thomas, Yann, all,

> On 2021-06-01 16:34 +0200, Thomas De Schampheleire spake thusly:
> +ifeq ($(BR2_ENABLE_RUNTIME_DEBUG),)
> +TARGET_DEBUGGING += -DNDEBUG

This broke bitcoin builds. I haven't looked deeply into the
details, but it is my understanding that NDEBUG changes the
behaviour of assert() in a way that Bitcoin explicitly does
not support.

See: https://github.com/bitcoin/bitcoin/blob/707ba8692b0013f4824dc3c2ea6554ccad5d20c2/src/compat/assumptions.h#L13

I don't know if there is a workaround for this, but I have a
feeling that passing NDEBUG for all packages might not be a
good idea?


---
D. Olsson
PGP: 8204A8CD
Thomas De Schampheleire June 5, 2021, 5:11 p.m. UTC | #3
Hello,



On Sat, Jun 5, 2021, 18:50 D. Olsson <hi@senzilla.io> wrote:

> Hi Thomas, Yann, all,
>
> > On 2021-06-01 16:34 +0200, Thomas De Schampheleire spake thusly:
> > +ifeq ($(BR2_ENABLE_RUNTIME_DEBUG),)
> > +TARGET_DEBUGGING += -DNDEBUG
>
> This broke bitcoin builds. I haven't looked deeply into the
> details, but it is my understanding that NDEBUG changes the
> behaviour of assert() in a way that Bitcoin explicitly does
> not support.
>
> See:
> https://github.com/bitcoin/bitcoin/blob/707ba8692b0013f4824dc3c2ea6554ccad5d20c2/src/compat/assumptions.h#L13
>
> I don't know if there is a workaround for this, but I have a
> feeling that passing NDEBUG for all packages might not be a
> good idea?
>

NDEBUG is a macro that, when set, will neutralize assert statements (see
man assert). The rationale is that assert statements can help developers
validate assumptions in a test phase, but that this code is better disabled
in release builds, for reasons of performance. At that time, the code is
expected to be correct.

It's odd but not unacceptable that bitcoin expects NDEBUG not to be set.

The workaround/solution would be to explicitly undefine NDEBUG, via
-UNDEBUG. This should become part of the flags set in bitcoin.mk on
Buildroot.

It is possible that there are other packages that need this change, but
generally one wpuld expect projects to allow both scenarios.

Best regards
Thomas
Thomas Petazzoni July 4, 2021, 9:15 a.m. UTC | #4
Hello,

On Tue,  1 Jun 2021 16:34:07 +0200
Thomas De Schampheleire <patrickdepinguin@gmail.com> wrote:

> From: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
> 
> The 'assert' statement in glibc honors the 'NDEBUG' preprocessor macro: if
> it is set, then the assert statement is compiled away.
> 
> Define this 'NDEBUG' macro when BR2_ENABLE_RUNTIME_DEBUG is disabled (the
> default case).
> 
> Signed-off-by: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>

So, I would like to challenge whether this change is really a good
idea. Since this has been merged, we had to add a significant number of
patches to undefine NDEBUG.

The manpage of assert() tells us:

       If  the  macro  NDEBUG is defined at the moment <assert.h> was last in‐
       cluded, the macro assert() generates no code, and hence does nothing at
       all.   It  is not recommended to define NDEBUG if using assert() to de‐
       tect error conditions since the software may  behave  non-deterministi‐
       cally.

So there is a clear recommandation to *not* define NDEBUG if assert is
used to detect error conditions. And later on, it also says:

BUGS
       assert() is implemented as a macro; if the expression tested has  side-
       effects, program behavior will be different depending on whether NDEBUG
       is defined.  This may create Heisenbugs which go away when debugging is
       turned on.

We have catched a number of packages where defining NDEBUG is causing
build issues, but I am worried about those packages where it doesn't
cause build issues, but assert() is used to run something that has
side-effects, and where the issue would only be visible at runtime.

Yes, ideally, assert() should only be used to verify expressions that
have no side effects. But practically speaking, I am not sure how many
programmers are really aware of the fact that assert() expressions
should not have side effects, because assert() can be disabled by
defining NDEBUG.

I believe this change is too dangerous, has caused a number of build
breakage + can cause run-time breakage that is difficult to predict.

What do you think ?

Thomas
Peter Korsgaard July 4, 2021, 11:01 a.m. UTC | #5
>>>>> "Thomas" == Thomas Petazzoni <thomas.petazzoni@bootlin.com> writes:

 > Hello,
 > On Tue,  1 Jun 2021 16:34:07 +0200
 > Thomas De Schampheleire <patrickdepinguin@gmail.com> wrote:

 >> From: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
 >> 
 >> The 'assert' statement in glibc honors the 'NDEBUG' preprocessor macro: if
 >> it is set, then the assert statement is compiled away.
 >> 
 >> Define this 'NDEBUG' macro when BR2_ENABLE_RUNTIME_DEBUG is disabled (the
 >> default case).
 >> 
 >> Signed-off-by: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>

 > So, I would like to challenge whether this change is really a good
 > idea. Since this has been merged, we had to add a significant number of
 > patches to undefine NDEBUG.

Indeed.

 > I believe this change is too dangerous, has caused a number of build
 > breakage + can cause run-time breakage that is difficult to predict.

 > What do you think ?

I agree, I don't think it ends up being a net positive regarding
cost/benefit. I would prefer to revert it.
Yann E. MORIN July 4, 2021, 11:32 a.m. UTC | #6
Peter, Thomas², All,

On 2021-07-04 13:01 +0200, Peter Korsgaard spake thusly:
> >>>>> "Thomas" == Thomas Petazzoni <thomas.petazzoni@bootlin.com> writes:
>  > Hello,
>  > On Tue,  1 Jun 2021 16:34:07 +0200
>  > Thomas De Schampheleire <patrickdepinguin@gmail.com> wrote:
>  >> From: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
>  >> 
>  >> The 'assert' statement in glibc honors the 'NDEBUG' preprocessor macro: if
>  >> it is set, then the assert statement is compiled away.
>  >> 
>  >> Define this 'NDEBUG' macro when BR2_ENABLE_RUNTIME_DEBUG is disabled (the
>  >> default case).
>  >> 
>  >> Signed-off-by: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
>  > So, I would like to challenge whether this change is really a good
>  > idea. Since this has been merged, we had to add a significant number of
>  > patches to undefine NDEBUG.
> Indeed.

Compiling without assert() should never have had an impact on the
behaviour of the program. Semantically, assert() should only be used to
test pre- and post-conditions (e.g. a value we were passed is in an
acceptable range), i.e. to ensure the contract of the API.

The problem is that developpers have started using assert() as a mean to
test expected error conditions (e.g. the socket was closed by the remote
end), instead of handling them gracefully.

As such, the semantic of assert() has shifted enough that the original
intent is mush less prominent than what it is currently used for.

So yes, this does more harm than it brings benefits.

>  > I believe this change is too dangerous, has caused a number of build
>  > breakage + can cause run-time breakage that is difficult to predict.
>  > What do you think ?
> I agree, I don't think it ends up being a net positive regarding
> cost/benefit. I would prefer to revert it.

In the end, for packages that do use assert() can add NDEBUG to their
CFLAGS if so they wish.

I will revert all those changes, since I was the one who applied the
original changes.

Regards,
Yann E. MORIN.
Yann E. MORIN July 4, 2021, 12:09 p.m. UTC | #7
Peter, Thomas², All,

On 2021-07-04 13:32 +0200, Yann E. MORIN spake thusly:
> On 2021-07-04 13:01 +0200, Peter Korsgaard spake thusly:
> > >>>>> "Thomas" == Thomas Petazzoni <thomas.petazzoni@bootlin.com> writes:
> >  > On Tue,  1 Jun 2021 16:34:07 +0200
> >  > Thomas De Schampheleire <patrickdepinguin@gmail.com> wrote:
> >  >> From: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
> >  >> 
> >  >> The 'assert' statement in glibc honors the 'NDEBUG' preprocessor macro: if
> >  >> it is set, then the assert statement is compiled away.
> >  >> 
> >  >> Define this 'NDEBUG' macro when BR2_ENABLE_RUNTIME_DEBUG is disabled (the
> >  >> default case).
> >  >> 
> >  >> Signed-off-by: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
> >  > So, I would like to challenge whether this change is really a good
> >  > idea. Since this has been merged, we had to add a significant number of
> >  > patches to undefine NDEBUG.

"Significant", being just 2 packages I had to revert as they do not have
corresponding upstream changes, or upstream expressly refused to support
NDEBUGi: bitcoin and weston. A few others have had the NDEBUG changes
accepted upstream, and for some wee even have updated to a version with
those patches.

Plus 6 patches pending in patchwork:
  - gcc: the issue is not trivial: -Werror or -DNDEBUG?
  - sane-backends: merged upstream
  - tor: can't build with NDEBUG
  - casync: can't with NDEBUG
  - qemu: can't with NDEBUG
  - xen: is already using NDEBUG, but causes -Werror issues

So, that's 8 packages affected by NDEBUG, of which 6 or 7 are going to be
an issue. I would not call that 'significant' yet.

However, as I said earlier, I still think this is not worth the effort,
indeed.

Regards,
Yann E. MORIN.
Arnout Vandecappelle July 4, 2021, 2:58 p.m. UTC | #8
On 04/07/2021 11:15, Thomas Petazzoni wrote:
> Hello,
> 
> On Tue,  1 Jun 2021 16:34:07 +0200
> Thomas De Schampheleire <patrickdepinguin@gmail.com> wrote:
> 
>> From: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
>>
>> The 'assert' statement in glibc honors the 'NDEBUG' preprocessor macro: if
>> it is set, then the assert statement is compiled away.
>>
>> Define this 'NDEBUG' macro when BR2_ENABLE_RUNTIME_DEBUG is disabled (the
>> default case).
>>
>> Signed-off-by: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
> 
> So, I would like to challenge whether this change is really a good
> idea.

 I'm the one who originally proposed this (I think) so I better pipe up.


> Since this has been merged, we had to add a significant number of
> patches to undefine NDEBUG.
> 
> The manpage of assert() tells us:
> 
>        If  the  macro  NDEBUG is defined at the moment <assert.h> was last in‐
>        cluded, the macro assert() generates no code, and hence does nothing at
>        all.   It  is not recommended to define NDEBUG if using assert() to de‐
>        tect error conditions since the software may  behave  non-deterministi‐
>        cally.

 In practice, the build failures are mostly due to variables that become unused
because the assert() is gone, and that triggers -Werror.


> So there is a clear recommandation to *not* define NDEBUG if assert is
> used to detect error conditions. And later on, it also says:
> 
> BUGS
>        assert() is implemented as a macro; if the expression tested has  side-
>        effects, program behavior will be different depending on whether NDEBUG
>        is defined.  This may create Heisenbugs which go away when debugging is
>        turned on.
> 
> We have catched a number of packages where defining NDEBUG is causing
> build issues, but I am worried about those packages where it doesn't
> cause build issues, but assert() is used to run something that has
> side-effects, and where the issue would only be visible at runtime.

 I don't think it's very likely that this happens, but indeed it's possibly. You
just have to "optimise":

    bool result = f();
    assert(result);

into

    assert(f());

and you get hit by this issue...


> Yes, ideally, assert() should only be used to verify expressions that
> have no side effects. But practically speaking, I am not sure how many
> programmers are really aware of the fact that assert() expressions
> should not have side effects, because assert() can be disabled by
> defining NDEBUG.

 Well, packages that use meson or CMake *do* get NDEBUG set (because that's done
by the release variants), so those packages at least seem to behave sanely...

 That said:

> I believe this change is too dangerous,

 This is definitely true. So I also think reverting it is the safest option.


 Regards,
 Arnout


> has caused a number of build
> breakage + can cause run-time breakage that is difficult to predict.
> 
> What do you think ?
> 
> Thomas
>
Yann E. MORIN July 4, 2021, 7:52 p.m. UTC | #9
Arnout, Peter, Thomas², All,

On 2021-07-04 16:58 +0200, Arnout Vandecappelle spake thusly:
> On 04/07/2021 11:15, Thomas Petazzoni wrote:
> > On Tue,  1 Jun 2021 16:34:07 +0200
> > Thomas De Schampheleire <patrickdepinguin@gmail.com> wrote:
> >> From: Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
> >> The 'assert' statement in glibc honors the 'NDEBUG' preprocessor macro: if
> >> it is set, then the assert statement is compiled away.
> >> Define this 'NDEBUG' macro when BR2_ENABLE_RUNTIME_DEBUG is disabled (the
> >> default case).
> > So, I would like to challenge whether this change is really a good
> > idea.
>  In practice, the build failures are mostly due to variables that become unused
> because the assert() is gone, and that triggers -Werror.
[--SNIP--]
>  I don't think it's very likely that this happens, but indeed it's possibly. You
> just have to "optimise":
>     bool result = f();
>     assert(result);
> into
>     assert(f());
> and you get hit by this issue...

And this is way too easy an optimisation to do. ;-)

> > Yes, ideally, assert() should only be used to verify expressions that
> > have no side effects. But practically speaking, I am not sure how many
> > programmers are really aware of the fact that assert() expressions
> > should not have side effects, because assert() can be disabled by
> > defining NDEBUG.
>  Well, packages that use meson or CMake *do* get NDEBUG set (because that's done
> by the release variants), so those packages at least seem to behave sanely...

>  That said:
> 
> > I believe this change is too dangerous,
> 
>  This is definitely true. So I also think reverting it is the safest option.

I've now done so: I reverted the change itself, and reverted the two
packages that had workarounds for this;

    fc0fb99f0307  Revert "package/weston: disable -NDEBUG"
    1aa3cb109067  Revert "package/bitcoin: unset the NDEBUG flag"
    a1c7cff1a081  Revert "core: enable 'NDEBUG' unless BR2_ENABLE_RUNTIME_DEBUG is set"

The other NDEBUG-related changes, I have left in the tree:

    b7939fe2a0b1  core: introduce BR2_ENABLE_RUNTIME_DEBUG
        => used by meson and cmake

    61c5e0319c5  package/pkg-cmake.mk: determine CMAKE_BUILD_TYPE depending on BR2_ENABLE_RUNTIME_DEBUG
        => see commit log for details

    fb12adbb761  package/sofia-sip: correct passing of '--enable-ndebug'
    0993954814e  package/sofia-sip: don't set 'NDEBUG' explicitly
        => the first was a fix; the second drops an explicit handling
           maybe we could revert 0993954814e, but I don't see the point

    ea02ac3e061  package/daemon: fix build without BR2_ENABLE_RUNTIME_DEBUG
        => very similar fix accepted upstream, so we will drop the patch
           when we bump

    bf32928bbb0  package/gnutls: disable tests
        => we still want to disable tests even without NDEBUG

    1f1d536e7e3  package/pdbg: fix build with -DNDEBUG
    932f6a0a2ad  package/pdbg: Bump version to v3.3
        => a patch we had was accepted upstream, and we updated the
           version

    cc1c8c3bb1c  package/openswan: disable -Werror
        => we still want to disable Werror even without NDEBUG

    12551386027  package/filemq: bump to af4768dcaf2fcb8083a32bad107a22ecb7a5d954
        => is a version bump anyway

I will now reply to the individual pending patches telated to NDEBUG,
and reject them.

Regards,
Yann E. MORIN.
diff mbox series

Patch

diff --git a/package/Makefile.in b/package/Makefile.in
index 86db62ba5b..955e6a8e8c 100644
--- a/package/Makefile.in
+++ b/package/Makefile.in
@@ -148,6 +148,9 @@  endif
 ifeq ($(BR2_DEBUG_3),y)
 TARGET_DEBUGGING = -g3
 endif
+ifeq ($(BR2_ENABLE_RUNTIME_DEBUG),)
+TARGET_DEBUGGING += -DNDEBUG
+endif
 
 TARGET_LDFLAGS = $(call qstrip,$(BR2_TARGET_LDFLAGS))