Hi Arsen,
Just read the article and am quite pleased with it. It's short and sweet
and gets to the point quickly - I think this will be my go to reference
the next time I need to explain assertions to someone.
It's unfortunate that the education around `assert` is so poor, it has
been a very valuable tool in my experience (especially in early
development where things can change rapidly).
One small gripe I have is that it doesn't mention (ab)using `assert` as
error handlers. For example:
FILE *f = fopen(...);
assert(f != NULL);
In this case, fopen failing is a legitimate runtime error condition that
needs to be handled properly - not an "impossible case".
I think this mistake is common enough (I used to do this in my early
days) that it warrants being called out explicitly.
Also regarding comments, I think Rob Pike really nails it down on his
style notes https://www.lysator.liu.se/c/pikestyle.html:
| Comments aren't checked by the compiler, so there is no guarantee
| they're right, especially after the code is modified.
Since your article tries to be language agnostic, this is probably out
of scope, but for C I've found `__builtin_trap` to provide a better
experience when run under debuggers, since it traps immediately instead
of raising SIGABRT: https://nullprogram.com/blog/2022/06/26/
And that's all from me. A very well written article and I'm glad I found
it, it will be a handy reference in the future!
- NRK
Hi,
NRK <nrk@disroot.org> writes:
> Hi Arsen,>> Just read the article and am quite pleased with it. It's short and sweet> and gets to the point quickly - I think this will be my go to reference> the next time I need to explain assertions to someone.>> It's unfortunate that the education around `assert` is so poor, it has> been a very valuable tool in my experience (especially in early> development where things can change rapidly).>> One small gripe I have is that it doesn't mention (ab)using `assert` as> error handlers. For example:>> FILE *f = fopen(...);> assert(f != NULL);>> In this case, fopen failing is a legitimate runtime error condition that> needs to be handled properly - not an "impossible case".
This is indeed abuse, and I wrote this article with a codebase that does
this profusely in mind.
I should have probably been more explicit in the "Assertions MUST NOT be
the only thing between misbehavior and input data" paragraph about this,
but, I indeed meant to include data returned from libraries in the set
of input data. I'll update with stronger wording, thanks for the
suggestion.
> I think this mistake is common enough (I used to do this in my early> days) that it warrants being called out explicitly.>> Also regarding comments, I think Rob Pike really nails it down on his> style notes https://www.lysator.liu.se/c/pikestyle.html:>> | Comments aren't checked by the compiler, so there is no guarantee> | they're right, especially after the code is modified.>> Since your article tries to be language agnostic, this is probably out> of scope, but for C I've found `__builtin_trap` to provide a better> experience when run under debuggers, since it traps immediately instead> of raising SIGABRT: https://nullprogram.com/blog/2022/06/26/
SIGABRT should be caught as a synchronous signal would be in debuggers,
such as the one you get via __builtin_trap, so I don't agree with this
making a difference. There's also no need to ``up'' many times, as the
author suggests, instead, you can ``select-frame'' or ``up N''.
Asserts also have additional runtime behavior compared to a trap, in
that they diagnose the failure. I'd like to even have GCC add code to
print out what sub-expressions evaluated to at some point. Sounds like
a fun thing to implement.
Re missing debuginfo: this should really be provided by your
distributor, if applicable, and "debugging without debuginfo" seems like
an unnecessary exercise.
> And that's all from me. A very well written article and I'm glad I found> it, it will be a handy reference in the future!>> - NRK
Thanks for the kind words!
Have a great night.
--
Arsen Arsenović
On Wed, Feb 01, 2023 at 08:12:54PM +0100, Arsen Arsenović wrote:
> There's also no need to ``up'' many times, as the author suggests,> instead, you can ``select-frame'' or ``up N''.
I personally use `f $N` to jump to a frame. But with `__builtin_trap`
there's no need to do that at all as it will break right where the
assertion failed - which is less friction than having to `bt + f $n`
(unless you happen to know something I don't that can make standard C
assertions have the same effect).
> Asserts also have additional runtime behavior compared to a trap, in> that they diagnose the failure.
When running under a debugger, I don't find that too useful. In fact I
almost always have the stdout/err window hidden when using vim's gdb
plugin (the primary way I interact with gdb).
> Have a great night.
Same to you!
- NRK
Morning,
NRK <nrk@disroot.org> writes:
> On Wed, Feb 01, 2023 at 08:12:54PM +0100, Arsen Arsenović wrote:>> There's also no need to ``up'' many times, as the author suggests,>> instead, you can ``select-frame'' or ``up N''.>> I personally use `f $N` to jump to a frame. But with `__builtin_trap`> there's no need to do that at all as it will break right where the> assertion failed - which is less friction than having to `bt + f $n`> (unless you happen to know something I don't that can make standard C> assertions have the same effect).
Does breaking on __assert_fail do the trick for you? Technically, it's
still a frame higher, but IMO that's worth it over adding unnecessary
platform-specifics and worse runtime behavior.
You could add that break in your gdb init file.
>> Asserts also have additional runtime behavior compared to a trap, in>> that they diagnose the failure.>> When running under a debugger, I don't find that too useful. In fact I> almost always have the stdout/err window hidden when using vim's gdb> plugin (the primary way I interact with gdb).
You can't assume you're running in a debugger when designing software,
that said, it is OK to leave stuff as helpers for debugging. As an
example, GCC exposes some pretty printing functions so that they can be
called from GDB.
You could write a new assert that, if defined (TRAP_ON_ASSERT), expands
to an if (!(x)) __builtin_trap (); or otherwise simply assert (x).
Have a nice day.
--
Arsen Arsenović
On Thu, Feb 02, 2023 at 11:02:27AM +0100, Arsen Arsenović wrote:
> Does breaking on __assert_fail do the trick for you? Technically, it's> still a frame higher, but IMO that's worth it over adding unnecessary> platform-specifics and worse runtime behavior.>> You can't assume you're running in a debugger when designing software,
It's a bit better, but still not quite there. But perhaps that can be
fixed with some gdb hook (I'll have to look into this sometime).
Also I think I realized the main disconnect: since I'm mostly working on
small & solo stuff - I don't care much about having platform-specific
things in debug code. While you're thinking of larger projects with many
people working on it (some of whom might not be running under a
debugger). In which case, standard assert probably is better than having
to mess with ifdefs to get __builtin_trap.
- NRK
Hi,
Sorry for responding so late, I've been beyond busy lately...
NRK <nrk@disroot.org> writes:
> On Thu, Feb 02, 2023 at 11:02:27AM +0100, Arsen Arsenović wrote:>> Does breaking on __assert_fail do the trick for you? Technically, it's>> still a frame higher, but IMO that's worth it over adding unnecessary>> platform-specifics and worse runtime behavior.>>>> You can't assume you're running in a debugger when designing software,>> It's a bit better, but still not quite there. But perhaps that can be> fixed with some gdb hook (I'll have to look into this sometime).
Yeah, you could associate a 'command' to that breakpoint that 'up's
immediately. Like so:
~$ gdb -q poke
Reading symbols from poke...
(gdb) break __assert_fail
Breakpoint 1 at 0x4260
(gdb) command 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>up
>end
(gdb)
See (gdb)Breakpoints.
> Also I think I realized the main disconnect: since I'm mostly working on> small & solo stuff - I don't care much about having platform-specific> things in debug code. While you're thinking of larger projects with many> people working on it (some of whom might not be running under a> debugger). In which case, standard assert probably is better than having> to mess with ifdefs to get __builtin_trap.
I recommend not making the distinction, you never know when someone
might join your efforts. That is the joy of Free Software, after all.
> - NRK
Have a lovely day.
--
Arsen Arsenović