SDL  2.0
SDL_assert.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2020 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, etc. */
40 #include <stdio.h>
41 #include <stdlib.h>
42 #endif
43 
44 #if defined(__EMSCRIPTEN__)
45 #include <emscripten.h>
46 #endif
47 
48 
50 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata);
51 
52 /*
53  * We keep all triggered assertions in a singly-linked list so we can
54  * generate a report later.
55  */
57 
58 #ifndef SDL_THREADS_DISABLED
60 #endif
61 
63 static void *assertion_userdata = NULL;
64 
65 #ifdef __GNUC__
66 static void
67 debug_print(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
68 #endif
69 
70 static void
71 debug_print(const char *fmt, ...)
72 {
73  va_list ap;
74  va_start(ap, fmt);
76  va_end(ap);
77 }
78 
79 
81 {
82  /* (data) is always a static struct defined with the assert macros, so
83  we don't have to worry about copying or allocating them. */
84  data->trigger_count++;
85  if (data->trigger_count == 1) { /* not yet added? */
86  data->next = triggered_assertions;
88  }
89 }
90 
91 
92 static void SDL_GenerateAssertionReport(void)
93 {
95 
96  /* only do this if the app hasn't assigned an assertion handler. */
97  if ((item != NULL) && (assertion_handler != SDL_PromptAssertion)) {
98  debug_print("\n\nSDL assertion report.\n");
99  debug_print("All SDL assertions between last init/quit:\n\n");
100 
101  while (item != NULL) {
102  debug_print(
103  "'%s'\n"
104  " * %s (%s:%d)\n"
105  " * triggered %u time%s.\n"
106  " * always ignore: %s.\n",
107  item->condition, item->function, item->filename,
108  item->linenum, item->trigger_count,
109  (item->trigger_count == 1) ? "" : "s",
110  item->always_ignore ? "yes" : "no");
111  item = item->next;
112  }
113  debug_print("\n");
114 
116  }
117 }
118 
119 
120 /* This is not declared in any header, although it is shared between some
121  parts of SDL, because we don't want anything calling it without an
122  extremely good reason. */
123 #if defined(__WATCOMC__)
124 extern void SDL_ExitProcess(int exitcode);
125 #pragma aux SDL_ExitProcess aborts;
126 #endif
127 extern SDL_NORETURN void SDL_ExitProcess(int exitcode);
128 
129 
130 #if defined(__WATCOMC__)
131 static void SDL_AbortAssertion (void);
132 #pragma aux SDL_AbortAssertion aborts;
133 #endif
135 {
136  SDL_Quit();
137  SDL_ExitProcess(42);
138 }
139 
140 
142 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
143 {
144 #ifdef __WIN32__
145  #define ENDLINE "\r\n"
146 #else
147  #define ENDLINE "\n"
148 #endif
149 
150  const char *envr;
153  SDL_MessageBoxData messagebox;
154  SDL_MessageBoxButtonData buttons[] = {
155  { 0, SDL_ASSERTION_RETRY, "Retry" },
156  { 0, SDL_ASSERTION_BREAK, "Break" },
157  { 0, SDL_ASSERTION_ABORT, "Abort" },
159  SDL_ASSERTION_IGNORE, "Ignore" },
161  SDL_ASSERTION_ALWAYS_IGNORE, "Always Ignore" }
162  };
163  char *message;
164  int selected;
165 
166  (void) userdata; /* unused in default handler. */
167 
168  /* !!! FIXME: why is this using SDL_stack_alloc and not just "char message[SDL_MAX_LOG_MESSAGE];" ? */
170  if (!message) {
171  /* Uh oh, we're in real trouble now... */
172  return SDL_ASSERTION_ABORT;
173  }
175  "Assertion failure at %s (%s:%d), triggered %u %s:" ENDLINE
176  " '%s'",
177  data->function, data->filename, data->linenum,
178  data->trigger_count, (data->trigger_count == 1) ? "time" : "times",
179  data->condition);
180 
181  debug_print("\n\n%s\n\n", message);
182 
183  /* let env. variable override, so unit tests won't block in a GUI. */
184  envr = SDL_getenv("SDL_ASSERT");
185  if (envr != NULL) {
187 
188  if (SDL_strcmp(envr, "abort") == 0) {
189  return SDL_ASSERTION_ABORT;
190  } else if (SDL_strcmp(envr, "break") == 0) {
191  return SDL_ASSERTION_BREAK;
192  } else if (SDL_strcmp(envr, "retry") == 0) {
193  return SDL_ASSERTION_RETRY;
194  } else if (SDL_strcmp(envr, "ignore") == 0) {
195  return SDL_ASSERTION_IGNORE;
196  } else if (SDL_strcmp(envr, "always_ignore") == 0) {
198  } else {
199  return SDL_ASSERTION_ABORT; /* oh well. */
200  }
201  }
202 
203  /* Leave fullscreen mode, if possible (scary!) */
205  if (window) {
208  } else {
209  /* !!! FIXME: ungrab the input if we're not fullscreen? */
210  /* No need to mess with the window */
211  window = NULL;
212  }
213  }
214 
215  /* Show a messagebox if we can, otherwise fall back to stdio */
216  SDL_zero(messagebox);
217  messagebox.flags = SDL_MESSAGEBOX_WARNING;
218  messagebox.window = window;
219  messagebox.title = "Assertion Failed";
220  messagebox.message = message;
221  messagebox.numbuttons = SDL_arraysize(buttons);
222  messagebox.buttons = buttons;
223 
224  if (SDL_ShowMessageBox(&messagebox, &selected) == 0) {
225  if (selected == -1) {
227  } else {
228  state = (SDL_assert_state)selected;
229  }
230  }
231 
232  else
233  {
234 #if defined(__EMSCRIPTEN__)
235  /* This is nasty, but we can't block on a custom UI. */
236  for ( ; ; ) {
237  SDL_bool okay = SDL_TRUE;
238  char *buf = (char *) EM_ASM_INT({
239  var str =
240  UTF8ToString($0) + '\n\n' +
241  'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :';
242  var reply = window.prompt(str, "i");
243  if (reply === null) {
244  reply = "i";
245  }
246  return allocate(intArrayFromString(reply), 'i8', ALLOC_NORMAL);
247  }, message);
248 
249  if (SDL_strcmp(buf, "a") == 0) {
251  /* (currently) no break functionality on Emscripten
252  } else if (SDL_strcmp(buf, "b") == 0) {
253  state = SDL_ASSERTION_BREAK; */
254  } else if (SDL_strcmp(buf, "r") == 0) {
256  } else if (SDL_strcmp(buf, "i") == 0) {
258  } else if (SDL_strcmp(buf, "A") == 0) {
260  } else {
261  okay = SDL_FALSE;
262  }
263  free(buf);
264 
265  if (okay) {
266  break;
267  }
268  }
269 #elif defined(HAVE_STDIO_H)
270  /* this is a little hacky. */
271  for ( ; ; ) {
272  char buf[32];
273  fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
274  fflush(stderr);
275  if (fgets(buf, sizeof (buf), stdin) == NULL) {
276  break;
277  }
278 
279  if (SDL_strncmp(buf, "a", 1) == 0) {
281  break;
282  } else if (SDL_strncmp(buf, "b", 1) == 0) {
284  break;
285  } else if (SDL_strncmp(buf, "r", 1) == 0) {
287  break;
288  } else if (SDL_strncmp(buf, "i", 1) == 0) {
290  break;
291  } else if (SDL_strncmp(buf, "A", 1) == 0) {
293  break;
294  }
295  }
296 #endif /* HAVE_STDIO_H */
297  }
298 
299  /* Re-enter fullscreen mode */
300  if (window) {
302  }
303 
305 
306  return state;
307 }
308 
309 
311 SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
312  int line)
313 {
315  static int assertion_running = 0;
316 
317 #ifndef SDL_THREADS_DISABLED
318  static SDL_SpinLock spinlock = 0;
319  SDL_AtomicLock(&spinlock);
320  if (assertion_mutex == NULL) { /* never called SDL_Init()? */
322  if (assertion_mutex == NULL) {
323  SDL_AtomicUnlock(&spinlock);
324  return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
325  }
326  }
327  SDL_AtomicUnlock(&spinlock);
328 
329  if (SDL_LockMutex(assertion_mutex) < 0) {
330  return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
331  }
332 #endif
333 
334  /* doing this because Visual C is upset over assigning in the macro. */
335  if (data->trigger_count == 0) {
336  data->function = func;
337  data->filename = file;
338  data->linenum = line;
339  }
340 
342 
343  assertion_running++;
344  if (assertion_running > 1) { /* assert during assert! Abort. */
345  if (assertion_running == 2) {
347  } else if (assertion_running == 3) { /* Abort asserted! */
348  SDL_ExitProcess(42);
349  } else {
350  while (1) { /* do nothing but spin; what else can you do?! */ }
351  }
352  }
353 
354  if (!data->always_ignore) {
356  }
357 
358  switch (state)
359  {
362  data->always_ignore = 1;
363  break;
364 
366  case SDL_ASSERTION_RETRY:
367  case SDL_ASSERTION_BREAK:
368  break; /* macro handles these. */
369 
370  case SDL_ASSERTION_ABORT:
372  /*break; ...shouldn't return, but oh well. */
373  }
374 
375  assertion_running--;
376 
377 #ifndef SDL_THREADS_DISABLED
379 #endif
380 
381  return state;
382 }
383 
384 
386 {
388 #ifndef SDL_THREADS_DISABLED
389  if (assertion_mutex != NULL) {
392  }
393 #endif
394 }
395 
396 void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
397 {
398  if (handler != NULL) {
399  assertion_handler = handler;
400  assertion_userdata = userdata;
401  } else {
404  }
405 }
406 
408 {
409  return triggered_assertions;
410 }
411 
413 {
414  SDL_assert_data *next = NULL;
415  SDL_assert_data *item;
416  for (item = triggered_assertions; item != NULL; item = next) {
417  next = (SDL_assert_data *) item->next;
418  item->always_ignore = SDL_FALSE;
419  item->trigger_count = 0;
420  item->next = NULL;
421  }
422 
424 }
425 
427 {
428  return SDL_PromptAssertion;
429 }
430 
432 {
433  if (userdata != NULL) {
434  *userdata = assertion_userdata;
435  }
436  return assertion_handler;
437 }
438 
439 /* vi: set ts=4 sw=4 expandtab: */
SDL.h
SDL_zero
#define SDL_zero(x)
Definition: SDL_stdinc.h:418
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_ExitProcess
SDL_NORETURN void SDL_ExitProcess(int exitcode)
Definition: SDL.c:60
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
@ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
Definition: SDL_messagebox.h:52
SDL_ASSERTION_IGNORE
@ SDL_ASSERTION_IGNORE
Definition: SDL_assert.h:107
debug_print
static void debug_print(const char *fmt,...)
Definition: SDL_assert.c:71
assertion_userdata
static void * assertion_userdata
Definition: SDL_assert.c:63
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:2486
SDL_MessageBoxData::title
const char * title
Definition: SDL_messagebox.h:98
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:396
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:407
assertion_mutex
static SDL_mutex * assertion_mutex
Definition: SDL_assert.c:59
SDL_GetWindowFlags
#define SDL_GetWindowFlags
Definition: SDL_dynapi_overrides.h:518
SDL_ReportAssertion
SDL_assert_state SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file, int line)
Definition: SDL_assert.c:311
SDLCALL
#define SDLCALL
Definition: SDL_internal.h:49
SDL_WINDOW_FULLSCREEN
@ SDL_WINDOW_FULLSCREEN
Definition: SDL_video.h:99
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:80
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:99
SDL_GenerateAssertionReport
static void SDL_GenerateAssertionReport(void)
Definition: SDL_assert.c:92
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:660
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:74
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:412
SDL_GetFocusWindow
SDL_Window * SDL_GetFocusWindow(void)
Definition: SDL_video.c:2717
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:142
SDL_LOG_CATEGORY_ASSERT
@ SDL_LOG_CATEGORY_ASSERT
Definition: SDL_log.h:68
buf
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: SDL_opengl_glext.h:2483
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:96
SDL_MessageBoxData
MessageBox structure containing title, text, window, etc.
Definition: SDL_messagebox.h:94
SDL_MessageBoxData::window
SDL_Window * window
Definition: SDL_messagebox.h:97
SDL_assert.h
SDL_Quit
#define SDL_Quit
Definition: SDL_dynapi_overrides.h:58
SDL_MessageBoxButtonData
Individual button data.
Definition: SDL_messagebox.h:58
SDL_AssertionsQuit
void SDL_AssertionsQuit(void)
Definition: SDL_assert.c:385
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:431
SDL_arraysize
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
SDL_MessageBoxData::buttons
const SDL_MessageBoxButtonData * buttons
Definition: SDL_messagebox.h:102
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:51
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:426
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:89
state
struct xkb_state * state
Definition: SDL_waylandsym.h:114
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:62
SDL_MessageBoxData::numbuttons
int numbuttons
Definition: SDL_messagebox.h:101
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:56
SDL_AbortAssertion
static SDL_NORETURN void SDL_AbortAssertion(void)
Definition: SDL_assert.c:134