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