Atlas - SDL_assert.c

Home / ext / SDL2 / src Lines: 19 | Size: 13215 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 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 47#if defined(__EMSCRIPTEN__) 48#include <emscripten.h> 49#endif 50 51 52static SDL_assert_state SDLCALL 53SDL_PromptAssertion(const SDL_assert_data *data, void *userdata); 54 55/* 56 * We keep all triggered assertions in a singly-linked list so we can 57 * generate a report later. 58 */ 59static SDL_assert_data *triggered_assertions = NULL; 60 61#ifndef SDL_THREADS_DISABLED 62static SDL_mutex *assertion_mutex = NULL; 63#endif 64 65static SDL_AssertionHandler assertion_handler = SDL_PromptAssertion; 66static void *assertion_userdata = NULL; 67 68#ifdef __GNUC__ 69static void 70debug_print(const char *fmt, ...) __attribute__((format (printf, 1, 2))); 71#endif 72 73static void 74debug_print(const char *fmt, ...) 75{ 76 va_list ap; 77 va_start(ap, fmt); 78 SDL_LogMessageV(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_WARN, fmt, ap); 79 va_end(ap); 80} 81 82 83static void SDL_AddAssertionToReport(SDL_assert_data *data) 84{ 85 /* (data) is always a static struct defined with the assert macros, so 86 we don't have to worry about copying or allocating them. */ 87 data->trigger_count++; 88 if (data->trigger_count == 1) { /* not yet added? */ 89 data->next = triggered_assertions; 90 triggered_assertions = data; 91 } 92} 93 94 95static void SDL_GenerateAssertionReport(void) 96{ 97 const SDL_assert_data *item = triggered_assertions; 98 99 /* only do this if the app hasn't assigned an assertion handler. */ 100 if ((item != NULL) && (assertion_handler != SDL_PromptAssertion)) { 101 debug_print("\n\nSDL assertion report.\n"); 102 debug_print("All SDL assertions between last init/quit:\n\n"); 103 104 while (item != NULL) { 105 debug_print( 106 "'%s'\n" 107 " * %s (%s:%d)\n" 108 " * triggered %u time%s.\n" 109 " * always ignore: %s.\n", 110 item->condition, item->function, item->filename, 111 item->linenum, item->trigger_count, 112 (item->trigger_count == 1) ? "" : "s", 113 item->always_ignore ? "yes" : "no"); 114 item = item->next; 115 } 116 debug_print("\n"); 117 118 SDL_ResetAssertionReport(); 119 } 120} 121 122 123#if defined(__WATCOMC__) 124#pragma aux SDL_ExitProcess aborts; 125#endif 126static void SDL_ExitProcess(int exitcode) 127{ 128#ifdef __WIN32__ 129 /* "if you do not know the state of all threads in your process, it is 130 better to call TerminateProcess than ExitProcess" 131 https://msdn.microsoft.com/en-us/library/windows/desktop/ms682658(v=vs.85).aspx */ 132 TerminateProcess(GetCurrentProcess(), exitcode); 133 134#elif defined(__EMSCRIPTEN__) 135 emscripten_cancel_main_loop(); /* this should "kill" the app. */ 136 emscripten_force_exit(exitcode); /* this should "kill" the app. */ 137 exit(exitcode); 138#else 139 _exit(exitcode); 140#endif 141} 142 143 144#if defined(__WATCOMC__) 145#pragma aux SDL_AbortAssertion aborts; 146#endif 147static void SDL_AbortAssertion(void) 148{ 149 SDL_Quit(); 150 SDL_ExitProcess(42); 151} 152 153 154static SDL_assert_state SDLCALL 155SDL_PromptAssertion(const SDL_assert_data *data, void *userdata) 156{ 157#ifdef __WIN32__ 158 #define ENDLINE "\r\n" 159#else 160 #define ENDLINE "\n" 161#endif 162 163 const char *envr; 164 SDL_assert_state state = SDL_ASSERTION_ABORT; 165 SDL_Window *window; 166 SDL_MessageBoxData messagebox; 167 SDL_MessageBoxButtonData buttons[] = { 168 { 0, SDL_ASSERTION_RETRY, "Retry" }, 169 { 0, SDL_ASSERTION_BREAK, "Break" }, 170 { 0, SDL_ASSERTION_ABORT, "Abort" }, 171 { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 172 SDL_ASSERTION_IGNORE, "Ignore" }, 173 { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 174 SDL_ASSERTION_ALWAYS_IGNORE, "Always Ignore" } 175 }; 176 char *message; 177 int selected; 178 179 (void) userdata; /* unused in default handler. */ 180 181 message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE); 182 if (!message) { 183 /* Uh oh, we're in real trouble now... */ 184 return SDL_ASSERTION_ABORT; 185 } 186 SDL_snprintf(message, SDL_MAX_LOG_MESSAGE, 187 "Assertion failure at %s (%s:%d), triggered %u %s:" ENDLINE 188 " '%s'", 189 data->function, data->filename, data->linenum, 190 data->trigger_count, (data->trigger_count == 1) ? "time" : "times", 191 data->condition); 192 193 debug_print("\n\n%s\n\n", message); 194 195 /* let env. variable override, so unit tests won't block in a GUI. */ 196 envr = SDL_getenv("SDL_ASSERT"); 197 if (envr != NULL) { 198 SDL_stack_free(message); 199 200 if (SDL_strcmp(envr, "abort") == 0) { 201 return SDL_ASSERTION_ABORT; 202 } else if (SDL_strcmp(envr, "break") == 0) { 203 return SDL_ASSERTION_BREAK; 204 } else if (SDL_strcmp(envr, "retry") == 0) { 205 return SDL_ASSERTION_RETRY; 206 } else if (SDL_strcmp(envr, "ignore") == 0) { 207 return SDL_ASSERTION_IGNORE; 208 } else if (SDL_strcmp(envr, "always_ignore") == 0) { 209 return SDL_ASSERTION_ALWAYS_IGNORE; 210 } else { 211 return SDL_ASSERTION_ABORT; /* oh well. */ 212 } 213 } 214 215 /* Leave fullscreen mode, if possible (scary!) */ 216 window = SDL_GetFocusWindow(); 217 if (window) { 218 if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { 219 SDL_MinimizeWindow(window); 220 } else { 221 /* !!! FIXME: ungrab the input if we're not fullscreen? */ 222 /* No need to mess with the window */ 223 window = NULL; 224 } 225 } 226 227 /* Show a messagebox if we can, otherwise fall back to stdio */ 228 SDL_zero(messagebox); 229 messagebox.flags = SDL_MESSAGEBOX_WARNING; 230 messagebox.window = window; 231 messagebox.title = "Assertion Failed"; 232 messagebox.message = message; 233 messagebox.numbuttons = SDL_arraysize(buttons); 234 messagebox.buttons = buttons; 235 236 if (SDL_ShowMessageBox(&messagebox, &selected) == 0) { 237 if (selected == -1) { 238 state = SDL_ASSERTION_IGNORE; 239 } else { 240 state = (SDL_assert_state)selected; 241 } 242 } 243 244 else 245 { 246#if defined(__EMSCRIPTEN__) 247 /* This is nasty, but we can't block on a custom UI. */ 248 for ( ; ; ) { 249 SDL_bool okay = SDL_TRUE; 250 char *buf = (char *) EM_ASM_INT({ 251 var str = 252 Pointer_stringify($0) + '\n\n' + 253 'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :'; 254 var reply = window.prompt(str, "i"); 255 if (reply === null) { 256 reply = "i"; 257 } 258 return allocate(intArrayFromString(reply), 'i8', ALLOC_NORMAL); 259 }, message); 260 261 if (SDL_strcmp(buf, "a") == 0) { 262 state = SDL_ASSERTION_ABORT; 263 /* (currently) no break functionality on Emscripten 264 } else if (SDL_strcmp(buf, "b") == 0) { 265 state = SDL_ASSERTION_BREAK; */ 266 } else if (SDL_strcmp(buf, "r") == 0) { 267 state = SDL_ASSERTION_RETRY; 268 } else if (SDL_strcmp(buf, "i") == 0) { 269 state = SDL_ASSERTION_IGNORE; 270 } else if (SDL_strcmp(buf, "A") == 0) { 271 state = SDL_ASSERTION_ALWAYS_IGNORE; 272 } else { 273 okay = SDL_FALSE; 274 } 275 free(buf); 276 277 if (okay) { 278 break; 279 } 280 } 281#elif defined(HAVE_STDIO_H) 282 /* this is a little hacky. */ 283 for ( ; ; ) { 284 char buf[32]; 285 fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : "); 286 fflush(stderr); 287 if (fgets(buf, sizeof (buf), stdin) == NULL) { 288 break; 289 } 290 291 if (SDL_strncmp(buf, "a", 1) == 0) { 292 state = SDL_ASSERTION_ABORT; 293 break; 294 } else if (SDL_strncmp(buf, "b", 1) == 0) { 295 state = SDL_ASSERTION_BREAK; 296 break; 297 } else if (SDL_strncmp(buf, "r", 1) == 0) { 298 state = SDL_ASSERTION_RETRY; 299 break; 300 } else if (SDL_strncmp(buf, "i", 1) == 0) { 301 state = SDL_ASSERTION_IGNORE; 302 break; 303 } else if (SDL_strncmp(buf, "A", 1) == 0) { 304 state = SDL_ASSERTION_ALWAYS_IGNORE; 305 break; 306 } 307 } 308#endif /* HAVE_STDIO_H */ 309 } 310 311 /* Re-enter fullscreen mode */ 312 if (window) { 313 SDL_RestoreWindow(window); 314 } 315 316 SDL_stack_free(message); 317 318 return state; 319} 320 321 322SDL_assert_state 323SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file, 324 int line) 325{ 326 SDL_assert_state state = SDL_ASSERTION_IGNORE; 327 static int assertion_running = 0; 328 329#ifndef SDL_THREADS_DISABLED 330 static SDL_SpinLock spinlock = 0; 331 SDL_AtomicLock(&spinlock); 332 if (assertion_mutex == NULL) { /* never called SDL_Init()? */ 333 assertion_mutex = SDL_CreateMutex(); 334 if (assertion_mutex == NULL) { 335 SDL_AtomicUnlock(&spinlock); 336 return SDL_ASSERTION_IGNORE; /* oh well, I guess. */ 337 } 338 } 339 SDL_AtomicUnlock(&spinlock); 340 341 if (SDL_LockMutex(assertion_mutex) < 0) { 342 return SDL_ASSERTION_IGNORE; /* oh well, I guess. */ 343 } 344#endif 345 346 /* doing this because Visual C is upset over assigning in the macro. */ 347 if (data->trigger_count == 0) { 348 data->function = func; 349 data->filename = file; 350 data->linenum = line; 351 } 352 353 SDL_AddAssertionToReport(data); 354 355 assertion_running++; 356 if (assertion_running > 1) { /* assert during assert! Abort. */ 357 if (assertion_running == 2) { 358 SDL_AbortAssertion(); 359 } else if (assertion_running == 3) { /* Abort asserted! */ 360 SDL_ExitProcess(42); 361 } else { 362 while (1) { /* do nothing but spin; what else can you do?! */ } 363 } 364 } 365 366 if (!data->always_ignore) { 367 state = assertion_handler(data, assertion_userdata); 368 } 369 370 switch (state) 371 { 372 case SDL_ASSERTION_ABORT: 373 SDL_AbortAssertion(); 374 return SDL_ASSERTION_IGNORE; /* shouldn't return, but oh well. */ 375 376 case SDL_ASSERTION_ALWAYS_IGNORE: 377 state = SDL_ASSERTION_IGNORE; 378 data->always_ignore = 1; 379 break; 380 381 case SDL_ASSERTION_IGNORE: 382 case SDL_ASSERTION_RETRY: 383 case SDL_ASSERTION_BREAK: 384 break; /* macro handles these. */ 385 } 386 387 assertion_running--; 388 389#ifndef SDL_THREADS_DISABLED 390 SDL_UnlockMutex(assertion_mutex); 391#endif 392 393 return state; 394} 395 396 397void SDL_AssertionsQuit(void) 398{ 399 SDL_GenerateAssertionReport(); 400#ifndef SDL_THREADS_DISABLED 401 if (assertion_mutex != NULL) { 402 SDL_DestroyMutex(assertion_mutex); 403 assertion_mutex = NULL; 404 } 405#endif 406} 407 408void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata) 409{ 410 if (handler != NULL) { 411 assertion_handler = handler; 412 assertion_userdata = userdata; 413 } else { 414 assertion_handler = SDL_PromptAssertion; 415 assertion_userdata = NULL; 416 } 417} 418 419const SDL_assert_data *SDL_GetAssertionReport(void) 420{ 421 return triggered_assertions; 422} 423 424void SDL_ResetAssertionReport(void) 425{ 426 SDL_assert_data *next = NULL; 427 SDL_assert_data *item; 428 for (item = triggered_assertions; item != NULL; item = next) { 429 next = (SDL_assert_data *) item->next; 430 item->always_ignore = SDL_FALSE; 431 item->trigger_count = 0; 432 item->next = NULL; 433 } 434 435 triggered_assertions = NULL; 436} 437 438SDL_AssertionHandler SDL_GetDefaultAssertionHandler(void) 439{ 440 return SDL_PromptAssertion; 441} 442 443SDL_AssertionHandler SDL_GetAssertionHandler(void **userdata) 444{ 445 if (userdata != NULL) { 446 *userdata = assertion_userdata; 447 } 448 return assertion_handler; 449} 450 451/* vi: set ts=4 sw=4 expandtab: */ 452
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.