comparison src/SDL_assert.c @ 3647:c5925cd41955

First pass at Ryan's assertion code, minor tweaks to come.
author Sam Lantinga <slouken@libsdl.org>
date Wed, 13 Jan 2010 06:47:17 +0000
parents
children a9d830c05998
comparison
equal deleted inserted replaced
3646:88235d40b135 3647:c5925cd41955
1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 Sam Lantinga
20 slouken@libsdl.org
21 */
22
23 #include "SDL_assert.h"
24 #include "SDL.h"
25
26 #if (SDL_ASSERT_LEVEL > 0)
27
28 #ifdef _WINDOWS
29 #define WIN32_LEAN_AND_MEAN 1
30 #include <windows.h>
31 #else /* fprintf, _exit(), etc. */
32 #include <stdio.h>
33 #include <stdlib.h>
34 #endif
35
36 /* We can keep all triggered assertions in a singly-linked list so we can
37 * generate a report later.
38 */
39 #if !SDL_ASSERTION_REPORT_DISABLED
40 static SDL_assert_data assertion_list_terminator = { 0, 0, 0, 0, 0, 0, 0 };
41 static SDL_assert_data *triggered_assertions = &assertion_list_terminator;
42 #endif
43
44 static void
45 debug_print(const char *fmt, ...)
46 //#ifdef __GNUC__
47 //__attribute__((format (printf, 1, 2)))
48 //#endif
49 {
50 #ifdef _WINDOWS
51 /* Format into a buffer for OutputDebugStringA(). */
52 char buf[1024];
53 char *startptr;
54 char *ptr;
55 int len;
56 va_list ap;
57 va_start(ap, fmt);
58 len = (int) SDL_vsnprintf(buf, sizeof (buf), fmt, ap);
59 va_end(ap);
60
61 /* Visual C's vsnprintf() may not null-terminate the buffer. */
62 if ((len >= sizeof (buf)) || (len < 0)) {
63 buf[sizeof (buf) - 1] = '\0';
64 }
65
66 /* Write it, sorting out the Unix newlines... */
67 startptr = buf;
68 for (ptr = startptr; *ptr; ptr++) {
69 if (*ptr == '\n') {
70 *ptr = '\0';
71 OutputDebugStringA(startptr);
72 OutputDebugStringA("\r\n");
73 startptr = ptr+1;
74 }
75 }
76
77 /* catch that last piece if it didn't have a newline... */
78 if (startptr != ptr) {
79 OutputDebugStringA(startptr);
80 }
81 #else
82 /* Unix has it easy. Just dump it to stderr. */
83 va_list ap;
84 va_start(ap, fmt);
85 fprintf(stderr, fmt, ap);
86 va_end(ap);
87 fflush(stderr);
88 #endif
89 }
90
91
92 #ifdef _WINDOWS
93 static SDL_assert_state SDL_Windows_AssertChoice = SDL_ASSERTION_ABORT;
94 static const SDL_assert_data *SDL_Windows_AssertData = NULL;
95
96 static LRESULT CALLBACK
97 SDL_Assertion_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
98 {
99 switch (msg)
100 {
101 case WM_CREATE:
102 {
103 /* !!! FIXME: all this code stinks. */
104 const SDL_assert_data *data = SDL_Windows_AssertData;
105 char buf[1024];
106 const int w = 100;
107 const int h = 25;
108 const int gap = 10;
109 int x = gap;
110 int y = 50;
111 int len;
112 int i;
113 static const struct {
114 const char *name;
115 SDL_assert_state state;
116 } buttons[] = {
117 {"Abort", SDL_ASSERTION_ABORT },
118 {"Break", SDL_ASSERTION_BREAK },
119 {"Retry", SDL_ASSERTION_RETRY },
120 {"Ignore", SDL_ASSERTION_IGNORE },
121 {"Always Ignore", SDL_ASSERTION_ALWAYS_IGNORE },
122 };
123
124 len = (int) SDL_snprintf(buf, sizeof (buf),
125 "Assertion failure at %s (%s:%d), triggered %u time%s:\r\n '%s'",
126 data->function, data->filename, data->linenum,
127 data->trigger_count, (data->trigger_count == 1) ? "" : "s",
128 data->condition);
129 if ((len < 0) || (len >= sizeof (buf))) {
130 buf[sizeof (buf) - 1] = '\0';
131 }
132
133 CreateWindowA("STATIC", buf,
134 WS_VISIBLE | WS_CHILD | SS_LEFT,
135 x, y, 550, 100,
136 hwnd, (HMENU) 1, NULL, NULL);
137 y += 110;
138
139 for (i = 0; i < (sizeof (buttons) / sizeof (buttons[0])); i++) {
140 CreateWindowA("BUTTON", buttons[i].name,
141 WS_VISIBLE | WS_CHILD,
142 x, y, w, h,
143 hwnd, (HMENU) buttons[i].state, NULL, NULL);
144 x += w + gap;
145 }
146 break;
147 }
148
149 case WM_COMMAND:
150 SDL_Windows_AssertChoice = ((SDL_assert_state) (LOWORD(wParam)));
151 SDL_Windows_AssertData = NULL;
152 break;
153
154 case WM_DESTROY:
155 SDL_Windows_AssertData = NULL;
156 break;
157 }
158
159 return DefWindowProc(hwnd, msg, wParam, lParam);
160 }
161
162 static SDL_assert_state
163 SDL_PromptAssertion_windows(const SDL_assert_data *data)
164 {
165 HINSTANCE hInstance = 0; /* !!! FIXME? */
166 HWND hwnd;
167 MSG msg;
168 WNDCLASS wc = {0};
169
170 SDL_Windows_AssertChoice = SDL_ASSERTION_ABORT;
171 SDL_Windows_AssertData = data;
172
173 wc.lpszClassName = TEXT("SDL_assert");
174 wc.hInstance = hInstance ;
175 wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
176 wc.lpfnWndProc = SDL_Assertion_WndProc;
177 wc.hCursor = LoadCursor(0, IDC_ARROW);
178
179 RegisterClass(&wc);
180 hwnd = CreateWindow(wc.lpszClassName, TEXT("SDL assertion failure"),
181 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
182 150, 150, 570, 260, 0, 0, hInstance, 0);
183
184 while (GetMessage(&msg, NULL, 0, 0) && (SDL_Windows_AssertData != NULL)) {
185 TranslateMessage(&msg);
186 DispatchMessage(&msg);
187 }
188
189 DestroyWindow(hwnd);
190 UnregisterClass(wc.lpszClassName, hInstance);
191 return SDL_Windows_AssertChoice;
192 }
193 #endif
194
195
196 static void SDL_AddAssertionToReport(SDL_assert_data *data)
197 {
198 #if !SDL_ASSERTION_REPORT_DISABLED
199 /* (data) is always a static struct defined with the assert macros, so
200 we don't have to worry about copying or allocating them. */
201 if (data->next == NULL) { /* not yet added? */
202 data->next = triggered_assertions;
203 triggered_assertions = data;
204 }
205 #endif
206 }
207
208 static void SDL_GenerateAssertionReport(void)
209 {
210 #if !SDL_ASSERTION_REPORT_DISABLED
211 if (triggered_assertions != &assertion_list_terminator)
212 {
213 SDL_assert_data *item = triggered_assertions;
214
215 debug_print("\n\nSDL assertion report.\n");
216 debug_print("All SDL assertions between last init/quit:\n\n");
217
218 while (item != &assertion_list_terminator) {
219 debug_print(
220 "'%s'\n"
221 " * %s (%s:%d)\n"
222 " * triggered %u time%s.\n"
223 " * always ignore: %s.\n",
224 item->condition, item->function, item->filename,
225 item->linenum, item->trigger_count,
226 (item->trigger_count == 1) ? "" : "s",
227 item->always_ignore ? "yes" : "no");
228 item = item->next;
229 }
230 debug_print("\n");
231
232 triggered_assertions = &assertion_list_terminator;
233 }
234 #endif
235 }
236
237
238 static void SDL_AbortAssertion(void)
239 {
240 SDL_Quit();
241 #ifdef _WINDOWS
242 ExitProcess(42);
243 #elif unix || __APPLE__
244 _exit(42);
245 #else
246 #error Please define your platform or set SDL_ASSERT_LEVEL to 0.
247 #endif
248 }
249
250
251 static SDL_assert_state SDL_PromptAssertion(const SDL_assert_data *data)
252 {
253 const char *envr;
254
255 debug_print("\n\n"
256 "Assertion failure at %s (%s:%d), triggered %u time%s:\n"
257 " '%s'\n"
258 "\n",
259 data->function, data->filename, data->linenum,
260 data->trigger_count, (data->trigger_count == 1) ? "" : "s",
261 data->condition);
262
263 /* let env. variable override, so unit tests won't block in a GUI. */
264 envr = SDL_getenv("SDL_ASSERT");
265 if (envr != NULL) {
266 if (SDL_strcmp(envr, "abort") == 0) {
267 return SDL_ASSERTION_ABORT;
268 } else if (SDL_strcmp(envr, "break") == 0) {
269 return SDL_ASSERTION_BREAK;
270 } else if (SDL_strcmp(envr, "retry") == 0) {
271 return SDL_ASSERTION_RETRY;
272 } else if (SDL_strcmp(envr, "ignore") == 0) {
273 return SDL_ASSERTION_IGNORE;
274 } else if (SDL_strcmp(envr, "always_ignore") == 0) {
275 return SDL_ASSERTION_ALWAYS_IGNORE;
276 } else {
277 return SDL_ASSERTION_ABORT; /* oh well. */
278 }
279 }
280
281 /* platform-specific UI... */
282
283 #ifdef _WINDOWS
284 return SDL_PromptAssertion_windows(data);
285
286 #elif __APPLE__
287 /* This has to be done in an Objective-C (*.m) file, so we call out. */
288 extern SDL_assert_state SDL_PromptAssertion_cocoa(const SDL_assert_data *);
289 return SDL_PromptAssertion_cocoa(data);
290
291 #elif unix
292 /* this is a little hacky. */
293 for ( ; ; ) {
294 char buf[32];
295 fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
296 fflush(stderr);
297 if (fgets(buf, sizeof (buf), stdin) == NULL) {
298 return SDL_ASSERTION_ABORT;
299 }
300
301 if (SDL_strcmp(buf, "a") == 0) {
302 return SDL_ASSERTION_ABORT;
303 } else if (SDL_strcmp(envr, "b") == 0) {
304 return SDL_ASSERTION_BREAK;
305 } else if (SDL_strcmp(envr, "r") == 0) {
306 return SDL_ASSERTION_RETRY;
307 } else if (SDL_strcmp(envr, "i") == 0) {
308 return SDL_ASSERTION_IGNORE;
309 } else if (SDL_strcmp(envr, "A") == 0) {
310 return SDL_ASSERTION_ALWAYS_IGNORE;
311 }
312 }
313
314 #else
315 #error Please define your platform or set SDL_ASSERT_LEVEL to 0.
316 #endif
317
318 return SDL_ASSERTION_ABORT;
319 }
320
321
322 static SDL_mutex *assertion_mutex = NULL;
323
324 SDL_assert_state
325 SDL_ReportAssertion(SDL_assert_data *data, const char *func, int line)
326 {
327 SDL_assert_state state;
328
329 if (SDL_LockMutex(assertion_mutex) < 0) {
330 return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
331 }
332
333 /* doing this because Visual C is upset over assigning in the macro. */
334 if (data->trigger_count == 0) {
335 data->function = func;
336 data->linenum = line;
337 }
338
339 SDL_AddAssertionToReport(data);
340
341 data->trigger_count++;
342 if (data->always_ignore) {
343 SDL_UnlockMutex(assertion_mutex);
344 return SDL_ASSERTION_IGNORE;
345 }
346
347 state = SDL_PromptAssertion(data);
348
349 switch (state)
350 {
351 case SDL_ASSERTION_ABORT:
352 SDL_UnlockMutex(assertion_mutex); /* in case we assert in quit. */
353 SDL_AbortAssertion();
354 return SDL_ASSERTION_IGNORE; /* shouldn't return, but oh well. */
355
356 case SDL_ASSERTION_ALWAYS_IGNORE:
357 state = SDL_ASSERTION_IGNORE;
358 data->always_ignore = 1;
359 break;
360
361 case SDL_ASSERTION_IGNORE:
362 case SDL_ASSERTION_RETRY:
363 case SDL_ASSERTION_BREAK:
364 break; /* macro handles these. */
365 }
366
367 SDL_UnlockMutex(assertion_mutex);
368
369 return state;
370 }
371
372 #endif /* SDL_ASSERT_LEVEL > 0 */
373
374
375 int SDL_AssertionsInit(void)
376 {
377 #if (SDL_ASSERT_LEVEL > 0)
378 assertion_mutex = SDL_CreateMutex();
379 if (assertion_mutex == NULL) {
380 return -1;
381 }
382 #endif
383 return 0;
384 }
385
386 void SDL_AssertionsQuit(void)
387 {
388 #if (SDL_ASSERT_LEVEL > 0)
389 SDL_GenerateAssertionReport();
390 SDL_DestroyMutex(assertion_mutex);
391 assertion_mutex = NULL;
392 #endif
393 }
394
395 /* vi: set ts=4 sw=4 expandtab: */
396