1 /*
2 * Copyright © 2017 Google, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include "c11/threads.h"
29 #include "util/detect_os.h"
30 #include "util/log.h"
31 #include "util/ralloc.h"
32 #include "util/u_debug.h"
33
34 #if DETECT_OS_POSIX
35 #include <syslog.h>
36 #include "util/u_process.h"
37 #endif
38
39 #if DETECT_OS_ANDROID
40 #include <android/log.h>
41 #endif
42
43 #if DETECT_OS_WINDOWS
44 #include <windows.h>
45 #endif
46
47 enum mesa_log_control {
48 MESA_LOG_CONTROL_NULL = 1 << 0,
49 MESA_LOG_CONTROL_FILE = 1 << 1,
50 MESA_LOG_CONTROL_SYSLOG = 1 << 2,
51 MESA_LOG_CONTROL_ANDROID = 1 << 3,
52 MESA_LOG_CONTROL_WINDBG = 1 << 4,
53 MESA_LOG_CONTROL_LOGGER_MASK = 0xff,
54
55 MESA_LOG_CONTROL_WAIT = 1 << 8,
56 };
57
58 static const struct debug_control mesa_log_control_options[] = {
59 /* loggers */
60 { "null", MESA_LOG_CONTROL_NULL },
61 { "file", MESA_LOG_CONTROL_FILE },
62 { "syslog", MESA_LOG_CONTROL_SYSLOG },
63 { "android", MESA_LOG_CONTROL_ANDROID },
64 { "windbg", MESA_LOG_CONTROL_WINDBG },
65 /* flags */
66 { "wait", MESA_LOG_CONTROL_WAIT },
67 { NULL, 0 },
68 };
69
70 static uint32_t mesa_log_control;
71 static FILE *mesa_log_file;
72
73 static void
mesa_log_init_once(void)74 mesa_log_init_once(void)
75 {
76 mesa_log_control = parse_debug_string(os_get_option("MESA_LOG"),
77 mesa_log_control_options);
78
79 if (!(mesa_log_control & MESA_LOG_CONTROL_LOGGER_MASK)) {
80 /* pick the default loggers */
81 #if DETECT_OS_ANDROID
82 mesa_log_control |= MESA_LOG_CONTROL_ANDROID;
83 #else
84 mesa_log_control |= MESA_LOG_CONTROL_FILE;
85 #endif
86
87 #if DETECT_OS_WINDOWS
88 /* stderr from windows applications without console is not usually
89 * visible, so communicate with the debugger instead */
90 mesa_log_control |= MESA_LOG_CONTROL_WINDBG;
91 #endif
92 }
93
94 mesa_log_file = stderr;
95
96 #if !DETECT_OS_WINDOWS
97 if (__normal_user()) {
98 const char *log_file = os_get_option("MESA_LOG_FILE");
99 if (log_file) {
100 FILE *fp = fopen(log_file, "w");
101 if (fp) {
102 mesa_log_file = fp;
103 mesa_log_control |= MESA_LOG_CONTROL_FILE;
104 }
105 }
106 }
107 #endif
108
109 #if DETECT_OS_POSIX
110 if (mesa_log_control & MESA_LOG_CONTROL_SYSLOG)
111 openlog(util_get_process_name(), LOG_NDELAY | LOG_PID, LOG_USER);
112 #endif
113 }
114
115 static void
mesa_log_init(void)116 mesa_log_init(void)
117 {
118 static once_flag once = ONCE_FLAG_INIT;
119 call_once(&once, mesa_log_init_once);
120 }
121
122 void
mesa_log_if_debug(enum mesa_log_level level,const char * outputString)123 mesa_log_if_debug(enum mesa_log_level level, const char *outputString)
124 {
125 static int debug = -1;
126
127 /* Init the local 'debug' var once. */
128 if (debug == -1) {
129 const char *env = getenv("MESA_DEBUG");
130 bool silent = env && strstr(env, "silent") != NULL;
131 #ifndef NDEBUG
132 /* in debug builds, print messages unless MESA_DEBUG="silent" */
133 if (silent)
134 debug = 0;
135 else
136 debug = 1;
137 #else
138 /* in release builds, print messages if any MESA_DEBUG value other than
139 * MESA_DEBUG="silent" is set
140 */
141 debug = env && !silent;
142 #endif
143 }
144
145 /* Now only print the string if we're required to do so. */
146 if (debug)
147 mesa_log(level, "Mesa", "%s", outputString);
148 }
149
150 static inline const char *
level_to_str(enum mesa_log_level l)151 level_to_str(enum mesa_log_level l)
152 {
153 switch (l) {
154 case MESA_LOG_ERROR: return "error";
155 case MESA_LOG_WARN: return "warning";
156 case MESA_LOG_INFO: return "info";
157 case MESA_LOG_DEBUG: return "debug";
158 }
159
160 unreachable("bad mesa_log_level");
161 }
162
163 enum logger_vasnprintf_affix {
164 LOGGER_VASNPRINTF_AFFIX_TAG = 1 << 0,
165 LOGGER_VASNPRINTF_AFFIX_LEVEL = 1 << 1,
166 LOGGER_VASNPRINTF_AFFIX_NEWLINE = 1 << 2,
167 };
168
169 /* Try vsnprintf first and fall back to vasprintf if buf is too small. This
170 * function handles all errors and never fails.
171 */
172 static char *
logger_vasnprintf(char * buf,int size,int affixes,enum mesa_log_level level,const char * tag,const char * format,va_list in_va)173 logger_vasnprintf(char *buf,
174 int size,
175 int affixes,
176 enum mesa_log_level level,
177 const char *tag,
178 const char *format,
179 va_list in_va)
180 {
181 struct {
182 char *cur;
183 int rem;
184 int total;
185 bool invalid;
186 } state = {
187 .cur = buf,
188 .rem = size,
189 };
190
191 va_list va;
192 va_copy(va, in_va);
193
194 #define APPEND(state, func, ...) \
195 do { \
196 int ret = func(state.cur, state.rem, __VA_ARGS__); \
197 if (ret < 0) { \
198 state.invalid = true; \
199 } else { \
200 state.total += ret; \
201 if (ret >= state.rem) \
202 ret = state.rem; \
203 state.cur += ret; \
204 state.rem -= ret; \
205 } \
206 } while (false)
207
208 if (affixes & LOGGER_VASNPRINTF_AFFIX_TAG)
209 APPEND(state, snprintf, "%s: ", tag);
210 if (affixes & LOGGER_VASNPRINTF_AFFIX_LEVEL)
211 APPEND(state, snprintf, "%s: ", level_to_str(level));
212
213 APPEND(state, vsnprintf, format, va);
214
215 if (affixes & LOGGER_VASNPRINTF_AFFIX_NEWLINE) {
216 if (state.cur == buf || state.cur[-1] != '\n')
217 APPEND(state, snprintf, "\n");
218 }
219 #undef APPEND
220
221 assert(size >= 64);
222 if (state.invalid) {
223 strncpy(buf, "invalid message format", size);
224 } else if (state.total >= size) {
225 /* print again into alloc to avoid truncation */
226 void *alloc = malloc(state.total + 1);
227 if (alloc) {
228 buf = logger_vasnprintf(alloc, state.total + 1, affixes, level, tag,
229 format, in_va);
230 assert(buf == alloc);
231 } else {
232 /* pretty-truncate the message */
233 strncpy(buf + size - 4, "...", 4);
234 }
235 }
236
237 va_end(va);
238
239 return buf;
240 }
241
242 static void
logger_file(enum mesa_log_level level,const char * tag,const char * format,va_list va)243 logger_file(enum mesa_log_level level,
244 const char *tag,
245 const char *format,
246 va_list va)
247 {
248 FILE *fp = mesa_log_file;
249 char local_msg[1024];
250 char *msg = logger_vasnprintf(local_msg, sizeof(local_msg),
251 LOGGER_VASNPRINTF_AFFIX_TAG |
252 LOGGER_VASNPRINTF_AFFIX_LEVEL |
253 LOGGER_VASNPRINTF_AFFIX_NEWLINE,
254 level, tag, format, va);
255
256 fprintf(fp, "%s", msg);
257 fflush(fp);
258
259 if (msg != local_msg)
260 free(msg);
261 }
262
263 #if DETECT_OS_POSIX
264
265 static inline int
level_to_syslog(enum mesa_log_level l)266 level_to_syslog(enum mesa_log_level l)
267 {
268 switch (l) {
269 case MESA_LOG_ERROR: return LOG_ERR;
270 case MESA_LOG_WARN: return LOG_WARNING;
271 case MESA_LOG_INFO: return LOG_INFO;
272 case MESA_LOG_DEBUG: return LOG_DEBUG;
273 }
274
275 unreachable("bad mesa_log_level");
276 }
277
278 static void
logger_syslog(enum mesa_log_level level,const char * tag,const char * format,va_list va)279 logger_syslog(enum mesa_log_level level,
280 const char *tag,
281 const char *format,
282 va_list va)
283 {
284 char local_msg[1024];
285 char *msg = logger_vasnprintf(local_msg, sizeof(local_msg),
286 LOGGER_VASNPRINTF_AFFIX_TAG, level, tag, format, va);
287
288 syslog(level_to_syslog(level), "%s", msg);
289
290 if (msg != local_msg)
291 free(msg);
292 }
293
294 #endif /* DETECT_OS_POSIX */
295
296 #if DETECT_OS_ANDROID
297
298 static inline android_LogPriority
level_to_android(enum mesa_log_level l)299 level_to_android(enum mesa_log_level l)
300 {
301 switch (l) {
302 case MESA_LOG_ERROR: return ANDROID_LOG_ERROR;
303 case MESA_LOG_WARN: return ANDROID_LOG_WARN;
304 case MESA_LOG_INFO: return ANDROID_LOG_INFO;
305 case MESA_LOG_DEBUG: return ANDROID_LOG_DEBUG;
306 }
307
308 unreachable("bad mesa_log_level");
309 }
310
311 static void
logger_android(enum mesa_log_level level,const char * tag,const char * format,va_list va)312 logger_android(enum mesa_log_level level,
313 const char *tag,
314 const char *format,
315 va_list va)
316 {
317 /* Android can truncate/drop messages
318 *
319 * - the internal buffer for vsnprintf has a fixed size (usually 1024)
320 * - the socket to logd is non-blocking
321 *
322 * and provides no way to detect. Try our best.
323 */
324 char local_msg[1024];
325 char *msg = logger_vasnprintf(local_msg, sizeof(local_msg), 0, level, tag,
326 format, va);
327
328 __android_log_write(level_to_android(level), tag, msg);
329
330 if (msg != local_msg)
331 free(msg);
332
333 /* increase the chance of logd doing its part */
334 if (mesa_log_control & MESA_LOG_CONTROL_WAIT)
335 thrd_yield();
336 }
337
338 #endif /* DETECT_OS_ANDROID */
339
340 #if DETECT_OS_WINDOWS
341
342 static void
logger_windbg(enum mesa_log_level level,const char * tag,const char * format,va_list va)343 logger_windbg(enum mesa_log_level level,
344 const char *tag,
345 const char *format,
346 va_list va)
347 {
348 char local_msg[1024];
349 char *msg = logger_vasnprintf(local_msg, sizeof(local_msg),
350 LOGGER_VASNPRINTF_AFFIX_TAG |
351 LOGGER_VASNPRINTF_AFFIX_LEVEL |
352 LOGGER_VASNPRINTF_AFFIX_NEWLINE,
353 level, tag, format, va);
354
355 OutputDebugStringA(msg);
356
357 if (msg != local_msg)
358 free(msg);
359 }
360
361 #endif /* DETECT_OS_WINDOWS */
362
363 /* This is for use with debug functions that take a FILE, such as
364 * nir_print_shader, although switching to nir_log_shader* is preferred.
365 */
366 FILE *
mesa_log_get_file(void)367 mesa_log_get_file(void)
368 {
369 mesa_log_init();
370 return mesa_log_file;
371 }
372
373 void
mesa_log(enum mesa_log_level level,const char * tag,const char * format,...)374 mesa_log(enum mesa_log_level level, const char *tag, const char *format, ...)
375 {
376 va_list va;
377
378 va_start(va, format);
379 mesa_log_v(level, tag, format, va);
380 va_end(va);
381 }
382
383 void
mesa_log_v(enum mesa_log_level level,const char * tag,const char * format,va_list va)384 mesa_log_v(enum mesa_log_level level, const char *tag, const char *format,
385 va_list va)
386 {
387 static const struct {
388 enum mesa_log_control bit;
389 void (*log)(enum mesa_log_level level,
390 const char *tag,
391 const char *format,
392 va_list va);
393 } loggers[] = {
394 { MESA_LOG_CONTROL_FILE, logger_file },
395 #if DETECT_OS_POSIX
396 { MESA_LOG_CONTROL_SYSLOG, logger_syslog },
397 #endif
398 #if DETECT_OS_ANDROID
399 { MESA_LOG_CONTROL_ANDROID, logger_android },
400 #endif
401 #if DETECT_OS_WINDOWS
402 { MESA_LOG_CONTROL_WINDBG, logger_windbg },
403 #endif
404 };
405
406 mesa_log_init();
407
408 for (uint32_t i = 0; i < ARRAY_SIZE(loggers); i++) {
409 if (mesa_log_control & loggers[i].bit) {
410 va_list copy;
411 va_copy(copy, va);
412 loggers[i].log(level, tag, format, copy);
413 va_end(copy);
414 }
415 }
416 }
417
418 void
_mesa_log(const char * fmtString,...)419 _mesa_log(const char *fmtString, ...)
420 {
421 char s[MAX_LOG_MESSAGE_LENGTH];
422 va_list args;
423 va_start(args, fmtString);
424 vsnprintf(s, MAX_LOG_MESSAGE_LENGTH, fmtString, args);
425 va_end(args);
426 mesa_log_if_debug(MESA_LOG_INFO, s);
427 }
428
429 void
_mesa_log_direct(const char * string)430 _mesa_log_direct(const char *string)
431 {
432 mesa_log_if_debug(MESA_LOG_INFO, string);
433 }
434
435 struct log_stream *
_mesa_log_stream_create(enum mesa_log_level level,const char * tag)436 _mesa_log_stream_create(enum mesa_log_level level, const char *tag)
437 {
438 struct log_stream *stream = ralloc(NULL, struct log_stream);
439 stream->level = level;
440 stream->tag = tag;
441 stream->msg = ralloc_strdup(stream, "");
442 stream->pos = 0;
443 return stream;
444 }
445
446 void
mesa_log_stream_destroy(struct log_stream * stream)447 mesa_log_stream_destroy(struct log_stream *stream)
448 {
449 /* If you left trailing stuff in the log stream, flush it out as a line. */
450 if (stream->pos != 0)
451 mesa_log(stream->level, stream->tag, "%s", stream->msg);
452
453 ralloc_free(stream);
454 }
455
456 static void
mesa_log_stream_flush(struct log_stream * stream,size_t scan_offset)457 mesa_log_stream_flush(struct log_stream *stream, size_t scan_offset)
458 {
459 char *end;
460 char *next = stream->msg;
461 while ((end = strchr(stream->msg + scan_offset, '\n'))) {
462 *end = 0;
463 mesa_log(stream->level, stream->tag, "%s", next);
464 next = end + 1;
465 scan_offset = next - stream->msg;
466 }
467 if (next != stream->msg) {
468 /* Clear out the lines we printed and move any trailing chars to the start. */
469 size_t remaining = stream->msg + stream->pos - next;
470 memmove(stream->msg, next, remaining);
471 stream->pos = remaining;
472 }
473 }
474
mesa_log_stream_printf(struct log_stream * stream,const char * format,...)475 void mesa_log_stream_printf(struct log_stream *stream, const char *format, ...)
476 {
477 size_t old_pos = stream->pos;
478
479 va_list va;
480 va_start(va, format);
481 ralloc_vasprintf_rewrite_tail(&stream->msg, &stream->pos, format, va);
482 va_end(va);
483
484 mesa_log_stream_flush(stream, old_pos);
485 }
486
487 void
_mesa_log_multiline(enum mesa_log_level level,const char * tag,const char * lines)488 _mesa_log_multiline(enum mesa_log_level level, const char *tag, const char *lines)
489 {
490 struct log_stream tmp = {
491 .level = level,
492 .tag = tag,
493 .msg = strdup(lines),
494 .pos = strlen(lines),
495 };
496 mesa_log_stream_flush(&tmp, 0);
497 free(tmp.msg);
498 }
499