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