src/SDL_assert.c
changeset 3647 c5925cd41955
child 3648 a9d830c05998
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/SDL_assert.c	Wed Jan 13 06:47:17 2010 +0000
     1.3 @@ -0,0 +1,396 @@
     1.4 +/*
     1.5 +    SDL - Simple DirectMedia Layer
     1.6 +    Copyright (C) 1997-2009 Sam Lantinga
     1.7 +
     1.8 +    This library is free software; you can redistribute it and/or
     1.9 +    modify it under the terms of the GNU Lesser General Public
    1.10 +    License as published by the Free Software Foundation; either
    1.11 +    version 2.1 of the License, or (at your option) any later version.
    1.12 +
    1.13 +    This library is distributed in the hope that it will be useful,
    1.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.16 +    Lesser General Public License for more details.
    1.17 +
    1.18 +    You should have received a copy of the GNU Lesser General Public
    1.19 +    License along with this library; if not, write to the Free Software
    1.20 +    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    1.21 +
    1.22 +    Sam Lantinga
    1.23 +    slouken@libsdl.org
    1.24 +*/
    1.25 +
    1.26 +#include "SDL_assert.h"
    1.27 +#include "SDL.h"
    1.28 +
    1.29 +#if (SDL_ASSERT_LEVEL > 0)
    1.30 +
    1.31 +#ifdef _WINDOWS
    1.32 +#define WIN32_LEAN_AND_MEAN 1
    1.33 +#include <windows.h>
    1.34 +#else  /* fprintf, _exit(), etc. */
    1.35 +#include <stdio.h>
    1.36 +#include <stdlib.h>
    1.37 +#endif
    1.38 +
    1.39 +/* We can keep all triggered assertions in a singly-linked list so we can
    1.40 + *  generate a report later.
    1.41 + */
    1.42 +#if !SDL_ASSERTION_REPORT_DISABLED
    1.43 +static SDL_assert_data assertion_list_terminator = { 0, 0, 0, 0, 0, 0, 0 };
    1.44 +static SDL_assert_data *triggered_assertions = &assertion_list_terminator;
    1.45 +#endif
    1.46 +
    1.47 +static void 
    1.48 +debug_print(const char *fmt, ...)
    1.49 +//#ifdef __GNUC__
    1.50 +//__attribute__((format (printf, 1, 2)))
    1.51 +//#endif
    1.52 +{
    1.53 +#ifdef _WINDOWS
    1.54 +    /* Format into a buffer for OutputDebugStringA(). */
    1.55 +    char buf[1024];
    1.56 +    char *startptr;
    1.57 +    char *ptr;
    1.58 +    int len;
    1.59 +    va_list ap;
    1.60 +    va_start(ap, fmt);
    1.61 +    len = (int) SDL_vsnprintf(buf, sizeof (buf), fmt, ap);
    1.62 +    va_end(ap);
    1.63 +
    1.64 +    /* Visual C's vsnprintf() may not null-terminate the buffer. */
    1.65 +    if ((len >= sizeof (buf)) || (len < 0)) {
    1.66 +        buf[sizeof (buf) - 1] = '\0';
    1.67 +    }
    1.68 +
    1.69 +    /* Write it, sorting out the Unix newlines... */
    1.70 +    startptr = buf;
    1.71 +    for (ptr = startptr; *ptr; ptr++) {
    1.72 +        if (*ptr == '\n') {
    1.73 +            *ptr = '\0';
    1.74 +            OutputDebugStringA(startptr);
    1.75 +            OutputDebugStringA("\r\n");
    1.76 +            startptr = ptr+1;
    1.77 +        }
    1.78 +    }
    1.79 +
    1.80 +    /* catch that last piece if it didn't have a newline... */
    1.81 +    if (startptr != ptr) {
    1.82 +        OutputDebugStringA(startptr);
    1.83 +    }
    1.84 +#else
    1.85 +    /* Unix has it easy. Just dump it to stderr. */
    1.86 +    va_list ap;
    1.87 +    va_start(ap, fmt);
    1.88 +    fprintf(stderr, fmt, ap);
    1.89 +    va_end(ap);
    1.90 +    fflush(stderr);
    1.91 +#endif
    1.92 +}
    1.93 +
    1.94 +
    1.95 +#ifdef _WINDOWS
    1.96 +static SDL_assert_state SDL_Windows_AssertChoice = SDL_ASSERTION_ABORT;
    1.97 +static const SDL_assert_data *SDL_Windows_AssertData = NULL;
    1.98 +
    1.99 +static LRESULT CALLBACK
   1.100 +SDL_Assertion_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
   1.101 +{
   1.102 +    switch (msg)
   1.103 +    {
   1.104 +        case WM_CREATE:
   1.105 +        {
   1.106 +            /* !!! FIXME: all this code stinks. */
   1.107 +            const SDL_assert_data *data = SDL_Windows_AssertData;
   1.108 +            char buf[1024];
   1.109 +            const int w = 100;
   1.110 +            const int h = 25;
   1.111 +            const int gap = 10;
   1.112 +            int x = gap;
   1.113 +            int y = 50;
   1.114 +            int len;
   1.115 +            int i;
   1.116 +            static const struct { 
   1.117 +                const char *name;
   1.118 +                SDL_assert_state state;
   1.119 +            } buttons[] = {
   1.120 +                {"Abort", SDL_ASSERTION_ABORT },
   1.121 +                {"Break", SDL_ASSERTION_BREAK },
   1.122 +                {"Retry", SDL_ASSERTION_RETRY },
   1.123 +                {"Ignore", SDL_ASSERTION_IGNORE },
   1.124 +                {"Always Ignore", SDL_ASSERTION_ALWAYS_IGNORE },
   1.125 +            };
   1.126 +
   1.127 +            len = (int) SDL_snprintf(buf, sizeof (buf), 
   1.128 +                         "Assertion failure at %s (%s:%d), triggered %u time%s:\r\n  '%s'",
   1.129 +                         data->function, data->filename, data->linenum,
   1.130 +                         data->trigger_count, (data->trigger_count == 1) ? "" : "s",
   1.131 +                         data->condition);
   1.132 +            if ((len < 0) || (len >= sizeof (buf))) {
   1.133 +                buf[sizeof (buf) - 1] = '\0';
   1.134 +            }
   1.135 +
   1.136 +            CreateWindowA("STATIC", buf,
   1.137 +                         WS_VISIBLE | WS_CHILD | SS_LEFT,
   1.138 +                         x, y, 550, 100,
   1.139 +                         hwnd, (HMENU) 1, NULL, NULL);
   1.140 +            y += 110;
   1.141 +
   1.142 +            for (i = 0; i < (sizeof (buttons) / sizeof (buttons[0])); i++) {
   1.143 +                CreateWindowA("BUTTON", buttons[i].name,
   1.144 +                         WS_VISIBLE | WS_CHILD,
   1.145 +                         x, y, w, h,
   1.146 +                         hwnd, (HMENU) buttons[i].state, NULL, NULL);
   1.147 +                x += w + gap;
   1.148 +            }
   1.149 +            break;
   1.150 +        }
   1.151 +
   1.152 +        case WM_COMMAND:
   1.153 +            SDL_Windows_AssertChoice = ((SDL_assert_state) (LOWORD(wParam)));
   1.154 +            SDL_Windows_AssertData = NULL;
   1.155 +            break;
   1.156 +
   1.157 +        case WM_DESTROY:
   1.158 +            SDL_Windows_AssertData = NULL;
   1.159 +            break;
   1.160 +    }
   1.161 +
   1.162 +    return DefWindowProc(hwnd, msg, wParam, lParam);
   1.163 +}
   1.164 +
   1.165 +static SDL_assert_state
   1.166 +SDL_PromptAssertion_windows(const SDL_assert_data *data)
   1.167 +{
   1.168 +    HINSTANCE hInstance = 0;  /* !!! FIXME? */
   1.169 +    HWND hwnd;
   1.170 +    MSG msg;
   1.171 +    WNDCLASS wc = {0};
   1.172 +
   1.173 +    SDL_Windows_AssertChoice = SDL_ASSERTION_ABORT;
   1.174 +    SDL_Windows_AssertData = data;
   1.175 +
   1.176 +    wc.lpszClassName = TEXT("SDL_assert");
   1.177 +    wc.hInstance = hInstance ;
   1.178 +    wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
   1.179 +    wc.lpfnWndProc = SDL_Assertion_WndProc;
   1.180 +    wc.hCursor = LoadCursor(0, IDC_ARROW);
   1.181 +  
   1.182 +    RegisterClass(&wc);
   1.183 +    hwnd = CreateWindow(wc.lpszClassName, TEXT("SDL assertion failure"),
   1.184 +                 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
   1.185 +                 150, 150, 570, 260, 0, 0, hInstance, 0);  
   1.186 +
   1.187 +    while (GetMessage(&msg, NULL, 0, 0) && (SDL_Windows_AssertData != NULL)) {
   1.188 +        TranslateMessage(&msg);
   1.189 +        DispatchMessage(&msg);
   1.190 +    }
   1.191 +
   1.192 +    DestroyWindow(hwnd);
   1.193 +    UnregisterClass(wc.lpszClassName, hInstance);
   1.194 +    return SDL_Windows_AssertChoice;
   1.195 +}
   1.196 +#endif
   1.197 +
   1.198 +
   1.199 +static void SDL_AddAssertionToReport(SDL_assert_data *data)
   1.200 +{
   1.201 +#if !SDL_ASSERTION_REPORT_DISABLED
   1.202 +    /* (data) is always a static struct defined with the assert macros, so
   1.203 +       we don't have to worry about copying or allocating them. */
   1.204 +    if (data->next == NULL) {  /* not yet added? */
   1.205 +        data->next = triggered_assertions;
   1.206 +        triggered_assertions = data;
   1.207 +    }
   1.208 +#endif
   1.209 +}
   1.210 +
   1.211 +static void SDL_GenerateAssertionReport(void)
   1.212 +{
   1.213 +#if !SDL_ASSERTION_REPORT_DISABLED
   1.214 +    if (triggered_assertions != &assertion_list_terminator)
   1.215 +    {
   1.216 +        SDL_assert_data *item = triggered_assertions;
   1.217 +
   1.218 +        debug_print("\n\nSDL assertion report.\n");
   1.219 +        debug_print("All SDL assertions between last init/quit:\n\n");
   1.220 +
   1.221 +        while (item != &assertion_list_terminator) {
   1.222 +            debug_print(
   1.223 +                "'%s'\n"
   1.224 +                "    * %s (%s:%d)\n"
   1.225 +                "    * triggered %u time%s.\n"
   1.226 +                "    * always ignore: %s.\n",
   1.227 +                item->condition, item->function, item->filename,
   1.228 +                item->linenum, item->trigger_count,
   1.229 +                (item->trigger_count == 1) ? "" : "s",
   1.230 +                item->always_ignore ? "yes" : "no");
   1.231 +            item = item->next;
   1.232 +        }
   1.233 +        debug_print("\n");
   1.234 +
   1.235 +        triggered_assertions = &assertion_list_terminator;
   1.236 +    }
   1.237 +#endif
   1.238 +}
   1.239 +
   1.240 +
   1.241 +static void SDL_AbortAssertion(void)
   1.242 +{
   1.243 +    SDL_Quit();
   1.244 +#ifdef _WINDOWS
   1.245 +    ExitProcess(42);
   1.246 +#elif unix || __APPLE__
   1.247 +    _exit(42);
   1.248 +#else
   1.249 +    #error Please define your platform or set SDL_ASSERT_LEVEL to 0.
   1.250 +#endif
   1.251 +}
   1.252 +    
   1.253 +
   1.254 +static SDL_assert_state SDL_PromptAssertion(const SDL_assert_data *data)
   1.255 +{
   1.256 +    const char *envr;
   1.257 +
   1.258 +    debug_print("\n\n"
   1.259 +                "Assertion failure at %s (%s:%d), triggered %u time%s:\n"
   1.260 +                "  '%s'\n"
   1.261 +                "\n",
   1.262 +                data->function, data->filename, data->linenum,
   1.263 +                data->trigger_count, (data->trigger_count == 1) ? "" : "s",
   1.264 +                data->condition);
   1.265 +
   1.266 +	/* let env. variable override, so unit tests won't block in a GUI. */
   1.267 +    envr = SDL_getenv("SDL_ASSERT");
   1.268 +    if (envr != NULL) {
   1.269 +        if (SDL_strcmp(envr, "abort") == 0) {
   1.270 +            return SDL_ASSERTION_ABORT;
   1.271 +        } else if (SDL_strcmp(envr, "break") == 0) {
   1.272 +            return SDL_ASSERTION_BREAK;
   1.273 +        } else if (SDL_strcmp(envr, "retry") == 0) {
   1.274 +            return SDL_ASSERTION_RETRY;
   1.275 +        } else if (SDL_strcmp(envr, "ignore") == 0) {
   1.276 +            return SDL_ASSERTION_IGNORE;
   1.277 +        } else if (SDL_strcmp(envr, "always_ignore") == 0) {
   1.278 +            return SDL_ASSERTION_ALWAYS_IGNORE;
   1.279 +        } else {
   1.280 +            return SDL_ASSERTION_ABORT;  /* oh well. */
   1.281 +        }
   1.282 +    }
   1.283 +
   1.284 +    /* platform-specific UI... */
   1.285 +
   1.286 +#ifdef _WINDOWS
   1.287 +    return SDL_PromptAssertion_windows(data);
   1.288 +
   1.289 +#elif __APPLE__
   1.290 +    /* This has to be done in an Objective-C (*.m) file, so we call out. */
   1.291 +    extern SDL_assert_state SDL_PromptAssertion_cocoa(const SDL_assert_data *);
   1.292 +    return SDL_PromptAssertion_cocoa(data);
   1.293 +
   1.294 +#elif unix
   1.295 +    /* this is a little hacky. */
   1.296 +    for ( ; ; ) {
   1.297 +        char buf[32];
   1.298 +        fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
   1.299 +        fflush(stderr);
   1.300 +        if (fgets(buf, sizeof (buf), stdin) == NULL) {
   1.301 +            return SDL_ASSERTION_ABORT;
   1.302 +        }
   1.303 +
   1.304 +        if (SDL_strcmp(buf, "a") == 0) {
   1.305 +            return SDL_ASSERTION_ABORT;
   1.306 +        } else if (SDL_strcmp(envr, "b") == 0) {
   1.307 +            return SDL_ASSERTION_BREAK;
   1.308 +        } else if (SDL_strcmp(envr, "r") == 0) {
   1.309 +            return SDL_ASSERTION_RETRY;
   1.310 +        } else if (SDL_strcmp(envr, "i") == 0) {
   1.311 +            return SDL_ASSERTION_IGNORE;
   1.312 +        } else if (SDL_strcmp(envr, "A") == 0) {
   1.313 +            return SDL_ASSERTION_ALWAYS_IGNORE;
   1.314 +        }
   1.315 +    }
   1.316 +
   1.317 +#else
   1.318 +    #error Please define your platform or set SDL_ASSERT_LEVEL to 0.
   1.319 +#endif
   1.320 +
   1.321 +    return SDL_ASSERTION_ABORT;
   1.322 +}
   1.323 +
   1.324 +
   1.325 +static SDL_mutex *assertion_mutex = NULL;
   1.326 +
   1.327 +SDL_assert_state
   1.328 +SDL_ReportAssertion(SDL_assert_data *data, const char *func, int line)
   1.329 +{
   1.330 +    SDL_assert_state state;
   1.331 +
   1.332 +    if (SDL_LockMutex(assertion_mutex) < 0) {
   1.333 +        return SDL_ASSERTION_IGNORE;   /* oh well, I guess. */
   1.334 +    }
   1.335 +
   1.336 +    /* doing this because Visual C is upset over assigning in the macro. */
   1.337 +    if (data->trigger_count == 0) {
   1.338 +        data->function = func;
   1.339 +		data->linenum = line;
   1.340 +    }
   1.341 +
   1.342 +    SDL_AddAssertionToReport(data);
   1.343 +
   1.344 +    data->trigger_count++;
   1.345 +    if (data->always_ignore) {
   1.346 +        SDL_UnlockMutex(assertion_mutex);
   1.347 +        return SDL_ASSERTION_IGNORE;
   1.348 +    }
   1.349 +
   1.350 +    state = SDL_PromptAssertion(data);
   1.351 +
   1.352 +    switch (state)
   1.353 +    {
   1.354 +        case SDL_ASSERTION_ABORT:
   1.355 +            SDL_UnlockMutex(assertion_mutex);  /* in case we assert in quit. */
   1.356 +            SDL_AbortAssertion();
   1.357 +            return SDL_ASSERTION_IGNORE;  /* shouldn't return, but oh well. */
   1.358 +
   1.359 +        case SDL_ASSERTION_ALWAYS_IGNORE:
   1.360 +            state = SDL_ASSERTION_IGNORE;
   1.361 +            data->always_ignore = 1;
   1.362 +            break;
   1.363 +
   1.364 +        case SDL_ASSERTION_IGNORE:
   1.365 +        case SDL_ASSERTION_RETRY:
   1.366 +        case SDL_ASSERTION_BREAK:
   1.367 +            break;  /* macro handles these. */
   1.368 +    }
   1.369 +
   1.370 +    SDL_UnlockMutex(assertion_mutex);
   1.371 +
   1.372 +    return state;
   1.373 +}
   1.374 +
   1.375 +#endif  /* SDL_ASSERT_LEVEL > 0 */
   1.376 +
   1.377 +
   1.378 +int SDL_AssertionsInit(void)
   1.379 +{
   1.380 +#if (SDL_ASSERT_LEVEL > 0)
   1.381 +    assertion_mutex = SDL_CreateMutex();
   1.382 +    if (assertion_mutex == NULL) {
   1.383 +        return -1;
   1.384 +    }
   1.385 +#endif
   1.386 +    return 0;
   1.387 +}
   1.388 +
   1.389 +void SDL_AssertionsQuit(void)
   1.390 +{
   1.391 +#if (SDL_ASSERT_LEVEL > 0)
   1.392 +    SDL_GenerateAssertionReport();
   1.393 +    SDL_DestroyMutex(assertion_mutex);
   1.394 +    assertion_mutex = NULL;
   1.395 +#endif
   1.396 +}
   1.397 +
   1.398 +/* vi: set ts=4 sw=4 expandtab: */
   1.399 +