src/SDL_assert.c
author David Ludwig <dludwig@pobox.com>
Sun, 04 Nov 2012 13:26:53 -0500
changeset 8341 99c7c87774d5
parent 8337 4a67a3cca43d
child 8377 3a7e8110201e
permissions -rw-r--r--
WinRT: build fix for SDL_assert.c
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 #include "SDL.h"
    24 #include "SDL_atomic.h"
    25 #include "SDL_messagebox.h"
    26 #include "SDL_video.h"
    27 #include "SDL_assert.h"
    28 #include "SDL_assert_c.h"
    29 #include "video/SDL_sysvideo.h"
    30 
    31 #ifdef __WIN32__
    32 #include "core/windows/SDL_windows.h"
    33 
    34 #ifndef WS_OVERLAPPEDWINDOW
    35 #define WS_OVERLAPPEDWINDOW 0
    36 #endif
    37 #else  /* fprintf, _exit(), etc. */
    38 #include <stdio.h>
    39 #include <stdlib.h>
    40 #if ! defined(__WINRT__)
    41 #include <unistd.h>
    42 #endif
    43 #endif
    44 
    45 static SDL_assert_state
    46 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata);
    47 
    48 /*
    49  * We keep all triggered assertions in a singly-linked list so we can
    50  *  generate a report later.
    51  */
    52 static SDL_assert_data *triggered_assertions = NULL;
    53 
    54 static SDL_mutex *assertion_mutex = NULL;
    55 static SDL_AssertionHandler assertion_handler = SDL_PromptAssertion;
    56 static void *assertion_userdata = NULL;
    57 
    58 #ifdef __GNUC__
    59 static void
    60 debug_print(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
    61 #endif
    62 
    63 static void
    64 debug_print(const char *fmt, ...)
    65 {
    66     va_list ap;
    67     va_start(ap, fmt);
    68     SDL_LogMessageV(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_WARN, fmt, ap);
    69     va_end(ap);
    70 }
    71 
    72 
    73 static void SDL_AddAssertionToReport(SDL_assert_data *data)
    74 {
    75     /* (data) is always a static struct defined with the assert macros, so
    76        we don't have to worry about copying or allocating them. */
    77     data->trigger_count++;
    78     if (data->trigger_count == 1) {  /* not yet added? */
    79         data->next = triggered_assertions;
    80         triggered_assertions = data;
    81     }
    82 }
    83 
    84 
    85 static void SDL_GenerateAssertionReport(void)
    86 {
    87     const SDL_assert_data *item = triggered_assertions;
    88 
    89     /* only do this if the app hasn't assigned an assertion handler. */
    90     if ((item != NULL) && (assertion_handler != SDL_PromptAssertion)) {
    91         debug_print("\n\nSDL assertion report.\n");
    92         debug_print("All SDL assertions between last init/quit:\n\n");
    93 
    94         while (item != NULL) {
    95             debug_print(
    96                 "'%s'\n"
    97                 "    * %s (%s:%d)\n"
    98                 "    * triggered %u time%s.\n"
    99                 "    * always ignore: %s.\n",
   100                 item->condition, item->function, item->filename,
   101                 item->linenum, item->trigger_count,
   102                 (item->trigger_count == 1) ? "" : "s",
   103                 item->always_ignore ? "yes" : "no");
   104             item = item->next;
   105         }
   106         debug_print("\n");
   107 
   108         SDL_ResetAssertionReport();
   109     }
   110 }
   111 
   112 static void SDL_ExitProcess(int exitcode)
   113 {
   114 #ifdef __WIN32__
   115     ExitProcess(exitcode);
   116 #else
   117     _exit(exitcode);
   118 #endif
   119 }
   120 
   121 static void SDL_AbortAssertion(void)
   122 {
   123     SDL_Quit();
   124     SDL_ExitProcess(42);
   125 }
   126 
   127 
   128 static SDL_assert_state
   129 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
   130 {
   131     const char *envr;
   132     SDL_assert_state state = SDL_ASSERTION_ABORT;
   133     SDL_Window *window;
   134     SDL_MessageBoxData messagebox;
   135     SDL_MessageBoxButtonData buttons[] = {
   136         {   0,  SDL_ASSERTION_RETRY,            "Retry" },
   137         {   0,  SDL_ASSERTION_BREAK,            "Break" },
   138         {   0,  SDL_ASSERTION_ABORT,            "Abort" },
   139         {   SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
   140                 SDL_ASSERTION_IGNORE,           "Ignore" },
   141         {   SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
   142                 SDL_ASSERTION_ALWAYS_IGNORE,    "Always Ignore" }
   143     };
   144     char *message;
   145     int selected;
   146 
   147     (void) userdata;  /* unused in default handler. */
   148 
   149     message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   150     if (!message) {
   151         /* Uh oh, we're in real trouble now... */
   152         return SDL_ASSERTION_ABORT;
   153     }
   154     SDL_snprintf(message, SDL_MAX_LOG_MESSAGE,
   155                  "Assertion failure at %s (%s:%d), triggered %u %s:\r\n  '%s'",
   156                  data->function, data->filename, data->linenum,
   157                  data->trigger_count, (data->trigger_count == 1) ? "time" : "times",
   158                  data->condition);
   159 
   160     debug_print("\n\n%s\n\n", message);
   161 
   162     /* let env. variable override, so unit tests won't block in a GUI. */
   163     envr = SDL_getenv("SDL_ASSERT");
   164     if (envr != NULL) {
   165         SDL_stack_free(message);
   166 
   167         if (SDL_strcmp(envr, "abort") == 0) {
   168             return SDL_ASSERTION_ABORT;
   169         } else if (SDL_strcmp(envr, "break") == 0) {
   170             return SDL_ASSERTION_BREAK;
   171         } else if (SDL_strcmp(envr, "retry") == 0) {
   172             return SDL_ASSERTION_RETRY;
   173         } else if (SDL_strcmp(envr, "ignore") == 0) {
   174             return SDL_ASSERTION_IGNORE;
   175         } else if (SDL_strcmp(envr, "always_ignore") == 0) {
   176             return SDL_ASSERTION_ALWAYS_IGNORE;
   177         } else {
   178             return SDL_ASSERTION_ABORT;  /* oh well. */
   179         }
   180     }
   181 
   182     /* Leave fullscreen mode, if possible (scary!) */
   183     window = SDL_GetFocusWindow();
   184     if (window) {
   185         if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) {
   186             SDL_MinimizeWindow(window);
   187         } else {
   188             /* !!! FIXME: ungrab the input if we're not fullscreen? */
   189             /* No need to mess with the window */
   190             window = NULL;
   191         }
   192     }
   193 
   194     /* Show a messagebox if we can, otherwise fall back to stdio */
   195     SDL_zero(messagebox);
   196     messagebox.flags = SDL_MESSAGEBOX_WARNING;
   197     messagebox.window = window;
   198     messagebox.title = "Assertion Failed";
   199     messagebox.message = message;
   200     messagebox.numbuttons = SDL_arraysize(buttons);
   201     messagebox.buttons = buttons;
   202 
   203     if (SDL_ShowMessageBox(&messagebox, &selected) == 0) {
   204         if (selected == -1) {
   205             state = SDL_ASSERTION_IGNORE;
   206         } else {
   207             state = (SDL_assert_state)selected;
   208         }
   209     }
   210 #ifdef HAVE_STDIO_H
   211     else
   212     {
   213         /* this is a little hacky. */
   214         for ( ; ; ) {
   215             char buf[32];
   216             fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
   217             fflush(stderr);
   218             if (fgets(buf, sizeof (buf), stdin) == NULL) {
   219                 break;
   220             }
   221 
   222             if (SDL_strcmp(buf, "a") == 0) {
   223                 state = SDL_ASSERTION_ABORT;
   224                 break;
   225             } else if (SDL_strcmp(buf, "b") == 0) {
   226                 state = SDL_ASSERTION_BREAK;
   227                 break;
   228             } else if (SDL_strcmp(buf, "r") == 0) {
   229                 state = SDL_ASSERTION_RETRY;
   230                 break;
   231             } else if (SDL_strcmp(buf, "i") == 0) {
   232                 state = SDL_ASSERTION_IGNORE;
   233                 break;
   234             } else if (SDL_strcmp(buf, "A") == 0) {
   235                 state = SDL_ASSERTION_ALWAYS_IGNORE;
   236                 break;
   237             }
   238         }
   239     }
   240 #endif /* HAVE_STDIO_H */
   241 
   242     /* Re-enter fullscreen mode */
   243     if (window) {
   244         SDL_RestoreWindow(window);
   245     }
   246 
   247     SDL_stack_free(message);
   248 
   249     return state;
   250 }
   251 
   252 
   253 SDL_assert_state
   254 SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
   255                     int line)
   256 {
   257     static int assertion_running = 0;
   258     static SDL_SpinLock spinlock = 0;
   259     SDL_assert_state state = SDL_ASSERTION_IGNORE;
   260 
   261     SDL_AtomicLock(&spinlock);
   262     if (assertion_mutex == NULL) { /* never called SDL_Init()? */
   263         assertion_mutex = SDL_CreateMutex();
   264         if (assertion_mutex == NULL) {
   265             SDL_AtomicUnlock(&spinlock);
   266             return SDL_ASSERTION_IGNORE;   /* oh well, I guess. */
   267         }
   268     }
   269     SDL_AtomicUnlock(&spinlock);
   270 
   271     if (SDL_LockMutex(assertion_mutex) < 0) {
   272         return SDL_ASSERTION_IGNORE;   /* oh well, I guess. */
   273     }
   274 
   275     /* doing this because Visual C is upset over assigning in the macro. */
   276     if (data->trigger_count == 0) {
   277         data->function = func;
   278         data->filename = file;
   279         data->linenum = line;
   280     }
   281 
   282     SDL_AddAssertionToReport(data);
   283 
   284     assertion_running++;
   285     if (assertion_running > 1) {   /* assert during assert! Abort. */
   286         if (assertion_running == 2) {
   287             SDL_AbortAssertion();
   288         } else if (assertion_running == 3) {  /* Abort asserted! */
   289             SDL_ExitProcess(42);
   290         } else {
   291             while (1) { /* do nothing but spin; what else can you do?! */ }
   292         }
   293     }
   294 
   295     if (!data->always_ignore) {
   296         state = assertion_handler(data, assertion_userdata);
   297     }
   298 
   299     switch (state)
   300     {
   301         case SDL_ASSERTION_ABORT:
   302             SDL_AbortAssertion();
   303             return SDL_ASSERTION_IGNORE;  /* shouldn't return, but oh well. */
   304 
   305         case SDL_ASSERTION_ALWAYS_IGNORE:
   306             state = SDL_ASSERTION_IGNORE;
   307             data->always_ignore = 1;
   308             break;
   309 
   310         case SDL_ASSERTION_IGNORE:
   311         case SDL_ASSERTION_RETRY:
   312         case SDL_ASSERTION_BREAK:
   313             break;  /* macro handles these. */
   314     }
   315 
   316     assertion_running--;
   317     SDL_UnlockMutex(assertion_mutex);
   318 
   319     return state;
   320 }
   321 
   322 
   323 int SDL_AssertionsInit(void)
   324 {
   325     /* this is a no-op at the moment. */
   326     return 0;
   327 }
   328 
   329 void SDL_AssertionsQuit(void)
   330 {
   331     SDL_GenerateAssertionReport();
   332     if (assertion_mutex != NULL) {
   333         SDL_DestroyMutex(assertion_mutex);
   334         assertion_mutex = NULL;
   335     }
   336 }
   337 
   338 void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
   339 {
   340     if (handler != NULL) {
   341         assertion_handler = handler;
   342         assertion_userdata = userdata;
   343     } else {
   344         assertion_handler = SDL_PromptAssertion;
   345         assertion_userdata = NULL;
   346     }
   347 }
   348 
   349 const SDL_assert_data *SDL_GetAssertionReport(void)
   350 {
   351     return triggered_assertions;
   352 }
   353 
   354 void SDL_ResetAssertionReport(void)
   355 {
   356     SDL_assert_data *next = NULL;
   357     SDL_assert_data *item;
   358     for (item = triggered_assertions; item != NULL; item = next) {
   359         next = (SDL_assert_data *) item->next;
   360         item->always_ignore = SDL_FALSE;
   361         item->trigger_count = 0;
   362         item->next = NULL;
   363     }
   364 
   365     triggered_assertions = NULL;
   366 }
   367 
   368 /* vi: set ts=4 sw=4 expandtab: */