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