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 static SDL_NORETURN void SDL_ExitProcess(int exitcode)
124 {
125 #ifdef __WIN32__
126  ExitProcess(exitcode);
127 #elif defined(__EMSCRIPTEN__)
128  emscripten_cancel_main_loop(); /* this should "kill" the app. */
129  emscripten_force_exit(exitcode); /* this should "kill" the app. */
130  exit(exitcode);
131 #else
132  _exit(exitcode);
133 #endif
134 }
135 
136 
138 {
139  SDL_Quit();
140  SDL_ExitProcess(42);
141 }
142 
143 
145 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
146 {
147 #ifdef __WIN32__
148  #define ENDLINE "\r\n"
149 #else
150  #define ENDLINE "\n"
151 #endif
152 
153  const char *envr;
156  SDL_MessageBoxData messagebox;
157  SDL_MessageBoxButtonData buttons[] = {
158  { 0, SDL_ASSERTION_RETRY, "Retry" },
159  { 0, SDL_ASSERTION_BREAK, "Break" },
160  { 0, SDL_ASSERTION_ABORT, "Abort" },
162  SDL_ASSERTION_IGNORE, "Ignore" },
164  SDL_ASSERTION_ALWAYS_IGNORE, "Always Ignore" }
165  };
166  char *message;
167  int selected;
168 
169  (void) userdata; /* unused in default handler. */
170 
171  message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
172  if (!message) {
173  /* Uh oh, we're in real trouble now... */
174  return SDL_ASSERTION_ABORT;
175  }
177  "Assertion failure at %s (%s:%d), triggered %u %s:" ENDLINE
178  " '%s'",
179  data->function, data->filename, data->linenum,
180  data->trigger_count, (data->trigger_count == 1) ? "time" : "times",
181  data->condition);
182 
183  debug_print("\n\n%s\n\n", message);
184 
185  /* let env. variable override, so unit tests won't block in a GUI. */
186  envr = SDL_getenv("SDL_ASSERT");
187  if (envr != NULL) {
188  SDL_stack_free(message);
189 
190  if (SDL_strcmp(envr, "abort") == 0) {
191  return SDL_ASSERTION_ABORT;
192  } else if (SDL_strcmp(envr, "break") == 0) {
193  return SDL_ASSERTION_BREAK;
194  } else if (SDL_strcmp(envr, "retry") == 0) {
195  return SDL_ASSERTION_RETRY;
196  } else if (SDL_strcmp(envr, "ignore") == 0) {
197  return SDL_ASSERTION_IGNORE;
198  } else if (SDL_strcmp(envr, "always_ignore") == 0) {
200  } else {
201  return SDL_ASSERTION_ABORT; /* oh well. */
202  }
203  }
204 
205  /* Leave fullscreen mode, if possible (scary!) */
206  window = SDL_GetFocusWindow();
207  if (window) {
209  SDL_MinimizeWindow(window);
210  } else {
211  /* !!! FIXME: ungrab the input if we're not fullscreen? */
212  /* No need to mess with the window */
213  window = NULL;
214  }
215  }
216 
217  /* Show a messagebox if we can, otherwise fall back to stdio */
218  SDL_zero(messagebox);
219  messagebox.flags = SDL_MESSAGEBOX_WARNING;
220  messagebox.window = window;
221  messagebox.title = "Assertion Failed";
222  messagebox.message = message;
223  messagebox.numbuttons = SDL_arraysize(buttons);
224  messagebox.buttons = buttons;
225 
226  if (SDL_ShowMessageBox(&messagebox, &selected) == 0) {
227  if (selected == -1) {
228  state = SDL_ASSERTION_IGNORE;
229  } else {
230  state = (SDL_assert_state)selected;
231  }
232  }
233 
234  else
235  {
236 #if defined(__EMSCRIPTEN__)
237  /* This is nasty, but we can't block on a custom UI. */
238  for ( ; ; ) {
239  SDL_bool okay = SDL_TRUE;
240  char *buf = (char *) EM_ASM_INT({
241  var str =
242  Pointer_stringify($0) + '\n\n' +
243  'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :';
244  var reply = window.prompt(str, "i");
245  if (reply === null) {
246  reply = "i";
247  }
248  return allocate(intArrayFromString(reply), 'i8', ALLOC_NORMAL);
249  }, message);
250 
251  if (SDL_strcmp(buf, "a") == 0) {
252  state = SDL_ASSERTION_ABORT;
253  /* (currently) no break functionality on Emscripten
254  } else if (SDL_strcmp(buf, "b") == 0) {
255  state = SDL_ASSERTION_BREAK; */
256  } else if (SDL_strcmp(buf, "r") == 0) {
257  state = SDL_ASSERTION_RETRY;
258  } else if (SDL_strcmp(buf, "i") == 0) {
259  state = SDL_ASSERTION_IGNORE;
260  } else if (SDL_strcmp(buf, "A") == 0) {
262  } else {
263  okay = SDL_FALSE;
264  }
265  free(buf);
266 
267  if (okay) {
268  break;
269  }
270  }
271 #elif defined(HAVE_STDIO_H)
272  /* this is a little hacky. */
273  for ( ; ; ) {
274  char buf[32];
275  fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
276  fflush(stderr);
277  if (fgets(buf, sizeof (buf), stdin) == NULL) {
278  break;
279  }
280 
281  if (SDL_strncmp(buf, "a", 1) == 0) {
282  state = SDL_ASSERTION_ABORT;
283  break;
284  } else if (SDL_strncmp(buf, "b", 1) == 0) {
285  state = SDL_ASSERTION_BREAK;
286  break;
287  } else if (SDL_strncmp(buf, "r", 1) == 0) {
288  state = SDL_ASSERTION_RETRY;
289  break;
290  } else if (SDL_strncmp(buf, "i", 1) == 0) {
291  state = SDL_ASSERTION_IGNORE;
292  break;
293  } else if (SDL_strncmp(buf, "A", 1) == 0) {
295  break;
296  }
297  }
298 #endif /* HAVE_STDIO_H */
299  }
300 
301  /* Re-enter fullscreen mode */
302  if (window) {
303  SDL_RestoreWindow(window);
304  }
305 
306  SDL_stack_free(message);
307 
308  return state;
309 }
310 
311 
313 SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
314  int line)
315 {
317  static int assertion_running = 0;
318 
319 #ifndef SDL_THREADS_DISABLED
320  static SDL_SpinLock spinlock = 0;
321  SDL_AtomicLock(&spinlock);
322  if (assertion_mutex == NULL) { /* never called SDL_Init()? */
323  assertion_mutex = SDL_CreateMutex();
324  if (assertion_mutex == NULL) {
325  SDL_AtomicUnlock(&spinlock);
326  return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
327  }
328  }
329  SDL_AtomicUnlock(&spinlock);
330 
331  if (SDL_LockMutex(assertion_mutex) < 0) {
332  return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
333  }
334 #endif
335 
336  /* doing this because Visual C is upset over assigning in the macro. */
337  if (data->trigger_count == 0) {
338  data->function = func;
339  data->filename = file;
340  data->linenum = line;
341  }
342 
344 
345  assertion_running++;
346  if (assertion_running > 1) { /* assert during assert! Abort. */
347  if (assertion_running == 2) {
349  } else if (assertion_running == 3) { /* Abort asserted! */
350  SDL_ExitProcess(42);
351  } else {
352  while (1) { /* do nothing but spin; what else can you do?! */ }
353  }
354  }
355 
356  if (!data->always_ignore) {
357  state = assertion_handler(data, assertion_userdata);
358  }
359 
360  switch (state)
361  {
362  case SDL_ASSERTION_ABORT:
364  return SDL_ASSERTION_IGNORE; /* shouldn't return, but oh well. */
365 
367  state = SDL_ASSERTION_IGNORE;
368  data->always_ignore = 1;
369  break;
370 
372  case SDL_ASSERTION_RETRY:
373  case SDL_ASSERTION_BREAK:
374  break; /* macro handles these. */
375  }
376 
377  assertion_running--;
378 
379 #ifndef SDL_THREADS_DISABLED
380  SDL_UnlockMutex(assertion_mutex);
381 #endif
382 
383  return state;
384 }
385 
386 
388 {
390 #ifndef SDL_THREADS_DISABLED
391  if (assertion_mutex != NULL) {
392  SDL_DestroyMutex(assertion_mutex);
393  assertion_mutex = NULL;
394  }
395 #endif
396 }
397 
398 void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
399 {
400  if (handler != NULL) {
401  assertion_handler = handler;
402  assertion_userdata = userdata;
403  } else {
406  }
407 }
408 
410 {
411  return triggered_assertions;
412 }
413 
415 {
416  SDL_assert_data *next = NULL;
417  SDL_assert_data *item;
418  for (item = triggered_assertions; item != NULL; item = next) {
419  next = (SDL_assert_data *) item->next;
420  item->always_ignore = SDL_FALSE;
421  item->trigger_count = 0;
422  item->next = NULL;
423  }
424 
426 }
427 
429 {
430  return SDL_PromptAssertion;
431 }
432 
434 {
435  if (userdata != NULL) {
436  *userdata = assertion_userdata;
437  }
438  return assertion_handler;
439 }
440 
441 /* vi: set ts=4 sw=4 expandtab: */
static SDL_AssertionHandler assertion_handler
Definition: SDL_assert.c:65
SDL_AssertState(* SDL_AssertionHandler)(const SDL_AssertData *data, void *userdata)
Definition: SDL_assert.h:188
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
void SDL_ResetAssertionReport(void)
Reset the list of all assertion failures.
Definition: SDL_assert.c:414
#define SDL_NORETURN
Definition: begin_code.h:154
#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:145
const SDL_assert_data * SDL_GetAssertionReport(void)
Get a list of all assertion failures.
Definition: SDL_assert.c:409
#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:398
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:387
#define SDL_stack_alloc(type, count)
Definition: SDL_stdinc.h:354
SDL_Window * SDL_GetFocusWindow(void)
Definition: SDL_video.c:2594
#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:433
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
static SDL_NORETURN void SDL_AbortAssertion(void)
Definition: SDL_assert.c:137
#define SDL_ShowMessageBox
#define NULL
Definition: begin_code.h:164
SDL_bool
Definition: SDL_stdinc.h:139
static SDL_NORETURN void SDL_ExitProcess(int exitcode)
Definition: SDL_assert.c:123
#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:428
#define SDL_snprintf
#define SDL_UnlockMutex
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:93
#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:313
#define SDLCALL
Definition: SDL_internal.h:45
#define ENDLINE
#define SDL_assert_data
Definition: SDL_assert.h:280