We are currently migrating Bugzilla to GitHub issues.
Any changes made to the bug tracker now will be lost, so please do not post new bugs or make changes to them.
When we're done, all bug URLs will redirect to their equivalent location on the new bug tracker.

Bug 676 - There is no SDL_assert macro.
Summary: There is no SDL_assert macro.
Status: RESOLVED FIXED
Alias: None
Product: SDL
Classification: Unclassified
Component: *don't know* (show other bugs)
Version: HG 2.0
Hardware: All All
: P1 enhancement
Assignee: Sam Lantinga
QA Contact: Sam Lantinga
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-01-10 19:24 UTC by Ryan C. Gordon
Modified: 2010-01-18 06:59 UTC (History)
0 users

See Also:


Attachments
RFC patch for assert macro. (7.17 KB, patch)
2009-12-15 11:52 UTC, Ryan C. Gordon
Details | Diff
Second attempt. (17.87 KB, patch)
2009-12-16 01:51 UTC, Ryan C. Gordon
Details | Diff
Third shot. (21.43 KB, patch)
2009-12-28 23:55 UTC, Ryan C. Gordon
Details | Diff
Feature-complete patch. (26.85 KB, patch)
2010-01-02 01:43 UTC, Ryan C. Gordon
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Ryan C. Gordon 2009-01-10 19:24:05 UTC
We need a (non-public!) macro for assertions, or just use assert() itself? We don't have many asserts in the codebase, and that's probably a bad habit, so we should probably stick something in the stdlib code to encourage this.

--ryan.
Comment 1 Sam Lantinga 2009-02-16 21:41:29 UTC
I agree. :)
Comment 2 Ryan C. Gordon 2009-12-15 11:52:15 UTC
Created attachment 465 [details]
RFC patch for assert macro.


Here's a first shot at this. I decided to go hardcore here.  :)

Totally untested, not even compiled, this is just proof of concept.

The plan:
Using the C runtime assert() has issues and limitations, so we never use it, even if available. I figure if we're going to do this, we might as well think about the best possible assert() we can get.


This version has the following features:
- It works with single-statement "if" statements...
    if (blah)
      SDL_assert(x);
    else
      do_something();

   ...that "else" will end up in the right place, with or without assertions.

- It works with otherwise unused variables...

      int q = blah();
      SDL_assert(q != 1);

   ...won't throw a compiler warning about unused variables.

- It totally removes itself from generated code when assertions are disabled, but the compiler still parses the expression, so these don't bitrot if assertions get disabled for extended periods of time.

- It allows the developer to abort/break/retry/ignore/ignore-always for every individual assertion.

- It keeps a running total of how many times a given assertion has triggered, with unique counter for each assertion.

Obviously, there are several FIXMEs still, and the code style needs a cleanup. But I'm posting this for review of the basic idea first.

--ryan.
Comment 3 Sam Lantinga 2009-12-15 13:20:40 UTC
Woah.  I like it!

I'm not sure about the trigger count.  If you get a line printed each time the assertion is hit, it seems like it's only useful for accumulated stats at the end of the program.

Also, it would be awesome for the app to be able to provide an assertion catching function and/or set the default assertion behavior.

Can we check to see if the compiler supports __FUNCTION__ as well?

Can we have a couple levels of assertion as well?  e.g. NONE/RELEASE/PARANOID
Comment 4 Ryan C. Gordon 2009-12-15 22:33:02 UTC
Bumping priority on a set of bugs.

--ryan.
Comment 5 Ryan C. Gordon 2009-12-15 22:53:46 UTC
Let me think on the trigger count stuff.

> Also, it would be awesome for the app to be able to provide an assertion
> catching function and/or set the default assertion behavior.

I hesitate to let the app touch this stuff at all. Part of me wants to add hooks to let the app show its own UI, but this is meant for debugging SDL, and things get complicated when we start trying to interact with the app.

> Can we check to see if the compiler supports __FUNCTION__ as well?

That's a good idea.
 
> Can we have a couple levels of assertion as well?  e.g. NONE/RELEASE/PARANOID

Yeah, we can do that.

--ryan.
Comment 6 Ryan C. Gordon 2009-12-16 01:51:12 UTC
Created attachment 466 [details]
Second attempt.


Okay, round two.

Still TODO:
- Platform code needs work, but x86/x86-64 Windows/Mac/Linux should be good to go for the basic stuff.
- Need to fill in UI for triggering assertions. Right now it just assumes the user wants an abort, which is basically what a normal assert does.

- Now mutexed, so it's safe for two threads to assert at the same time, even on the same line of code.
- Now reports function name, if possible.
- Now hooked into the build system and compiling cleanly.
- Code has been cleaned up.
- Everything that isn't crucial to run in the macro has been moved out of it. Just the things that need to be there (unique static data allocation, breakpoint triggering so it's on the right line in the debugger, and loops so you can retry the condition).
- Can now enable "levels" of assertion. This is currently disabled, release, normal, and paranoid. Use --enable-assertions=[no|release|yes|paranoid] in the configure script to set it. Things that don't use configure have release settings in their SDL_config_*.h for now, just for basic test coverage.
The macros are SDL_assert(), SDL_assert_release() and SDL_assert_paranoid().
- We can now generate reports of all assertions (we build a linked list of the static structs each assert macro creates, as they trigger, so adding to the report is an O(1) operation with no dynamic allocations involved). Does nothing if no assertions ever trigger. Still, this can be turned off with a #define.

So if you were to have this code and ignore all assertions...

    SDL_assert_release(1 == 2);
    SDL_assert_release(5 < 4);
    SDL_assert_release(0 && "This is a test");

...you'd see this when you call SDL_Quit()...

SDL assertion report.
All SDL assertions between last init/quit:

'0 && "This is a test"'
    * SDL_Init (src/SDL.c:195)
    * triggered 1 times.
    * always ignore: no.
'5 < 4'
    * SDL_Init (src/SDL.c:194)
    * triggered 1 times.
    * always ignore: no.
'1 == 2'
    * SDL_Init (src/SDL.c:193)
    * triggered 1 times.
    * always ignore: no.



...is this sounding closer to your idea, Sam?

--ryan.
Comment 7 Ryan C. Gordon 2009-12-28 23:55:54 UTC
Created attachment 473 [details]
Third shot.


More work.

- Assertion handler can take an environment variable (so unit tests, etc, don't hang up, waiting on a click in an assertion GUI).
- Cocoa UI for Mac OS X is complete.
- stdio "UI" for Unix is complete.
- mutex fixes.
- other tweaks.

Still todo:
- Windows UI.

--ryan.
Comment 8 Ryan C. Gordon 2010-01-02 01:43:16 UTC
Created attachment 475 [details]
Feature-complete patch.


Here's the final patch. I cleaned up a few things and wrote the Windows code.

Please note that the Windows code is really nasty. There is definite room for improvement. But spending too much time building GUI controls with win32 API calls is madness.   :)

Also, Windows (and Mac OS X) throw a fit when an assertion is triggered during a fullscreen rendering context. The assertion dialog will work, but it tends to leave the desktop or application broken. This could be handled better with some later refinements.

All this needs now is Sam to look it over and clean it up to his liking, so I'm tossing the bug to him. Toss it back to me if you just want me to commit it.

After that, we can start sprinkling the codebase with assertions.

--ryan.
Comment 9 Ryan C. Gordon 2010-01-02 01:44:01 UTC
Tossing to Sam.

--ryan.
Comment 10 Sam Lantinga 2010-01-18 06:59:51 UTC
Okay, this is in!