Change std:vector to eina_array
[platform/upstream/SDL.git] / src / SDL_log.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 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__) || defined(__WINRT__)
24 #include "core/windows/SDL_windows.h"
25 #endif
26
27 /* Simple log messages in SDL */
28
29 #include "SDL_error.h"
30 #include "SDL_log.h"
31
32 #if HAVE_STDIO_H
33 #include <stdio.h>
34 #endif
35
36 #if defined(__ANDROID__)
37 #include <android/log.h>
38 #endif
39
40 #ifndef __TIZEN__
41 #define __TIZEN__
42 #endif
43
44 #if defined(__TIZEN__)
45 #include <dlog/dlog.h>
46 #ifdef LOG_TAG
47 #undef LOG_TAG
48 #endif
49 #define LOG_TAG "SDL_LOG"
50 #define _SECURE_LOG
51 #endif
52
53
54
55 #define DEFAULT_PRIORITY                SDL_LOG_PRIORITY_CRITICAL
56 #define DEFAULT_ASSERT_PRIORITY         SDL_LOG_PRIORITY_WARN
57 #define DEFAULT_APPLICATION_PRIORITY    SDL_LOG_PRIORITY_INFO
58 #define DEFAULT_TEST_PRIORITY           SDL_LOG_PRIORITY_VERBOSE
59
60 typedef struct SDL_LogLevel
61 {
62     int category;
63     SDL_LogPriority priority;
64     struct SDL_LogLevel *next;
65 } SDL_LogLevel;
66
67 /* The default log output function */
68 static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, const char *message);
69
70 static SDL_LogLevel *SDL_loglevels;
71 static SDL_LogPriority SDL_default_priority = DEFAULT_PRIORITY;
72 static SDL_LogPriority SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
73 static SDL_LogPriority SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
74 static SDL_LogPriority SDL_test_priority = DEFAULT_TEST_PRIORITY;
75 static SDL_LogOutputFunction SDL_log_function = SDL_LogOutput;
76 static void *SDL_log_userdata = NULL;
77
78 static const char *SDL_priority_prefixes[SDL_NUM_LOG_PRIORITIES] = {
79     NULL,
80     "VERBOSE",
81     "DEBUG",
82     "INFO",
83     "WARN",
84     "ERROR",
85     "CRITICAL"
86 };
87
88 #if defined(__TIZEN__)
89 static const int SDL_dlog_debug_priority[SDL_NUM_LOG_PRIORITIES] = {
90     DLOG_UNKNOWN,
91     DLOG_VERBOSE,
92     DLOG_DEBUG,
93     DLOG_INFO,
94     DLOG_WARN,
95     DLOG_ERROR,
96     DLOG_FATAL
97 };
98 #endif
99
100 #ifdef __ANDROID__
101 static const char *SDL_category_prefixes[SDL_LOG_CATEGORY_RESERVED1] = {
102     "APP",
103     "ERROR",
104     "SYSTEM",
105     "AUDIO",
106     "VIDEO",
107     "RENDER",
108     "INPUT"
109 };
110
111 static int SDL_android_priority[SDL_NUM_LOG_PRIORITIES] = {
112     ANDROID_LOG_UNKNOWN,
113     ANDROID_LOG_VERBOSE,
114     ANDROID_LOG_DEBUG,
115     ANDROID_LOG_INFO,
116     ANDROID_LOG_WARN,
117     ANDROID_LOG_ERROR,
118     ANDROID_LOG_FATAL
119 };
120 #endif /* __ANDROID__ */
121
122
123 void
124 SDL_LogSetAllPriority(SDL_LogPriority priority)
125 {
126     SDL_LogLevel *entry;
127
128     for (entry = SDL_loglevels; entry; entry = entry->next) {
129         entry->priority = priority;
130     }
131     SDL_default_priority = priority;
132     SDL_assert_priority = priority;
133     SDL_application_priority = priority;
134 }
135
136 void
137 SDL_LogSetPriority(int category, SDL_LogPriority priority)
138 {
139     SDL_LogLevel *entry;
140
141     for (entry = SDL_loglevels; entry; entry = entry->next) {
142         if (entry->category == category) {
143             entry->priority = priority;
144             return;
145         }
146     }
147
148     /* Create a new entry */
149     entry = (SDL_LogLevel *)SDL_malloc(sizeof(*entry));
150     if (entry) {
151         entry->category = category;
152         entry->priority = priority;
153         entry->next = SDL_loglevels;
154         SDL_loglevels = entry;
155     }
156 }
157
158 SDL_LogPriority
159 SDL_LogGetPriority(int category)
160 {
161     SDL_LogLevel *entry;
162
163     for (entry = SDL_loglevels; entry; entry = entry->next) {
164         if (entry->category == category) {
165             return entry->priority;
166         }
167     }
168
169     if (category == SDL_LOG_CATEGORY_TEST) {
170         return SDL_test_priority;
171     } else if (category == SDL_LOG_CATEGORY_APPLICATION) {
172         return SDL_application_priority;
173     } else if (category == SDL_LOG_CATEGORY_ASSERT) {
174         return SDL_assert_priority;
175     } else {
176         return SDL_default_priority;
177     }
178 }
179
180 void
181 SDL_LogResetPriorities(void)
182 {
183     SDL_LogLevel *entry;
184
185     while (SDL_loglevels) {
186         entry = SDL_loglevels;
187         SDL_loglevels = entry->next;
188         SDL_free(entry);
189     }
190
191     SDL_default_priority = DEFAULT_PRIORITY;
192     SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
193     SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
194     SDL_test_priority = DEFAULT_TEST_PRIORITY;
195 }
196
197 void
198 SDL_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
199 {
200     va_list ap;
201
202     va_start(ap, fmt);
203     SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap);
204     va_end(ap);
205 }
206
207 void
208 SDL_LogVerbose(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
209 {
210     va_list ap;
211
212     va_start(ap, fmt);
213     SDL_LogMessageV(category, SDL_LOG_PRIORITY_VERBOSE, fmt, ap);
214     va_end(ap);
215 }
216
217 void
218 SDL_LogDebug(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
219 {
220     va_list ap;
221
222     va_start(ap, fmt);
223     SDL_LogMessageV(category, SDL_LOG_PRIORITY_DEBUG, fmt, ap);
224     va_end(ap);
225 }
226
227 void
228 SDL_LogInfo(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
229 {
230     va_list ap;
231
232     va_start(ap, fmt);
233     SDL_LogMessageV(category, SDL_LOG_PRIORITY_INFO, fmt, ap);
234     va_end(ap);
235 }
236
237 void
238 SDL_LogWarn(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
239 {
240     va_list ap;
241
242     va_start(ap, fmt);
243     SDL_LogMessageV(category, SDL_LOG_PRIORITY_WARN, fmt, ap);
244     va_end(ap);
245 }
246
247 void
248 SDL_LogError(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
249 {
250     va_list ap;
251
252     va_start(ap, fmt);
253     SDL_LogMessageV(category, SDL_LOG_PRIORITY_ERROR, fmt, ap);
254     va_end(ap);
255 }
256
257 void
258 SDL_LogCritical(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
259 {
260     va_list ap;
261
262     va_start(ap, fmt);
263     SDL_LogMessageV(category, SDL_LOG_PRIORITY_CRITICAL, fmt, ap);
264     va_end(ap);
265 }
266
267 void
268 SDL_LogMessage(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
269 {
270     va_list ap;
271
272     va_start(ap, fmt);
273     SDL_LogMessageV(category, priority, fmt, ap);
274     va_end(ap);
275 }
276
277 #ifdef __ANDROID__
278 static const char *
279 GetCategoryPrefix(int category)
280 {
281     if (category < SDL_LOG_CATEGORY_RESERVED1) {
282         return SDL_category_prefixes[category];
283     }
284     if (category < SDL_LOG_CATEGORY_CUSTOM) {
285         return "RESERVED";
286     }
287     return "CUSTOM";
288 }
289 #endif /* __ANDROID__ */
290
291 void
292 SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list ap)
293 {
294     char *message;
295     size_t len;
296
297     /* Nothing to do if we don't have an output function */
298     if (!SDL_log_function) {
299         return;
300     }
301
302     /* Make sure we don't exceed array bounds */
303     if ((int)priority < 0 || priority >= SDL_NUM_LOG_PRIORITIES) {
304         return;
305     }
306
307     /* See if we want to do anything with this message */
308     if (priority < SDL_LogGetPriority(category)) {
309         return;
310     }
311
312     /* !!! FIXME: why not just "char message[SDL_MAX_LOG_MESSAGE];" ? */
313     message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
314     if (!message) {
315         return;
316     }
317
318     SDL_vsnprintf(message, SDL_MAX_LOG_MESSAGE, fmt, ap);
319
320     /* Chop off final endline. */
321     len = SDL_strlen(message);
322     if ((len > 0) && (message[len-1] == '\n')) {
323         message[--len] = '\0';
324         if ((len > 0) && (message[len-1] == '\r')) {  /* catch "\r\n", too. */
325             message[--len] = '\0';
326         }
327     }
328
329     SDL_log_function(SDL_log_userdata, category, priority, message);
330     SDL_stack_free(message);
331 }
332
333 #if defined(__WIN32__) && !defined(HAVE_STDIO_H) && !defined(__WINRT__)
334 /* Flag tracking the attachment of the console: 0=unattached, 1=attached to a console, 2=attached to a file, -1=error */
335 static int consoleAttached = 0;
336
337 /* Handle to stderr output of console. */
338 static HANDLE stderrHandle = NULL;
339 #endif
340
341
342 #if defined(__TIZEN__)
343 static void
344 SDL_PrintDlog(int priority, char *format, ...)
345 {
346     va_list ap;
347
348     if (priority >= SDL_NUM_LOG_PRIORITIES)
349         return;
350
351     va_start(ap, format);
352     dlog_vprint(SDL_dlog_debug_priority[priority], LOG_TAG, format, ap);
353     va_end(ap);
354 }
355 #endif
356
357 static void SDLCALL
358 SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
359               const char *message)
360 {
361 #if defined(__WIN32__) || defined(__WINRT__)
362     /* Way too many allocations here, urgh */
363     /* Note: One can't call SDL_SetError here, since that function itself logs. */
364     {
365         char *output;
366         size_t length;
367         LPTSTR tstr;
368         SDL_bool isstack;
369
370 #if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
371         BOOL attachResult;
372         DWORD attachError;
373         unsigned long charsWritten; 
374         DWORD consoleMode;
375
376         /* Maybe attach console and get stderr handle */
377         if (consoleAttached == 0) {
378             attachResult = AttachConsole(ATTACH_PARENT_PROCESS);
379             if (!attachResult) {
380                     attachError = GetLastError();
381                     if (attachError == ERROR_INVALID_HANDLE) {
382                         /* This is expected when running from Visual Studio */
383                         /*OutputDebugString(TEXT("Parent process has no console\r\n"));*/
384                         consoleAttached = -1;
385                     } else if (attachError == ERROR_GEN_FAILURE) {
386                          OutputDebugString(TEXT("Could not attach to console of parent process\r\n"));
387                          consoleAttached = -1;
388                     } else if (attachError == ERROR_ACCESS_DENIED) {  
389                          /* Already attached */
390                         consoleAttached = 1;
391                     } else {
392                         OutputDebugString(TEXT("Error attaching console\r\n"));
393                         consoleAttached = -1;
394                     }
395                 } else {
396                     /* Newly attached */
397                     consoleAttached = 1;
398                 }
399
400                 if (consoleAttached == 1) {
401                         stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
402
403                         if (GetConsoleMode(stderrHandle, &consoleMode) == 0) {
404                             /* WriteConsole fails if the output is redirected to a file. Must use WriteFile instead. */
405                             consoleAttached = 2;
406                         }
407                 }
408         }
409 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
410
411         length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1 + 1;
412         output = SDL_small_alloc(char, length, &isstack);
413         SDL_snprintf(output, length, "%s: %s\r\n", SDL_priority_prefixes[priority], message);
414         tstr = WIN_UTF8ToString(output);
415         
416         /* Output to debugger */
417         OutputDebugString(tstr);
418        
419 #if !defined(HAVE_STDIO_H) && !defined(__WINRT__)
420         /* Screen output to stderr, if console was attached. */
421         if (consoleAttached == 1) {
422                 if (!WriteConsole(stderrHandle, tstr, lstrlen(tstr), &charsWritten, NULL)) {
423                     OutputDebugString(TEXT("Error calling WriteConsole\r\n"));
424                     if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
425                         OutputDebugString(TEXT("Insufficient heap memory to write message\r\n"));
426                     }
427                 }
428
429         } else if (consoleAttached == 2) {
430             if (!WriteFile(stderrHandle, output, lstrlenA(output), &charsWritten, NULL)) {
431                 OutputDebugString(TEXT("Error calling WriteFile\r\n"));
432             }
433         }
434 #endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) */
435
436         SDL_free(tstr);
437         SDL_small_free(output, isstack);
438     }
439 #elif defined(__ANDROID__)
440     {
441         char tag[32];
442
443         SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category));
444         __android_log_write(SDL_android_priority[priority], tag, message);
445     }
446 #elif defined(__APPLE__) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT))
447     /* Technically we don't need Cocoa/UIKit, but that's where this function is defined for now.
448     */
449     extern void SDL_NSLog(const char *text);
450     {
451         char *text;
452         /* !!! FIXME: why not just "char text[SDL_MAX_LOG_MESSAGE];" ? */
453         text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
454         if (text) {
455             SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message);
456             SDL_NSLog(text);
457             SDL_stack_free(text);
458             return;
459         }
460     }
461 #elif defined(__PSP__)
462     {
463         FILE*        pFile;
464         pFile = fopen ("SDL_Log.txt", "a");
465         fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
466         fclose (pFile);
467     }
468 #elif defined(__TIZEN__)
469     {
470         SDL_PrintDlog(priority, "%s: %s", SDL_priority_prefixes[priority], message);
471     }
472 #endif
473 #if HAVE_STDIO_H
474     fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message);
475 #endif
476 #if __NACL__
477     fflush(stderr);
478 #endif
479 }
480
481 void
482 SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
483 {
484     if (callback) {
485         *callback = SDL_log_function;
486     }
487     if (userdata) {
488         *userdata = SDL_log_userdata;
489     }
490 }
491
492 void
493 SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata)
494 {
495     SDL_log_function = callback;
496     SDL_log_userdata = userdata;
497 }
498
499 /* vi: set ts=4 sw=4 expandtab: */