SDL  2.0
SDL_assert.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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 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
147 static void SDL_AbortAssertion(void)
148 {
149  SDL_Quit();
150  SDL_ExitProcess(42);
151 }
152 
153 
155 SDL_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;
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" },
172  SDL_ASSERTION_IGNORE, "Ignore" },
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  }
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) {
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) {
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) {
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) {
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 
323 SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
324  int line)
325 {
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 
354 
355  assertion_running++;
356  if (assertion_running > 1) { /* assert during assert! Abort. */
357  if (assertion_running == 2) {
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:
374  return SDL_ASSERTION_IGNORE; /* shouldn't return, but oh well. */
375 
377  state = SDL_ASSERTION_IGNORE;
378  data->always_ignore = 1;
379  break;
380 
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 
398 {
400 #ifndef SDL_THREADS_DISABLED
401  if (assertion_mutex != NULL) {
402  SDL_DestroyMutex(assertion_mutex);
403  assertion_mutex = NULL;
404  }
405 #endif
406 }
407 
408 void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
409 {
410  if (handler != NULL) {
411  assertion_handler = handler;
412  assertion_userdata = userdata;
413  } else {
416  }
417 }
418 
420 {
421  return triggered_assertions;
422 }
423 
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 
436 }
437 
439 {
440  return SDL_PromptAssertion;
441 }
442 
444 {
445  if (userdata != NULL) {
446  *userdata = assertion_userdata;
447  }
448  return assertion_handler;
449 }
450 
451 /* vi: set ts=4 sw=4 expandtab: */
static SDL_AssertionHandler assertion_handler
Definition: SDL_assert.c:65
const char * message
#define SDL_MAX_LOG_MESSAGE
The maximum size of a log message.
Definition: SDL_log.h:54
#define SDL_LockMutex
static void SDL_GenerateAssertionReport(void)
Definition: SDL_assert.c:95
#define SDL_LogMessageV
static SDL_assert_data * triggered_assertions
Definition: SDL_assert.c:59
const char * title
#define SDL_AtomicLock
SDL_Window * window
GLuint GLsizei const GLchar * message
static void SDL_AbortAssertion(void)
Definition: SDL_assert.c:147
void SDL_ResetAssertionReport(void)
Reset the list of all assertion failures.
Definition: SDL_assert.c:424
#define SDL_CreateMutex
struct xkb_state * state
SDL_EventEntry * free
Definition: SDL_events.c:84
#define SDL_GetWindowFlags
static void * assertion_userdata
Definition: SDL_assert.c:66
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
#define SDL_MinimizeWindow
static SDL_assert_state SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
Definition: SDL_assert.c:155
const SDL_assert_data * SDL_GetAssertionReport(void)
Get a list of all assertion failures.
Definition: SDL_assert.c:419
#define SDL_strncmp
static void debug_print(const char *fmt,...)
Definition: SDL_assert.c:74
void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
Set an application-defined assertion handler.
Definition: SDL_assert.c:408
Individual button data.
#define SDL_AtomicUnlock
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: SDL_opengl.h:1572
void SDL_AssertionsQuit(void)
Definition: SDL_assert.c:397
#define SDL_stack_alloc(type, count)
Definition: SDL_stdinc.h:354
SDL_Window * SDL_GetFocusWindow(void)
Definition: SDL_video.c:2671
#define SDL_Quit
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
SDL_AssertionHandler SDL_GetAssertionHandler(void **userdata)
Get the current assertion handler.
Definition: SDL_assert.c:443
GLenum GLuint GLenum GLsizei const GLchar * buf
const SDL_MessageBoxButtonData * buttons
MessageBox structure containing title, text, window, etc.
#define SDL_RestoreWindow
static void SDL_AddAssertionToReport(SDL_assert_data *data)
Definition: SDL_assert.c:83
#define SDL_getenv
#define SDL_ShowMessageBox
#define NULL
Definition: begin_code.h:164
SDL_bool
Definition: SDL_stdinc.h:161
#define SDL_assert_state
Definition: SDL_assert.h:279
#define SDL_DestroyMutex
GLenum func
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
The type used to identify a window.
Definition: SDL_sysvideo.h:73
static SDL_mutex * assertion_mutex
Definition: SDL_assert.c:62
SDL_AssertionHandler SDL_GetDefaultAssertionHandler(void)
Get the default assertion handler.
Definition: SDL_assert.c:438
#define SDL_snprintf
#define SDL_UnlockMutex
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
#define SDL_strcmp
int SDL_SpinLock
Definition: SDL_atomic.h:89
#define SDL_stack_free(data)
Definition: SDL_stdinc.h:355
SDL_assert_state SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file, int line)
Definition: SDL_assert.c:323
SDL_AssertState(* SDL_AssertionHandler)(const SDL_AssertData *data, void *userdata)
Definition: SDL_assert.h:188
#define SDLCALL
Definition: SDL_internal.h:45
#define ENDLINE
static void SDL_ExitProcess(int exitcode)
Definition: SDL_assert.c:126
#define SDL_assert_data
Definition: SDL_assert.h:280