SDL  2.0
SDL_assert.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2019 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__)
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 
53 SDL_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  */
60 
61 #ifndef SDL_THREADS_DISABLED
63 #endif
64 
66 static void *assertion_userdata = NULL;
67 
68 #ifdef __GNUC__
69 static void
70 debug_print(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
71 #endif
72 
73 static void
74 debug_print(const char *fmt, ...)
75 {
76  va_list ap;
77  va_start(ap, fmt);
79  va_end(ap);
80 }
81 
82 
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;
91  }
92 }
93 
94 
95 static void SDL_GenerateAssertionReport(void)
96 {
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 
119  }
120 }
121 
122 
123 #if defined(__WATCOMC__)
124 #pragma aux SDL_ExitProcess aborts;
125 #endif
126 static SDL_NORETURN 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  /* MingW doesn't have TerminateProcess marked as noreturn, so add an
134  ExitProcess here that will never be reached but make MingW happy. */
135  ExitProcess(exitcode);
136 #elif defined(__EMSCRIPTEN__)
137  emscripten_cancel_main_loop(); /* this should "kill" the app. */
138  emscripten_force_exit(exitcode); /* this should "kill" the app. */
139  exit(exitcode);
140 #elif defined(__HAIKU__) /* Haiku has _Exit, but it's not marked noreturn. */
141  _exit(exitcode);
142 #elif defined(HAVE__EXIT) /* Upper case _Exit() */
143  _Exit(exitcode);
144 #else
145  _exit(exitcode);
146 #endif
147 }
148 
149 
150 #if defined(__WATCOMC__)
151 #pragma aux SDL_AbortAssertion aborts;
152 #endif
154 {
155  SDL_Quit();
156  SDL_ExitProcess(42);
157 }
158 
159 
161 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
162 {
163 #ifdef __WIN32__
164  #define ENDLINE "\r\n"
165 #else
166  #define ENDLINE "\n"
167 #endif
168 
169  const char *envr;
172  SDL_MessageBoxData messagebox;
173  SDL_MessageBoxButtonData buttons[] = {
174  { 0, SDL_ASSERTION_RETRY, "Retry" },
175  { 0, SDL_ASSERTION_BREAK, "Break" },
176  { 0, SDL_ASSERTION_ABORT, "Abort" },
178  SDL_ASSERTION_IGNORE, "Ignore" },
180  SDL_ASSERTION_ALWAYS_IGNORE, "Always Ignore" }
181  };
182  char *message;
183  int selected;
184 
185  (void) userdata; /* unused in default handler. */
186 
187  /* !!! FIXME: why is this using SDL_stack_alloc and not just "char message[SDL_MAX_LOG_MESSAGE];" ? */
189  if (!message) {
190  /* Uh oh, we're in real trouble now... */
191  return SDL_ASSERTION_ABORT;
192  }
194  "Assertion failure at %s (%s:%d), triggered %u %s:" ENDLINE
195  " '%s'",
196  data->function, data->filename, data->linenum,
197  data->trigger_count, (data->trigger_count == 1) ? "time" : "times",
198  data->condition);
199 
200  debug_print("\n\n%s\n\n", message);
201 
202  /* let env. variable override, so unit tests won't block in a GUI. */
203  envr = SDL_getenv("SDL_ASSERT");
204  if (envr != NULL) {
206 
207  if (SDL_strcmp(envr, "abort") == 0) {
208  return SDL_ASSERTION_ABORT;
209  } else if (SDL_strcmp(envr, "break") == 0) {
210  return SDL_ASSERTION_BREAK;
211  } else if (SDL_strcmp(envr, "retry") == 0) {
212  return SDL_ASSERTION_RETRY;
213  } else if (SDL_strcmp(envr, "ignore") == 0) {
214  return SDL_ASSERTION_IGNORE;
215  } else if (SDL_strcmp(envr, "always_ignore") == 0) {
217  } else {
218  return SDL_ASSERTION_ABORT; /* oh well. */
219  }
220  }
221 
222  /* Leave fullscreen mode, if possible (scary!) */
224  if (window) {
227  } else {
228  /* !!! FIXME: ungrab the input if we're not fullscreen? */
229  /* No need to mess with the window */
230  window = NULL;
231  }
232  }
233 
234  /* Show a messagebox if we can, otherwise fall back to stdio */
235  SDL_zero(messagebox);
236  messagebox.flags = SDL_MESSAGEBOX_WARNING;
237  messagebox.window = window;
238  messagebox.title = "Assertion Failed";
239  messagebox.message = message;
240  messagebox.numbuttons = SDL_arraysize(buttons);
241  messagebox.buttons = buttons;
242 
243  if (SDL_ShowMessageBox(&messagebox, &selected) == 0) {
244  if (selected == -1) {
246  } else {
247  state = (SDL_assert_state)selected;
248  }
249  }
250 
251  else
252  {
253 #if defined(__EMSCRIPTEN__)
254  /* This is nasty, but we can't block on a custom UI. */
255  for ( ; ; ) {
256  SDL_bool okay = SDL_TRUE;
257  char *buf = (char *) EM_ASM_INT({
258  var str =
259  UTF8ToString($0) + '\n\n' +
260  'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :';
261  var reply = window.prompt(str, "i");
262  if (reply === null) {
263  reply = "i";
264  }
265  return allocate(intArrayFromString(reply), 'i8', ALLOC_NORMAL);
266  }, message);
267 
268  if (SDL_strcmp(buf, "a") == 0) {
270  /* (currently) no break functionality on Emscripten
271  } else if (SDL_strcmp(buf, "b") == 0) {
272  state = SDL_ASSERTION_BREAK; */
273  } else if (SDL_strcmp(buf, "r") == 0) {
275  } else if (SDL_strcmp(buf, "i") == 0) {
277  } else if (SDL_strcmp(buf, "A") == 0) {
279  } else {
280  okay = SDL_FALSE;
281  }
282  free(buf);
283 
284  if (okay) {
285  break;
286  }
287  }
288 #elif defined(HAVE_STDIO_H)
289  /* this is a little hacky. */
290  for ( ; ; ) {
291  char buf[32];
292  fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
293  fflush(stderr);
294  if (fgets(buf, sizeof (buf), stdin) == NULL) {
295  break;
296  }
297 
298  if (SDL_strncmp(buf, "a", 1) == 0) {
300  break;
301  } else if (SDL_strncmp(buf, "b", 1) == 0) {
303  break;
304  } else if (SDL_strncmp(buf, "r", 1) == 0) {
306  break;
307  } else if (SDL_strncmp(buf, "i", 1) == 0) {
309  break;
310  } else if (SDL_strncmp(buf, "A", 1) == 0) {
312  break;
313  }
314  }
315 #endif /* HAVE_STDIO_H */
316  }
317 
318  /* Re-enter fullscreen mode */
319  if (window) {
321  }
322 
324 
325  return state;
326 }
327 
328 
330 SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
331  int line)
332 {
334  static int assertion_running = 0;
335 
336 #ifndef SDL_THREADS_DISABLED
337  static SDL_SpinLock spinlock = 0;
338  SDL_AtomicLock(&spinlock);
339  if (assertion_mutex == NULL) { /* never called SDL_Init()? */
341  if (assertion_mutex == NULL) {
342  SDL_AtomicUnlock(&spinlock);
343  return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
344  }
345  }
346  SDL_AtomicUnlock(&spinlock);
347 
348  if (SDL_LockMutex(assertion_mutex) < 0) {
349  return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
350  }
351 #endif
352 
353  /* doing this because Visual C is upset over assigning in the macro. */
354  if (data->trigger_count == 0) {
355  data->function = func;
356  data->filename = file;
357  data->linenum = line;
358  }
359 
361 
362  assertion_running++;
363  if (assertion_running > 1) { /* assert during assert! Abort. */
364  if (assertion_running == 2) {
366  } else if (assertion_running == 3) { /* Abort asserted! */
367  SDL_ExitProcess(42);
368  } else {
369  while (1) { /* do nothing but spin; what else can you do?! */ }
370  }
371  }
372 
373  if (!data->always_ignore) {
375  }
376 
377  switch (state)
378  {
381  data->always_ignore = 1;
382  break;
383 
385  case SDL_ASSERTION_RETRY:
386  case SDL_ASSERTION_BREAK:
387  break; /* macro handles these. */
388 
389  case SDL_ASSERTION_ABORT:
391  /*break; ...shouldn't return, but oh well. */
392  }
393 
394  assertion_running--;
395 
396 #ifndef SDL_THREADS_DISABLED
398 #endif
399 
400  return state;
401 }
402 
403 
405 {
407 #ifndef SDL_THREADS_DISABLED
408  if (assertion_mutex != NULL) {
411  }
412 #endif
413 }
414 
415 void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
416 {
417  if (handler != NULL) {
418  assertion_handler = handler;
419  assertion_userdata = userdata;
420  } else {
423  }
424 }
425 
427 {
428  return triggered_assertions;
429 }
430 
432 {
433  SDL_assert_data *next = NULL;
434  SDL_assert_data *item;
435  for (item = triggered_assertions; item != NULL; item = next) {
436  next = (SDL_assert_data *) item->next;
437  item->always_ignore = SDL_FALSE;
438  item->trigger_count = 0;
439  item->next = NULL;
440  }
441 
443 }
444 
446 {
447  return SDL_PromptAssertion;
448 }
449 
451 {
452  if (userdata != NULL) {
453  *userdata = assertion_userdata;
454  }
455  return assertion_handler;
456 }
457 
458 /* vi: set ts=4 sw=4 expandtab: */
SDL.h
SDL_zero
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
format
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: SDL_opengl.h:1572
SDL_LogMessageV
#define SDL_LogMessageV
Definition: SDL_dynapi_overrides.h:239
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
@ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
Definition: SDL_messagebox.h:50
SDL_ASSERTION_IGNORE
@ SDL_ASSERTION_IGNORE
Definition: SDL_assert.h:107
debug_print
static void debug_print(const char *fmt,...)
Definition: SDL_assert.c:74
assertion_userdata
static void * assertion_userdata
Definition: SDL_assert.c:66
SDL_LockMutex
#define SDL_LockMutex
Definition: SDL_dynapi_overrides.h:260
SDL_atomic.h
NULL
#define NULL
Definition: begin_code.h:167
message
GLuint GLsizei const GLchar * message
Definition: SDL_opengl_glext.h:2483
SDL_MessageBoxData::title
const char * title
Definition: SDL_messagebox.h:96
SDL_ASSERTION_BREAK
@ SDL_ASSERTION_BREAK
Definition: SDL_assert.h:105
SDL_SetAssertionHandler
void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
Set an application-defined assertion handler.
Definition: SDL_assert.c:415
SDL_AtomicLock
#define SDL_AtomicLock
Definition: SDL_dynapi_overrides.h:64
SDL_mutex
Definition: SDL_sysmutex.c:29
SDL_GetAssertionReport
const SDL_assert_data * SDL_GetAssertionReport(void)
Get a list of all assertion failures.
Definition: SDL_assert.c:426
assertion_mutex
static SDL_mutex * assertion_mutex
Definition: SDL_assert.c:62
SDL_GetWindowFlags
#define SDL_GetWindowFlags
Definition: SDL_dynapi_overrides.h:518
SDL_LOG_CATEGORY_ASSERT
@ SDL_LOG_CATEGORY_ASSERT
Definition: SDL_log.h:68
SDL_ReportAssertion
SDL_assert_state SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file, int line)
Definition: SDL_assert.c:330
SDLCALL
#define SDLCALL
Definition: SDL_internal.h:49
SDL_WINDOW_FULLSCREEN
@ SDL_WINDOW_FULLSCREEN
Definition: SDL_video.h:100
SDL_messagebox.h
SDL_assert_state
#define SDL_assert_state
Definition: SDL_assert.h:279
SDL_assert_data
#define SDL_assert_data
Definition: SDL_assert.h:280
SDL_AddAssertionToReport
static void SDL_AddAssertionToReport(SDL_assert_data *data)
Definition: SDL_assert.c:83
SDL_CreateMutex
#define SDL_CreateMutex
Definition: SDL_dynapi_overrides.h:259
SDL_MESSAGEBOX_WARNING
@ SDL_MESSAGEBOX_WARNING
Definition: SDL_messagebox.h:40
SDL_MessageBoxData::message
const char * message
Definition: SDL_messagebox.h:97
SDL_GenerateAssertionReport
static void SDL_GenerateAssertionReport(void)
Definition: SDL_assert.c:95
SDL_strncmp
#define SDL_strncmp
Definition: SDL_dynapi_overrides.h:418
SDL_assert_c.h
SDL_LOG_PRIORITY_WARN
@ SDL_LOG_PRIORITY_WARN
Definition: SDL_log.h:107
func
GLenum func
Definition: SDL_opengl_glext.h:657
SDL_ExitProcess
static SDL_NORETURN void SDL_ExitProcess(int exitcode)
Definition: SDL_assert.c:126
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
SDL_Window
The type used to identify a window.
Definition: SDL_sysvideo.h:73
SDL_stack_alloc
#define SDL_stack_alloc(type, count)
Definition: SDL_stdinc.h:354
SDL_ResetAssertionReport
void SDL_ResetAssertionReport(void)
Reset the list of all assertion failures.
Definition: SDL_assert.c:431
SDL_GetFocusWindow
SDL_Window * SDL_GetFocusWindow(void)
Definition: SDL_video.c:2699
SDL_MinimizeWindow
#define SDL_MinimizeWindow
Definition: SDL_dynapi_overrides.h:537
SDL_PromptAssertion
static SDL_assert_state SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
Definition: SDL_assert.c:161
buf
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: SDL_opengl_glext.h:2480
SDL_FALSE
@ SDL_FALSE
Definition: SDL_stdinc.h:163
SDL_AtomicUnlock
#define SDL_AtomicUnlock
Definition: SDL_dynapi_overrides.h:65
SDL_internal.h
window
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
SDL_AssertionHandler
SDL_AssertState(* SDL_AssertionHandler)(const SDL_AssertData *data, void *userdata)
Definition: SDL_assert.h:188
SDL_MessageBoxData::flags
Uint32 flags
Definition: SDL_messagebox.h:94
SDL_MessageBoxData
MessageBox structure containing title, text, window, etc.
Definition: SDL_messagebox.h:92
SDL_MessageBoxData::window
SDL_Window * window
Definition: SDL_messagebox.h:95
SDL_assert.h
SDL_Quit
#define SDL_Quit
Definition: SDL_dynapi_overrides.h:58
SDL_MessageBoxButtonData
Individual button data.
Definition: SDL_messagebox.h:56
SDL_AssertionsQuit
void SDL_AssertionsQuit(void)
Definition: SDL_assert.c:404
SDL_RestoreWindow
#define SDL_RestoreWindow
Definition: SDL_dynapi_overrides.h:538
SDL_ASSERTION_ALWAYS_IGNORE
@ SDL_ASSERTION_ALWAYS_IGNORE
Definition: SDL_assert.h:108
SDL_SpinLock
int SDL_SpinLock
Definition: SDL_atomic.h:89
SDL_GetAssertionHandler
SDL_AssertionHandler SDL_GetAssertionHandler(void **userdata)
Get the current assertion handler.
Definition: SDL_assert.c:450
SDL_arraysize
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
SDL_MessageBoxData::buttons
const SDL_MessageBoxButtonData * buttons
Definition: SDL_messagebox.h:100
SDL_getenv
#define SDL_getenv
Definition: SDL_dynapi_overrides.h:378
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
ENDLINE
#define ENDLINE
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT
@ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT
Definition: SDL_messagebox.h:49
SDL_stack_free
#define SDL_stack_free(data)
Definition: SDL_stdinc.h:355
SDL_ShowMessageBox
#define SDL_ShowMessageBox
Definition: SDL_dynapi_overrides.h:243
SDL_GetDefaultAssertionHandler
SDL_AssertionHandler SDL_GetDefaultAssertionHandler(void)
Get the default assertion handler.
Definition: SDL_assert.c:445
SDL_snprintf
#define SDL_snprintf
Definition: SDL_dynapi_overrides.h:40
SDL_NORETURN
#define SDL_NORETURN
Definition: begin_code.h:157
SDL_ASSERTION_ABORT
@ SDL_ASSERTION_ABORT
Definition: SDL_assert.h:106
SDL_DestroyMutex
#define SDL_DestroyMutex
Definition: SDL_dynapi_overrides.h:263
SDL_video.h
SDL_windows.h
SDL_sysvideo.h
SDL_strcmp
#define SDL_strcmp
Definition: SDL_dynapi_overrides.h:417
void
const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char const char const SDL_SCANF_FORMAT_STRING char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
Definition: SDL_dynapi_procs.h:89
SDL_UnlockMutex
#define SDL_UnlockMutex
Definition: SDL_dynapi_overrides.h:262
free
SDL_EventEntry * free
Definition: SDL_events.c:82
state
struct xkb_state * state
Definition: SDL_waylandsym.h:113
SDL_MAX_LOG_MESSAGE
#define SDL_MAX_LOG_MESSAGE
The maximum size of a log message.
Definition: SDL_log.h:54
assertion_handler
static SDL_AssertionHandler assertion_handler
Definition: SDL_assert.c:65
SDL_MessageBoxData::numbuttons
int numbuttons
Definition: SDL_messagebox.h:99
SDL_ASSERTION_RETRY
@ SDL_ASSERTION_RETRY
Definition: SDL_assert.h:104
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:161
triggered_assertions
static SDL_assert_data * triggered_assertions
Definition: SDL_assert.c:59
SDL_AbortAssertion
static SDL_NORETURN void SDL_AbortAssertion(void)
Definition: SDL_assert.c:153