Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/SDL_assert.c
9902 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
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(SDL_PLATFORM_WINDOWS)
24
#include "core/windows/SDL_windows.h"
25
#endif
26
27
#include "SDL_assert_c.h"
28
//#include "video/SDL_sysvideo.h"
29
30
#if defined(SDL_PLATFORM_WINDOWS)
31
#ifndef WS_OVERLAPPEDWINDOW
32
#define WS_OVERLAPPEDWINDOW 0
33
#endif
34
#endif
35
36
#ifdef SDL_PLATFORM_EMSCRIPTEN
37
#include <emscripten.h>
38
#endif
39
40
// The size of the stack buffer to use for rendering assert messages.
41
#define SDL_MAX_ASSERT_MESSAGE_STACK 256
42
43
static SDL_AssertState SDLCALL SDL_PromptAssertion(const SDL_AssertData *data, void *userdata);
44
45
/*
46
* We keep all triggered assertions in a singly-linked list so we can
47
* generate a report later.
48
*/
49
static SDL_AssertData *triggered_assertions = NULL;
50
51
#ifndef SDL_THREADS_DISABLED
52
static SDL_Mutex *assertion_mutex = NULL;
53
#endif
54
55
static SDL_AssertionHandler assertion_handler = SDL_PromptAssertion;
56
static void *assertion_userdata = NULL;
57
58
#ifdef __GNUC__
59
static void debug_print(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
60
#endif
61
62
static void debug_print(const char *fmt, ...)
63
{
64
va_list ap;
65
va_start(ap, fmt);
66
SDL_LogMessageV(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_WARN, fmt, ap);
67
va_end(ap);
68
}
69
70
static void SDL_AddAssertionToReport(SDL_AssertData *data)
71
{
72
/* (data) is always a static struct defined with the assert macros, so
73
we don't have to worry about copying or allocating them. */
74
data->trigger_count++;
75
if (data->trigger_count == 1) { // not yet added?
76
data->next = triggered_assertions;
77
triggered_assertions = data;
78
}
79
}
80
81
#if defined(SDL_PLATFORM_WINDOWS)
82
#define ENDLINE "\r\n"
83
#else
84
#define ENDLINE "\n"
85
#endif
86
87
static int SDL_RenderAssertMessage(char *buf, size_t buf_len, const SDL_AssertData *data)
88
{
89
return SDL_snprintf(buf, buf_len,
90
"Assertion failure at %s (%s:%d), triggered %u %s:" ENDLINE " '%s'",
91
data->function, data->filename, data->linenum,
92
data->trigger_count, (data->trigger_count == 1) ? "time" : "times",
93
data->condition);
94
}
95
96
static void SDL_GenerateAssertionReport(void)
97
{
98
const SDL_AssertData *item = triggered_assertions;
99
100
// only do this if the app hasn't assigned an assertion handler.
101
if ((item) && (assertion_handler != SDL_PromptAssertion)) {
102
debug_print("\n\nSDL assertion report.\n");
103
debug_print("All SDL assertions between last init/quit:\n\n");
104
105
while (item) {
106
debug_print(
107
"'%s'\n"
108
" * %s (%s:%d)\n"
109
" * triggered %u time%s.\n"
110
" * always ignore: %s.\n",
111
item->condition, item->function, item->filename,
112
item->linenum, item->trigger_count,
113
(item->trigger_count == 1) ? "" : "s",
114
item->always_ignore ? "yes" : "no");
115
item = item->next;
116
}
117
debug_print("\n");
118
119
SDL_ResetAssertionReport();
120
}
121
}
122
123
/* This is not declared in any header, although it is shared between some
124
parts of SDL, because we don't want anything calling it without an
125
extremely good reason. */
126
#ifdef __WATCOMC__
127
extern void SDL_ExitProcess(int exitcode);
128
#pragma aux SDL_ExitProcess aborts;
129
#endif
130
extern SDL_NORETURN void SDL_ExitProcess(int exitcode);
131
132
#ifdef __WATCOMC__
133
static void SDL_AbortAssertion(void);
134
#pragma aux SDL_AbortAssertion aborts;
135
#endif
136
static SDL_NORETURN void SDL_AbortAssertion(void)
137
{
138
SDL_Quit();
139
SDL_ExitProcess(42);
140
}
141
142
static SDL_AssertState SDLCALL SDL_PromptAssertion(const SDL_AssertData *data, void *userdata)
143
{
144
SDL_AssertState state = SDL_ASSERTION_ABORT;
145
SDL_Window *window;
146
SDL_MessageBoxData messagebox;
147
SDL_MessageBoxButtonData buttons[] = {
148
{ 0, SDL_ASSERTION_RETRY, "Retry" },
149
{ 0, SDL_ASSERTION_BREAK, "Break" },
150
{ 0, SDL_ASSERTION_ABORT, "Abort" },
151
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
152
SDL_ASSERTION_IGNORE, "Ignore" },
153
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
154
SDL_ASSERTION_ALWAYS_IGNORE, "Always Ignore" }
155
};
156
int selected;
157
158
char stack_buf[SDL_MAX_ASSERT_MESSAGE_STACK];
159
char *message = stack_buf;
160
size_t buf_len = sizeof(stack_buf);
161
int len;
162
163
(void)userdata; // unused in default handler.
164
165
// Assume the output will fit...
166
len = SDL_RenderAssertMessage(message, buf_len, data);
167
168
// .. and if it didn't, try to allocate as much room as we actually need.
169
if (len >= (int)buf_len) {
170
if (SDL_size_add_check_overflow(len, 1, &buf_len)) {
171
message = (char *)SDL_malloc(buf_len);
172
if (message) {
173
len = SDL_RenderAssertMessage(message, buf_len, data);
174
} else {
175
message = stack_buf;
176
}
177
}
178
}
179
180
// Something went very wrong
181
if (len < 0) {
182
if (message != stack_buf) {
183
SDL_free(message);
184
}
185
return SDL_ASSERTION_ABORT;
186
}
187
188
debug_print("\n\n%s\n\n", message);
189
190
// let env. variable override, so unit tests won't block in a GUI.
191
const char *hint = SDL_GetHint(SDL_HINT_ASSERT);
192
if (hint) {
193
if (message != stack_buf) {
194
SDL_free(message);
195
}
196
197
if (SDL_strcmp(hint, "abort") == 0) {
198
return SDL_ASSERTION_ABORT;
199
} else if (SDL_strcmp(hint, "break") == 0) {
200
return SDL_ASSERTION_BREAK;
201
} else if (SDL_strcmp(hint, "retry") == 0) {
202
return SDL_ASSERTION_RETRY;
203
} else if (SDL_strcmp(hint, "ignore") == 0) {
204
return SDL_ASSERTION_IGNORE;
205
} else if (SDL_strcmp(hint, "always_ignore") == 0) {
206
return SDL_ASSERTION_ALWAYS_IGNORE;
207
} else {
208
return SDL_ASSERTION_ABORT; // oh well.
209
}
210
}
211
212
// Show a messagebox if we can, otherwise fall back to stdio
213
SDL_zero(messagebox);
214
messagebox.flags = SDL_MESSAGEBOX_WARNING;
215
messagebox.window = window;
216
messagebox.title = "Assertion Failed";
217
messagebox.message = message;
218
messagebox.numbuttons = SDL_arraysize(buttons);
219
messagebox.buttons = buttons;
220
221
//if (SDL_ShowMessageBox(&messagebox, &selected)) {
222
if (false) {
223
if (selected == -1) {
224
state = SDL_ASSERTION_IGNORE;
225
} else {
226
state = (SDL_AssertState)selected;
227
}
228
} else {
229
#ifdef SDL_PLATFORM_PRIVATE_ASSERT
230
SDL_PRIVATE_PROMPTASSERTION();
231
#elif defined(SDL_PLATFORM_EMSCRIPTEN)
232
// This is nasty, but we can't block on a custom UI.
233
for (;;) {
234
bool okay = true;
235
/* *INDENT-OFF* */ // clang-format off
236
int reply = MAIN_THREAD_EM_ASM_INT({
237
var str =
238
UTF8ToString($0) + '\n\n' +
239
'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :';
240
var reply = window.prompt(str, "i");
241
if (reply === null) {
242
reply = "i";
243
}
244
return reply.length === 1 ? reply.charCodeAt(0) : -1;
245
}, message);
246
/* *INDENT-ON* */ // clang-format on
247
248
switch (reply) {
249
case 'a':
250
state = SDL_ASSERTION_ABORT;
251
break;
252
#if 0 // (currently) no break functionality on Emscripten
253
case 'b':
254
state = SDL_ASSERTION_BREAK;
255
break;
256
#endif
257
case 'r':
258
state = SDL_ASSERTION_RETRY;
259
break;
260
case 'i':
261
state = SDL_ASSERTION_IGNORE;
262
break;
263
case 'A':
264
state = SDL_ASSERTION_ALWAYS_IGNORE;
265
break;
266
default:
267
okay = false;
268
break;
269
}
270
271
if (okay) {
272
break;
273
}
274
}
275
#elif defined(HAVE_STDIO_H) && !defined(SDL_PLATFORM_3DS)
276
// this is a little hacky.
277
for (;;) {
278
char buf[32];
279
(void)fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
280
(void)fflush(stderr);
281
if (fgets(buf, sizeof(buf), stdin) == NULL) {
282
break;
283
}
284
285
if (SDL_strncmp(buf, "a", 1) == 0) {
286
state = SDL_ASSERTION_ABORT;
287
break;
288
} else if (SDL_strncmp(buf, "b", 1) == 0) {
289
state = SDL_ASSERTION_BREAK;
290
break;
291
} else if (SDL_strncmp(buf, "r", 1) == 0) {
292
state = SDL_ASSERTION_RETRY;
293
break;
294
} else if (SDL_strncmp(buf, "i", 1) == 0) {
295
state = SDL_ASSERTION_IGNORE;
296
break;
297
} else if (SDL_strncmp(buf, "A", 1) == 0) {
298
state = SDL_ASSERTION_ALWAYS_IGNORE;
299
break;
300
}
301
}
302
#else
303
//SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Assertion Failed", message, window);
304
#endif // HAVE_STDIO_H
305
}
306
307
// Re-enter fullscreen mode
308
if (window) {
309
//SDL_RestoreWindow(window);
310
}
311
312
if (message != stack_buf) {
313
SDL_free(message);
314
}
315
316
return state;
317
}
318
319
SDL_AssertState SDL_ReportAssertion(SDL_AssertData *data, const char *func, const char *file, int line)
320
{
321
SDL_AssertState state = SDL_ASSERTION_IGNORE;
322
static int assertion_running = 0;
323
324
#ifndef SDL_THREADS_DISABLED
325
static SDL_SpinLock spinlock = 0;
326
SDL_LockSpinlock(&spinlock);
327
if (!assertion_mutex) { // never called SDL_Init()?
328
assertion_mutex = SDL_CreateMutex();
329
if (!assertion_mutex) {
330
SDL_UnlockSpinlock(&spinlock);
331
return SDL_ASSERTION_IGNORE; // oh well, I guess.
332
}
333
}
334
SDL_UnlockSpinlock(&spinlock);
335
336
SDL_LockMutex(assertion_mutex);
337
#endif // !SDL_THREADS_DISABLED
338
339
// doing this because Visual C is upset over assigning in the macro.
340
if (data->trigger_count == 0) {
341
data->function = func;
342
data->filename = file;
343
data->linenum = line;
344
}
345
346
SDL_AddAssertionToReport(data);
347
348
assertion_running++;
349
if (assertion_running > 1) { // assert during assert! Abort.
350
if (assertion_running == 2) {
351
SDL_AbortAssertion();
352
} else if (assertion_running == 3) { // Abort asserted!
353
SDL_ExitProcess(42);
354
} else {
355
while (1) { // do nothing but spin; what else can you do?!
356
}
357
}
358
}
359
360
if (!data->always_ignore) {
361
state = assertion_handler(data, assertion_userdata);
362
}
363
364
switch (state) {
365
case SDL_ASSERTION_ALWAYS_IGNORE:
366
state = SDL_ASSERTION_IGNORE;
367
data->always_ignore = true;
368
break;
369
370
case SDL_ASSERTION_IGNORE:
371
case SDL_ASSERTION_RETRY:
372
case SDL_ASSERTION_BREAK:
373
break; // macro handles these.
374
375
case SDL_ASSERTION_ABORT:
376
SDL_AbortAssertion();
377
// break; ...shouldn't return, but oh well.
378
}
379
380
assertion_running--;
381
382
#ifndef SDL_THREADS_DISABLED
383
SDL_UnlockMutex(assertion_mutex);
384
#endif
385
386
return state;
387
}
388
389
void SDL_AssertionsQuit(void)
390
{
391
#if SDL_ASSERT_LEVEL > 0
392
SDL_GenerateAssertionReport();
393
#ifndef SDL_THREADS_DISABLED
394
if (assertion_mutex) {
395
SDL_DestroyMutex(assertion_mutex);
396
assertion_mutex = NULL;
397
}
398
#endif
399
#endif // SDL_ASSERT_LEVEL > 0
400
}
401
402
void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
403
{
404
if (handler != NULL) {
405
assertion_handler = handler;
406
assertion_userdata = userdata;
407
} else {
408
assertion_handler = SDL_PromptAssertion;
409
assertion_userdata = NULL;
410
}
411
}
412
413
const SDL_AssertData *SDL_GetAssertionReport(void)
414
{
415
return triggered_assertions;
416
}
417
418
void SDL_ResetAssertionReport(void)
419
{
420
SDL_AssertData *next = NULL;
421
SDL_AssertData *item;
422
for (item = triggered_assertions; item; item = next) {
423
next = (SDL_AssertData *)item->next;
424
item->always_ignore = false;
425
item->trigger_count = 0;
426
item->next = NULL;
427
}
428
429
triggered_assertions = NULL;
430
}
431
432
SDL_AssertionHandler SDL_GetDefaultAssertionHandler(void)
433
{
434
return SDL_PromptAssertion;
435
}
436
437
SDL_AssertionHandler SDL_GetAssertionHandler(void **userdata)
438
{
439
if (userdata) {
440
*userdata = assertion_userdata;
441
}
442
return assertion_handler;
443
}
444
445