SDL  2.0
SDL_assert.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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 static SDL_assert_state
48 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata);
49 
50 /*
51  * We keep all triggered assertions in a singly-linked list so we can
52  * generate a report later.
53  */
55 
58 static void *assertion_userdata = NULL;
59 
60 #ifdef __GNUC__
61 static void
62 debug_print(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
63 #endif
64 
65 static void
66 debug_print(const char *fmt, ...)
67 {
68  va_list ap;
69  va_start(ap, fmt);
71  va_end(ap);
72 }
73 
74 
76 {
77  /* (data) is always a static struct defined with the assert macros, so
78  we don't have to worry about copying or allocating them. */
79  data->trigger_count++;
80  if (data->trigger_count == 1) { /* not yet added? */
81  data->next = triggered_assertions;
83  }
84 }
85 
86 
87 static void SDL_GenerateAssertionReport(void)
88 {
90 
91  /* only do this if the app hasn't assigned an assertion handler. */
92  if ((item != NULL) && (assertion_handler != SDL_PromptAssertion)) {
93  debug_print("\n\nSDL assertion report.\n");
94  debug_print("All SDL assertions between last init/quit:\n\n");
95 
96  while (item != NULL) {
98  "'%s'\n"
99  " * %s (%s:%d)\n"
100  " * triggered %u time%s.\n"
101  " * always ignore: %s.\n",
102  item->condition, item->function, item->filename,
103  item->linenum, item->trigger_count,
104  (item->trigger_count == 1) ? "" : "s",
105  item->always_ignore ? "yes" : "no");
106  item = item->next;
107  }
108  debug_print("\n");
109 
111  }
112 }
113 
114 static void SDL_ExitProcess(int exitcode)
115 {
116 #ifdef __WIN32__
117  ExitProcess(exitcode);
118 #else
119  _exit(exitcode);
120 #endif
121 }
122 
123 static void SDL_AbortAssertion(void)
124 {
125  SDL_Quit();
126  SDL_ExitProcess(42);
127 }
128 
129 
130 static SDL_assert_state
131 SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
132 {
133 #ifdef __WIN32__
134  #define ENDLINE "\r\n"
135 #else
136  #define ENDLINE "\n"
137 #endif
138 
139  const char *envr;
142  SDL_MessageBoxData messagebox;
143  SDL_MessageBoxButtonData buttons[] = {
144  { 0, SDL_ASSERTION_RETRY, "Retry" },
145  { 0, SDL_ASSERTION_BREAK, "Break" },
146  { 0, SDL_ASSERTION_ABORT, "Abort" },
148  SDL_ASSERTION_IGNORE, "Ignore" },
150  SDL_ASSERTION_ALWAYS_IGNORE, "Always Ignore" }
151  };
152  char *message;
153  int selected;
154 
155  (void) userdata; /* unused in default handler. */
156 
157  message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
158  if (!message) {
159  /* Uh oh, we're in real trouble now... */
160  return SDL_ASSERTION_ABORT;
161  }
163  "Assertion failure at %s (%s:%d), triggered %u %s:" ENDLINE
164  " '%s'",
165  data->function, data->filename, data->linenum,
166  data->trigger_count, (data->trigger_count == 1) ? "time" : "times",
167  data->condition);
168 
169  debug_print("\n\n%s\n\n", message);
170 
171  /* let env. variable override, so unit tests won't block in a GUI. */
172  envr = SDL_getenv("SDL_ASSERT");
173  if (envr != NULL) {
174  SDL_stack_free(message);
175 
176  if (SDL_strcmp(envr, "abort") == 0) {
177  return SDL_ASSERTION_ABORT;
178  } else if (SDL_strcmp(envr, "break") == 0) {
179  return SDL_ASSERTION_BREAK;
180  } else if (SDL_strcmp(envr, "retry") == 0) {
181  return SDL_ASSERTION_RETRY;
182  } else if (SDL_strcmp(envr, "ignore") == 0) {
183  return SDL_ASSERTION_IGNORE;
184  } else if (SDL_strcmp(envr, "always_ignore") == 0) {
186  } else {
187  return SDL_ASSERTION_ABORT; /* oh well. */
188  }
189  }
190 
191  /* Leave fullscreen mode, if possible (scary!) */
192  window = SDL_GetFocusWindow();
193  if (window) {
195  SDL_MinimizeWindow(window);
196  } else {
197  /* !!! FIXME: ungrab the input if we're not fullscreen? */
198  /* No need to mess with the window */
199  window = NULL;
200  }
201  }
202 
203  /* Show a messagebox if we can, otherwise fall back to stdio */
204  SDL_zero(messagebox);
205  messagebox.flags = SDL_MESSAGEBOX_WARNING;
206  messagebox.window = window;
207  messagebox.title = "Assertion Failed";
208  messagebox.message = message;
209  messagebox.numbuttons = SDL_arraysize(buttons);
210  messagebox.buttons = buttons;
211 
212  if (SDL_ShowMessageBox(&messagebox, &selected) == 0) {
213  if (selected == -1) {
214  state = SDL_ASSERTION_IGNORE;
215  } else {
216  state = (SDL_assert_state)selected;
217  }
218  }
219 #ifdef HAVE_STDIO_H
220  else
221  {
222  /* this is a little hacky. */
223  for ( ; ; ) {
224  char buf[32];
225  fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
226  fflush(stderr);
227  if (fgets(buf, sizeof (buf), stdin) == NULL) {
228  break;
229  }
230 
231  if (SDL_strcmp(buf, "a") == 0) {
232  state = SDL_ASSERTION_ABORT;
233  break;
234  } else if (SDL_strcmp(buf, "b") == 0) {
235  state = SDL_ASSERTION_BREAK;
236  break;
237  } else if (SDL_strcmp(buf, "r") == 0) {
238  state = SDL_ASSERTION_RETRY;
239  break;
240  } else if (SDL_strcmp(buf, "i") == 0) {
241  state = SDL_ASSERTION_IGNORE;
242  break;
243  } else if (SDL_strcmp(buf, "A") == 0) {
245  break;
246  }
247  }
248  }
249 #endif /* HAVE_STDIO_H */
250 
251  /* Re-enter fullscreen mode */
252  if (window) {
253  SDL_RestoreWindow(window);
254  }
255 
256  SDL_stack_free(message);
257 
258  return state;
259 }
260 
261 
263 SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file,
264  int line)
265 {
266  static int assertion_running = 0;
267  static SDL_SpinLock spinlock = 0;
269 
270  SDL_AtomicLock(&spinlock);
271  if (assertion_mutex == NULL) { /* never called SDL_Init()? */
272  assertion_mutex = SDL_CreateMutex();
273  if (assertion_mutex == NULL) {
274  SDL_AtomicUnlock(&spinlock);
275  return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
276  }
277  }
278  SDL_AtomicUnlock(&spinlock);
279 
280  if (SDL_LockMutex(assertion_mutex) < 0) {
281  return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
282  }
283 
284  /* doing this because Visual C is upset over assigning in the macro. */
285  if (data->trigger_count == 0) {
286  data->function = func;
287  data->filename = file;
288  data->linenum = line;
289  }
290 
292 
293  assertion_running++;
294  if (assertion_running > 1) { /* assert during assert! Abort. */
295  if (assertion_running == 2) {
297  } else if (assertion_running == 3) { /* Abort asserted! */
298  SDL_ExitProcess(42);
299  } else {
300  while (1) { /* do nothing but spin; what else can you do?! */ }
301  }
302  }
303 
304  if (!data->always_ignore) {
305  state = assertion_handler(data, assertion_userdata);
306  }
307 
308  switch (state)
309  {
310  case SDL_ASSERTION_ABORT:
312  return SDL_ASSERTION_IGNORE; /* shouldn't return, but oh well. */
313 
315  state = SDL_ASSERTION_IGNORE;
316  data->always_ignore = 1;
317  break;
318 
320  case SDL_ASSERTION_RETRY:
321  case SDL_ASSERTION_BREAK:
322  break; /* macro handles these. */
323  }
324 
325  assertion_running--;
326  SDL_UnlockMutex(assertion_mutex);
327 
328  return state;
329 }
330 
331 
333 {
335  if (assertion_mutex != NULL) {
336  SDL_DestroyMutex(assertion_mutex);
337  assertion_mutex = NULL;
338  }
339 }
340 
341 void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
342 {
343  if (handler != NULL) {
344  assertion_handler = handler;
345  assertion_userdata = userdata;
346  } else {
349  }
350 }
351 
353 {
354  return triggered_assertions;
355 }
356 
358 {
359  SDL_assert_data *next = NULL;
360  SDL_assert_data *item;
361  for (item = triggered_assertions; item != NULL; item = next) {
362  next = (SDL_assert_data *) item->next;
363  item->always_ignore = SDL_FALSE;
364  item->trigger_count = 0;
365  item->next = NULL;
366  }
367 
369 }
370 
372 {
373  return SDL_PromptAssertion;
374 }
375 
377 {
378  if (userdata != NULL) {
379  *userdata = assertion_userdata;
380  }
381  return assertion_handler;
382 }
383 
384 /* vi: set ts=4 sw=4 expandtab: */
static SDL_AssertionHandler assertion_handler
Definition: SDL_assert.c:57
SDL_AssertState(* SDL_AssertionHandler)(const SDL_AssertData *data, void *userdata)
Definition: SDL_assert.h:186
const char * message
#define SDL_MAX_LOG_MESSAGE
The maximum size of a log message.
Definition: SDL_log.h:54
#define SDL_LockMutex
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: SDL_opengl.h:1565
static void SDL_GenerateAssertionReport(void)
Definition: SDL_assert.c:87
#define SDL_LogMessageV
static SDL_assert_data * triggered_assertions
Definition: SDL_assert.c:54
const char * title
#define SDL_AtomicLock
SDL_Window * window
GLuint GLsizei const GLchar * message
static void SDL_AbortAssertion(void)
Definition: SDL_assert.c:123
void SDL_ResetAssertionReport(void)
Reset the list of all assertion failures.
Definition: SDL_assert.c:357
static SDL_Window * window
#define SDL_CreateMutex
struct xkb_state * state
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
#define SDL_GetWindowFlags
static void * assertion_userdata
Definition: SDL_assert.c:58
#define SDL_MinimizeWindow
static SDL_assert_state SDL_PromptAssertion(const SDL_assert_data *data, void *userdata)
Definition: SDL_assert.c:131
const SDL_assert_data * SDL_GetAssertionReport(void)
Get a list of all assertion failures.
Definition: SDL_assert.c:352
static void debug_print(const char *fmt,...)
Definition: SDL_assert.c:66
void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
Set an application-defined assertion handler.
Definition: SDL_assert.c:341
Individual button data.
#define SDL_AtomicUnlock
void SDL_AssertionsQuit(void)
Definition: SDL_assert.c:332
#define SDL_stack_alloc(type, count)
Definition: SDL_stdinc.h:328
SDL_Window * SDL_GetFocusWindow(void)
Definition: SDL_video.c:2534
#define SDL_Quit
#define SDL_zero(x)
Definition: SDL_stdinc.h:359
SDL_AssertionHandler SDL_GetAssertionHandler(void **userdata)
Get the current assertion handler.
Definition: SDL_assert.c:376
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:75
#define SDL_getenv
#define SDL_ShowMessageBox
#define NULL
Definition: begin_code.h:143
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
#define SDL_assert_state
Definition: SDL_assert.h:277
#define SDL_DestroyMutex
GLenum func
The type used to identify a window.
Definition: SDL_sysvideo.h:71
static SDL_mutex * assertion_mutex
Definition: SDL_assert.c:56
SDL_AssertionHandler SDL_GetDefaultAssertionHandler(void)
Get the default assertion handler.
Definition: SDL_assert.c:371
#define SDL_snprintf
#define SDL_UnlockMutex
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:90
#define SDL_strcmp
int SDL_SpinLock
Definition: SDL_atomic.h:89
#define SDL_stack_free(data)
Definition: SDL_stdinc.h:329
SDL_assert_state SDL_ReportAssertion(SDL_assert_data *data, const char *func, const char *file, int line)
Definition: SDL_assert.c:263
#define ENDLINE
static void SDL_ExitProcess(int exitcode)
Definition: SDL_assert.c:114
#define SDL_assert_data
Definition: SDL_assert.h:278