include/SDL_assert.h
author Ozkan Sezer <sezeroz@gmail.com>
Thu, 17 Aug 2017 21:32:42 -0400
changeset 11316 76abc6e1eb4b
parent 11016 5326f7660eee
child 11317 f57e81db97b5
permissions -rw-r--r--
SDL_assert.h: add inline asm (int $3) as SDL_TriggerBreakpoint for Watcom/x86

(also disable SIGTRAP case to !watcom, because watcom doesn't have SIGTRAP.)

Partially fixes Bugzilla #3758.
slouken@3647
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@3647
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@3647
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@3647
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@3647
    20
*/
slouken@3647
    21
slouken@10638
    22
#ifndef SDL_assert_h_
slouken@10638
    23
#define SDL_assert_h_
slouken@3647
    24
slouken@3655
    25
#include "SDL_config.h"
slouken@3655
    26
slouken@3655
    27
#include "begin_code.h"
slouken@3655
    28
/* Set up for C function definitions, even when using C++ */
slouken@3655
    29
#ifdef __cplusplus
slouken@3655
    30
extern "C" {
slouken@3655
    31
#endif
slouken@3655
    32
slouken@3647
    33
#ifndef SDL_ASSERT_LEVEL
slouken@3654
    34
#ifdef SDL_DEFAULT_ASSERT_LEVEL
slouken@3654
    35
#define SDL_ASSERT_LEVEL SDL_DEFAULT_ASSERT_LEVEL
slouken@3654
    36
#elif defined(_DEBUG) || defined(DEBUG) || \
slouken@3654
    37
      (defined(__GNUC__) && !defined(__OPTIMIZE__))
slouken@3653
    38
#define SDL_ASSERT_LEVEL 2
slouken@3653
    39
#else
slouken@3653
    40
#define SDL_ASSERT_LEVEL 1
slouken@3647
    41
#endif
slouken@3653
    42
#endif /* SDL_ASSERT_LEVEL */
slouken@3647
    43
slouken@3647
    44
/*
slouken@3655
    45
These are macros and not first class functions so that the debugger breaks
slouken@3655
    46
on the assertion line and not in some random guts of SDL, and so each
icculus@3665
    47
assert can have unique static variables associated with it.
slouken@3655
    48
*/
slouken@3655
    49
icculus@6430
    50
#if defined(_MSC_VER)
slouken@5491
    51
/* Don't include intrin.h here because it contains C++ code */
icculus@6430
    52
    extern void __cdecl __debugbreak(void);
slouken@5011
    53
    #define SDL_TriggerBreakpoint() __debugbreak()
icculus@11016
    54
#elif ( (!defined(__NACL__)) && ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))) )
slouken@3655
    55
    #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "int $3\n\t" )
sezeroz@11316
    56
#elif defined(__386__) && defined(__WATCOMC__)
sezeroz@11316
    57
    #define SDL_TriggerBreakpoint() { _asm { int 0x03 } }
sezeroz@11316
    58
#elif defined(HAVE_SIGNAL_H) && !defined(__WATCOMC__)
slouken@3655
    59
    #include <signal.h>
slouken@3655
    60
    #define SDL_TriggerBreakpoint() raise(SIGTRAP)
slouken@3655
    61
#else
slouken@3655
    62
    /* How do we trigger breakpoints on this platform? */
slouken@3655
    63
    #define SDL_TriggerBreakpoint()
slouken@3655
    64
#endif
slouken@3655
    65
icculus@5552
    66
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 supports __func__ as a standard. */
slouken@3655
    67
#   define SDL_FUNCTION __func__
slouken@3655
    68
#elif ((__GNUC__ >= 2) || defined(_MSC_VER))
slouken@3655
    69
#   define SDL_FUNCTION __FUNCTION__
slouken@3655
    70
#else
slouken@3655
    71
#   define SDL_FUNCTION "???"
slouken@3655
    72
#endif
slouken@3655
    73
#define SDL_FILE    __FILE__
slouken@3655
    74
#define SDL_LINE    __LINE__
slouken@3655
    75
slouken@3655
    76
/*
slouken@3647
    77
sizeof (x) makes the compiler still parse the expression even without
slouken@3647
    78
assertions enabled, so the code is always checked at compile time, but
slouken@3647
    79
doesn't actually generate code for it, so there are no side effects or
slouken@3647
    80
expensive checks at run time, just the constant size of what x WOULD be,
slouken@3647
    81
which presumably gets optimized out as unused.
slouken@3647
    82
This also solves the problem of...
slouken@3647
    83
slouken@3647
    84
    int somevalue = blah();
slouken@3647
    85
    SDL_assert(somevalue == 1);
slouken@3647
    86
slouken@3647
    87
...which would cause compiles to complain that somevalue is unused if we
slouken@3647
    88
disable assertions.
slouken@3647
    89
*/
slouken@3647
    90
icculus@9463
    91
/* "while (0,0)" fools Microsoft's compiler's /W4 warning level into thinking
icculus@9463
    92
    this condition isn't constant. And looks like an owl's face! */
icculus@9470
    93
#ifdef _MSC_VER  /* stupid /W4 warnings. */
icculus@9463
    94
#define SDL_NULL_WHILE_LOOP_CONDITION (0,0)
icculus@9470
    95
#else
icculus@9470
    96
#define SDL_NULL_WHILE_LOOP_CONDITION (0)
icculus@9470
    97
#endif
icculus@7736
    98
slouken@3647
    99
#define SDL_disabled_assert(condition) \
icculus@7736
   100
    do { (void) sizeof ((condition)); } while (SDL_NULL_WHILE_LOOP_CONDITION)
slouken@3647
   101
slouken@3647
   102
typedef enum
slouken@3647
   103
{
slouken@3647
   104
    SDL_ASSERTION_RETRY,  /**< Retry the assert immediately. */
slouken@3647
   105
    SDL_ASSERTION_BREAK,  /**< Make the debugger trigger a breakpoint. */
slouken@3647
   106
    SDL_ASSERTION_ABORT,  /**< Terminate the program. */
slouken@3647
   107
    SDL_ASSERTION_IGNORE,  /**< Ignore the assert. */
icculus@5625
   108
    SDL_ASSERTION_ALWAYS_IGNORE  /**< Ignore the assert from now on. */
icculus@9371
   109
} SDL_AssertState;
slouken@3647
   110
icculus@9371
   111
typedef struct SDL_AssertData
slouken@3647
   112
{
slouken@3647
   113
    int always_ignore;
slouken@3647
   114
    unsigned int trigger_count;
slouken@3647
   115
    const char *condition;
slouken@3647
   116
    const char *filename;
slouken@3647
   117
    int linenum;
slouken@3647
   118
    const char *function;
icculus@9371
   119
    const struct SDL_AssertData *next;
icculus@9371
   120
} SDL_AssertData;
slouken@3647
   121
icculus@6761
   122
#if (SDL_ASSERT_LEVEL > 0)
icculus@6761
   123
icculus@3668
   124
/* Never call this directly. Use the SDL_assert* macros. */
icculus@9371
   125
extern DECLSPEC SDL_AssertState SDLCALL SDL_ReportAssertion(SDL_AssertData *,
slouken@3655
   126
                                                             const char *,
icculus@8189
   127
                                                             const char *, int)
icculus@8190
   128
#if defined(__clang__)
icculus@8190
   129
#if __has_feature(attribute_analyzer_noreturn)
icculus@8189
   130
/* this tells Clang's static analysis that we're a custom assert function,
icculus@8189
   131
   and that the analyzer should assume the condition was always true past this
icculus@8189
   132
   SDL_assert test. */
icculus@8189
   133
   __attribute__((analyzer_noreturn))
icculus@8189
   134
#endif
icculus@8190
   135
#endif
icculus@8189
   136
;
slouken@3647
   137
slouken@3647
   138
/* the do {} while(0) avoids dangling else problems:
slouken@3647
   139
    if (x) SDL_assert(y); else blah();
slouken@3647
   140
       ... without the do/while, the "else" could attach to this macro's "if".
slouken@3647
   141
   We try to handle just the minimum we need here in a macro...the loop,
slouken@3647
   142
   the static vars, and break points. The heavy lifting is handled in
slouken@3647
   143
   SDL_ReportAssertion(), in SDL_assert.c.
slouken@3647
   144
*/
slouken@3647
   145
#define SDL_enabled_assert(condition) \
slouken@3647
   146
    do { \
slouken@3647
   147
        while ( !(condition) ) { \
icculus@9371
   148
            static struct SDL_AssertData sdl_assert_data = { \
slouken@3655
   149
                0, 0, #condition, 0, 0, 0, 0 \
slouken@3647
   150
            }; \
icculus@9371
   151
            const SDL_AssertState sdl_assert_state = SDL_ReportAssertion(&sdl_assert_data, SDL_FUNCTION, SDL_FILE, SDL_LINE); \
icculus@9019
   152
            if (sdl_assert_state == SDL_ASSERTION_RETRY) { \
slouken@3647
   153
                continue; /* go again. */ \
icculus@9019
   154
            } else if (sdl_assert_state == SDL_ASSERTION_BREAK) { \
slouken@3647
   155
                SDL_TriggerBreakpoint(); \
slouken@3647
   156
            } \
slouken@3647
   157
            break; /* not retrying. */ \
slouken@3647
   158
        } \
icculus@7736
   159
    } while (SDL_NULL_WHILE_LOOP_CONDITION)
slouken@3647
   160
slouken@3647
   161
#endif  /* enabled assertions support code */
slouken@3647
   162
slouken@3647
   163
/* Enable various levels of assertions. */
slouken@3647
   164
#if SDL_ASSERT_LEVEL == 0   /* assertions disabled */
slouken@3647
   165
#   define SDL_assert(condition) SDL_disabled_assert(condition)
slouken@3647
   166
#   define SDL_assert_release(condition) SDL_disabled_assert(condition)
slouken@3647
   167
#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
slouken@3647
   168
#elif SDL_ASSERT_LEVEL == 1  /* release settings. */
slouken@3652
   169
#   define SDL_assert(condition) SDL_disabled_assert(condition)
slouken@3647
   170
#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
slouken@3652
   171
#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
slouken@3647
   172
#elif SDL_ASSERT_LEVEL == 2  /* normal settings. */
slouken@3647
   173
#   define SDL_assert(condition) SDL_enabled_assert(condition)
slouken@3647
   174
#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
slouken@3647
   175
#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
slouken@3647
   176
#elif SDL_ASSERT_LEVEL == 3  /* paranoid settings. */
slouken@3647
   177
#   define SDL_assert(condition) SDL_enabled_assert(condition)
slouken@3647
   178
#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
slouken@3647
   179
#   define SDL_assert_paranoid(condition) SDL_enabled_assert(condition)
slouken@3647
   180
#else
slouken@3655
   181
#   error Unknown assertion level.
slouken@3647
   182
#endif
slouken@3647
   183
icculus@7664
   184
/* this assertion is never disabled at any level. */
icculus@7664
   185
#define SDL_assert_always(condition) SDL_enabled_assert(condition)
icculus@7664
   186
icculus@3670
   187
icculus@9371
   188
typedef SDL_AssertState (SDLCALL *SDL_AssertionHandler)(
icculus@9371
   189
                                 const SDL_AssertData* data, void* userdata);
icculus@3670
   190
icculus@3670
   191
/**
icculus@3670
   192
 *  \brief Set an application-defined assertion handler.
icculus@3670
   193
 *
icculus@3670
   194
 *  This allows an app to show its own assertion UI and/or force the
icculus@3670
   195
 *  response to an assertion failure. If the app doesn't provide this, SDL
icculus@3670
   196
 *  will try to do the right thing, popping up a system-specific GUI dialog,
icculus@3670
   197
 *  and probably minimizing any fullscreen windows.
icculus@3670
   198
 *
icculus@3670
   199
 *  This callback may fire from any thread, but it runs wrapped in a mutex, so
icculus@3670
   200
 *  it will only fire from one thread at a time.
icculus@3670
   201
 *
icculus@3670
   202
 *  Setting the callback to NULL restores SDL's original internal handler.
icculus@3670
   203
 *
icculus@3670
   204
 *  This callback is NOT reset to SDL's internal handler upon SDL_Quit()!
icculus@3670
   205
 *
slouken@10636
   206
 *  Return SDL_AssertState value of how to handle the assertion failure.
slouken@7191
   207
 *
icculus@3670
   208
 *  \param handler Callback function, called when an assertion fails.
icculus@3670
   209
 *  \param userdata A pointer passed to the callback as-is.
icculus@3670
   210
 */
icculus@3670
   211
extern DECLSPEC void SDLCALL SDL_SetAssertionHandler(
icculus@3670
   212
                                            SDL_AssertionHandler handler,
icculus@3670
   213
                                            void *userdata);
icculus@3670
   214
icculus@3670
   215
/**
icculus@8167
   216
 *  \brief Get the default assertion handler.
icculus@8167
   217
 *
icculus@8167
   218
 *  This returns the function pointer that is called by default when an
icculus@8167
   219
 *   assertion is triggered. This is an internal function provided by SDL,
icculus@8167
   220
 *   that is used for assertions when SDL_SetAssertionHandler() hasn't been
icculus@8167
   221
 *   used to provide a different function.
icculus@8167
   222
 *
icculus@8167
   223
 *  \return The default SDL_AssertionHandler that is called when an assert triggers.
icculus@8167
   224
 */
icculus@8167
   225
extern DECLSPEC SDL_AssertionHandler SDLCALL SDL_GetDefaultAssertionHandler(void);
icculus@8167
   226
icculus@8167
   227
/**
icculus@8167
   228
 *  \brief Get the current assertion handler.
icculus@8167
   229
 *
icculus@8167
   230
 *  This returns the function pointer that is called when an assertion is
icculus@8167
   231
 *   triggered. This is either the value last passed to
icculus@8167
   232
 *   SDL_SetAssertionHandler(), or if no application-specified function is
icculus@8167
   233
 *   set, is equivalent to calling SDL_GetDefaultAssertionHandler().
icculus@8167
   234
 *
icculus@8167
   235
 *   \param puserdata Pointer to a void*, which will store the "userdata"
icculus@8167
   236
 *                    pointer that was passed to SDL_SetAssertionHandler().
icculus@8167
   237
 *                    This value will always be NULL for the default handler.
icculus@8167
   238
 *                    If you don't care about this data, it is safe to pass
icculus@8167
   239
 *                    a NULL pointer to this function to ignore it.
icculus@8167
   240
 *  \return The SDL_AssertionHandler that is called when an assert triggers.
icculus@8167
   241
 */
icculus@8167
   242
extern DECLSPEC SDL_AssertionHandler SDLCALL SDL_GetAssertionHandler(void **puserdata);
icculus@8167
   243
icculus@8167
   244
/**
icculus@3670
   245
 *  \brief Get a list of all assertion failures.
icculus@3670
   246
 *
icculus@3670
   247
 *  Get all assertions triggered since last call to SDL_ResetAssertionReport(),
icculus@3670
   248
 *  or the start of the program.
icculus@3670
   249
 *
icculus@3670
   250
 *  The proper way to examine this data looks something like this:
icculus@3670
   251
 *
icculus@3670
   252
 *  <code>
icculus@9371
   253
 *  const SDL_AssertData *item = SDL_GetAssertionReport();
icculus@5541
   254
 *  while (item) {
slouken@10636
   255
 *      printf("'%s', %s (%s:%d), triggered %u times, always ignore: %s.\\n",
icculus@3670
   256
 *             item->condition, item->function, item->filename,
icculus@3670
   257
 *             item->linenum, item->trigger_count,
icculus@3670
   258
 *             item->always_ignore ? "yes" : "no");
icculus@3670
   259
 *      item = item->next;
icculus@3670
   260
 *  }
icculus@3670
   261
 *  </code>
icculus@3670
   262
 *
icculus@5541
   263
 *  \return List of all assertions.
icculus@3670
   264
 *  \sa SDL_ResetAssertionReport
icculus@3670
   265
 */
icculus@9371
   266
extern DECLSPEC const SDL_AssertData * SDLCALL SDL_GetAssertionReport(void);
icculus@3670
   267
icculus@3670
   268
/**
icculus@3670
   269
 *  \brief Reset the list of all assertion failures.
icculus@3670
   270
 *
icculus@3670
   271
 *  Reset list of all assertions triggered.
icculus@3670
   272
 *
icculus@3670
   273
 *  \sa SDL_GetAssertionReport
icculus@3670
   274
 */
icculus@3670
   275
extern DECLSPEC void SDLCALL SDL_ResetAssertionReport(void);
icculus@3670
   276
icculus@9371
   277
icculus@9371
   278
/* these had wrong naming conventions until 2.0.4. Please update your app! */
icculus@9371
   279
#define SDL_assert_state SDL_AssertState
icculus@9371
   280
#define SDL_assert_data SDL_AssertData
icculus@9371
   281
icculus@9371
   282
slouken@3655
   283
/* Ends C function definitions when using C++ */
slouken@3655
   284
#ifdef __cplusplus
slouken@3655
   285
}
slouken@3655
   286
#endif
slouken@3655
   287
#include "close_code.h"
slouken@3655
   288
slouken@10638
   289
#endif /* SDL_assert_h_ */
slouken@3647
   290
slouken@3647
   291
/* vi: set ts=4 sw=4 expandtab: */