• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #if DETECT_OS_OHOS
48 /* There is a conflict with syslog.h */
49 #undef LOG_DEBUG
50 #undef LOG_INFO
51 #include <hilog/log.h>
52 #endif
53 
54 enum mesa_log_control {
55    MESA_LOG_CONTROL_NULL = 1 << 0,
56    MESA_LOG_CONTROL_FILE = 1 << 1,
57    MESA_LOG_CONTROL_SYSLOG = 1 << 2,
58    MESA_LOG_CONTROL_ANDROID = 1 << 3,
59    MESA_LOG_CONTROL_WINDBG = 1 << 4,
60    MESA_LOG_CONTROL_OHOS = 1 << 5,
61    MESA_LOG_CONTROL_LOGGER_MASK = 0xff,
62 
63    MESA_LOG_CONTROL_WAIT = 1 << 8,
64 };
65 
66 static const struct debug_control mesa_log_control_options[] = {
67    /* loggers */
68    { "null", MESA_LOG_CONTROL_NULL },
69    { "file", MESA_LOG_CONTROL_FILE },
70    { "syslog", MESA_LOG_CONTROL_SYSLOG },
71    { "android", MESA_LOG_CONTROL_ANDROID },
72    { "windbg", MESA_LOG_CONTROL_WINDBG },
73    { "ohos", MESA_LOG_CONTROL_OHOS },
74    /* flags */
75    { "wait", MESA_LOG_CONTROL_WAIT },
76    { NULL, 0 },
77 };
78 
79 static uint32_t mesa_log_control;
80 static FILE *mesa_log_file;
81 
82 static void
mesa_log_init_once(void)83 mesa_log_init_once(void)
84 {
85    mesa_log_control = parse_debug_string(os_get_option("MESA_LOG"),
86          mesa_log_control_options);
87 
88    if (!(mesa_log_control & MESA_LOG_CONTROL_LOGGER_MASK)) {
89       /* pick the default loggers */
90 #if DETECT_OS_ANDROID
91       mesa_log_control |= MESA_LOG_CONTROL_ANDROID;
92 #elif DETECT_OS_OHOS
93       mesa_log_control |= MESA_LOG_CONTROL_OHOS;
94 #else
95       mesa_log_control |= MESA_LOG_CONTROL_FILE;
96 #endif
97 
98 #if DETECT_OS_WINDOWS
99       /* stderr from windows applications without console is not usually
100        * visible, so communicate with the debugger instead */
101       mesa_log_control |= MESA_LOG_CONTROL_WINDBG;
102 #endif
103    }
104 
105    mesa_log_file = stderr;
106 
107 #if !DETECT_OS_WINDOWS
108    if (__normal_user()) {
109       const char *log_file = os_get_option("MESA_LOG_FILE");
110       if (log_file) {
111          FILE *fp = fopen(log_file, "w");
112          if (fp) {
113             mesa_log_file = fp;
114             mesa_log_control |= MESA_LOG_CONTROL_FILE;
115          }
116       }
117    }
118 #endif
119 
120 #if DETECT_OS_POSIX
121    if (mesa_log_control & MESA_LOG_CONTROL_SYSLOG)
122       openlog(util_get_process_name(), LOG_NDELAY | LOG_PID, LOG_USER);
123 #endif
124 }
125 
126 static void
mesa_log_init(void)127 mesa_log_init(void)
128 {
129    static once_flag once = ONCE_FLAG_INIT;
130    call_once(&once, mesa_log_init_once);
131 }
132 
133 void
mesa_log_if_debug(enum mesa_log_level level,const char * outputString)134 mesa_log_if_debug(enum mesa_log_level level, const char *outputString)
135 {
136    static int debug = -1;
137 
138    /* Init the local 'debug' var once. */
139    if (debug == -1) {
140       const char *env = getenv("MESA_DEBUG");
141       bool silent = env && strstr(env, "silent") != NULL;
142 #ifndef NDEBUG
143       /* in debug builds, print messages unless MESA_DEBUG="silent" */
144       if (silent)
145          debug = 0;
146       else
147          debug = 1;
148 #else
149       /* in release builds, print messages if any MESA_DEBUG value other than
150        * MESA_DEBUG="silent" is set
151        */
152       debug = env && !silent;
153 #endif
154    }
155 
156    /* Now only print the string if we're required to do so. */
157    if (debug)
158       mesa_log(level, "Mesa", "%s", outputString);
159 }
160 
161 static inline const char *
level_to_str(enum mesa_log_level l)162 level_to_str(enum mesa_log_level l)
163 {
164    switch (l) {
165    case MESA_LOG_ERROR: return "error";
166    case MESA_LOG_WARN: return "warning";
167    case MESA_LOG_INFO: return "info";
168    case MESA_LOG_DEBUG: return "debug";
169    }
170 
171    unreachable("bad mesa_log_level");
172 }
173 
174 enum logger_vasnprintf_affix {
175    LOGGER_VASNPRINTF_AFFIX_TAG = 1 << 0,
176    LOGGER_VASNPRINTF_AFFIX_LEVEL = 1 << 1,
177    LOGGER_VASNPRINTF_AFFIX_NEWLINE = 1 << 2,
178 };
179 
180 /* Try vsnprintf first and fall back to vasprintf if buf is too small.  This
181  * function handles all errors and never fails.
182  */
183 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)184 logger_vasnprintf(char *buf,
185                   int size,
186                   int affixes,
187                   enum mesa_log_level level,
188                   const char *tag,
189                   const char *format,
190                   va_list in_va)
191 {
192    struct {
193       char *cur;
194       int rem;
195       int total;
196       bool invalid;
197    } state = {
198       .cur = buf,
199       .rem = size,
200    };
201 
202    va_list va;
203    va_copy(va, in_va);
204 
205 #define APPEND(state, func, ...)                                     \
206    do {                                                              \
207       int ret = func(state.cur, state.rem, __VA_ARGS__);             \
208       if (ret < 0) {                                                 \
209          state.invalid = true;                                       \
210       }  else {                                                      \
211          state.total += ret;                                         \
212          if (ret >= state.rem)                                       \
213             ret = state.rem;                                         \
214          state.cur += ret;                                           \
215          state.rem -= ret;                                           \
216       }                                                              \
217    } while (false)
218 
219    if (affixes & LOGGER_VASNPRINTF_AFFIX_TAG)
220       APPEND(state, snprintf, "%s: ", tag);
221    if (affixes & LOGGER_VASNPRINTF_AFFIX_LEVEL)
222       APPEND(state, snprintf, "%s: ", level_to_str(level));
223 
224    APPEND(state, vsnprintf, format, va);
225 
226    if (affixes & LOGGER_VASNPRINTF_AFFIX_NEWLINE) {
227       if (state.cur == buf || state.cur[-1] != '\n')
228          APPEND(state, snprintf, "\n");
229    }
230 #undef APPEND
231 
232    assert(size >= 64);
233    if (state.invalid) {
234       strncpy(buf, "invalid message format", size);
235    } else if (state.total >= size) {
236       /* print again into alloc to avoid truncation */
237       void *alloc = malloc(state.total + 1);
238       if (alloc) {
239          buf = logger_vasnprintf(alloc, state.total + 1, affixes, level, tag,
240                format, in_va);
241          assert(buf == alloc);
242       } else {
243          /* pretty-truncate the message */
244          strncpy(buf + size - 4, "...", 4);
245       }
246    }
247 
248    va_end(va);
249 
250    return buf;
251 }
252 
253 static void
logger_file(enum mesa_log_level level,const char * tag,const char * format,va_list va)254 logger_file(enum mesa_log_level level,
255             const char *tag,
256             const char *format,
257             va_list va)
258 {
259    FILE *fp = mesa_log_file;
260    char local_msg[1024];
261    char *msg = logger_vasnprintf(local_msg, sizeof(local_msg),
262          LOGGER_VASNPRINTF_AFFIX_TAG |
263          LOGGER_VASNPRINTF_AFFIX_LEVEL |
264          LOGGER_VASNPRINTF_AFFIX_NEWLINE,
265          level, tag, format, va);
266 
267    fprintf(fp, "%s", msg);
268    fflush(fp);
269 
270    if (msg != local_msg)
271       free(msg);
272 }
273 
274 #if DETECT_OS_POSIX
275 
276 static inline int
level_to_syslog(enum mesa_log_level l)277 level_to_syslog(enum mesa_log_level l)
278 {
279    switch (l) {
280    case MESA_LOG_ERROR: return LOG_ERR;
281    case MESA_LOG_WARN: return LOG_WARNING;
282    case MESA_LOG_INFO: return LOG_INFO;
283    case MESA_LOG_DEBUG: return LOG_DEBUG;
284    }
285 
286    unreachable("bad mesa_log_level");
287 }
288 
289 static void
logger_syslog(enum mesa_log_level level,const char * tag,const char * format,va_list va)290 logger_syslog(enum mesa_log_level level,
291               const char *tag,
292               const char *format,
293               va_list va)
294 {
295    char local_msg[1024];
296    char *msg = logger_vasnprintf(local_msg, sizeof(local_msg),
297          LOGGER_VASNPRINTF_AFFIX_TAG, level, tag, format, va);
298 
299    syslog(level_to_syslog(level), "%s", msg);
300 
301    if (msg != local_msg)
302       free(msg);
303 }
304 
305 #endif /* DETECT_OS_POSIX */
306 
307 #if DETECT_OS_ANDROID
308 
309 static inline android_LogPriority
level_to_android(enum mesa_log_level l)310 level_to_android(enum mesa_log_level l)
311 {
312    switch (l) {
313    case MESA_LOG_ERROR: return ANDROID_LOG_ERROR;
314    case MESA_LOG_WARN: return ANDROID_LOG_WARN;
315    case MESA_LOG_INFO: return ANDROID_LOG_INFO;
316    case MESA_LOG_DEBUG: return ANDROID_LOG_DEBUG;
317    }
318 
319    unreachable("bad mesa_log_level");
320 }
321 
322 static void
logger_android(enum mesa_log_level level,const char * tag,const char * format,va_list va)323 logger_android(enum mesa_log_level level,
324                const char *tag,
325                const char *format,
326                va_list va)
327 {
328    /* Android can truncate/drop messages
329     *
330     *  - the internal buffer for vsnprintf has a fixed size (usually 1024)
331     *  - the socket to logd is non-blocking
332     *
333     * and provides no way to detect.  Try our best.
334     */
335    char local_msg[1024];
336    char *msg = logger_vasnprintf(local_msg, sizeof(local_msg), 0, level, tag,
337          format, va);
338 
339    __android_log_write(level_to_android(level), tag, msg);
340 
341    if (msg != local_msg)
342       free(msg);
343 
344    /* increase the chance of logd doing its part */
345    if (mesa_log_control & MESA_LOG_CONTROL_WAIT)
346       thrd_yield();
347 }
348 
349 #endif /* DETECT_OS_ANDROID */
350 
351 #if DETECT_OS_WINDOWS
352 
353 static void
logger_windbg(enum mesa_log_level level,const char * tag,const char * format,va_list va)354 logger_windbg(enum mesa_log_level level,
355               const char *tag,
356               const char *format,
357               va_list va)
358 {
359    char local_msg[1024];
360    char *msg = logger_vasnprintf(local_msg, sizeof(local_msg),
361          LOGGER_VASNPRINTF_AFFIX_TAG |
362          LOGGER_VASNPRINTF_AFFIX_LEVEL |
363          LOGGER_VASNPRINTF_AFFIX_NEWLINE,
364          level, tag, format, va);
365 
366    OutputDebugStringA(msg);
367 
368    if (msg != local_msg)
369       free(msg);
370 }
371 
372 #endif /* DETECT_OS_WINDOWS */
373 
374 #if DETECT_OS_OHOS
375 
376 static inline LogLevel
level_to_ohos(enum mesa_log_level l)377 level_to_ohos(enum mesa_log_level l)
378 {
379    switch (l) {
380    case MESA_LOG_ERROR: return LOG_ERROR;
381    case MESA_LOG_WARN: return LOG_WARN;
382    case MESA_LOG_INFO: return LOG_INFO;
383    case MESA_LOG_DEBUG: return LOG_DEBUG;
384    }
385 
386    unreachable("bad mesa_log_level");
387 }
388 
389 static void
logger_ohos(enum mesa_log_level level,const char * tag,const char * format,va_list va)390 logger_ohos(enum mesa_log_level level,
391             const char *tag,
392             const char *format,
393             va_list va)
394 {
395    char local_msg[1024];
396    char *msg = logger_vasnprintf(local_msg, sizeof(local_msg), 0, level, tag,
397          format, va);
398 
399    HiLogPrint(LOG_APP, level_to_ohos(level), 0xFF00, tag, "%{public}s", msg);
400 }
401 
402 #endif /* DETECT_OS_OHOS */
403 
404 /* This is for use with debug functions that take a FILE, such as
405  * nir_print_shader, although switching to nir_log_shader* is preferred.
406  */
407 FILE *
mesa_log_get_file(void)408 mesa_log_get_file(void)
409 {
410    mesa_log_init();
411    return mesa_log_file;
412 }
413 
414 void
mesa_log(enum mesa_log_level level,const char * tag,const char * format,...)415 mesa_log(enum mesa_log_level level, const char *tag, const char *format, ...)
416 {
417    va_list va;
418 
419    va_start(va, format);
420    mesa_log_v(level, tag, format, va);
421    va_end(va);
422 }
423 
424 void
mesa_log_v(enum mesa_log_level level,const char * tag,const char * format,va_list va)425 mesa_log_v(enum mesa_log_level level, const char *tag, const char *format,
426             va_list va)
427 {
428    static const struct {
429       enum mesa_log_control bit;
430       void (*log)(enum mesa_log_level level,
431                   const char *tag,
432                   const char *format,
433                   va_list va);
434    } loggers[] = {
435       { MESA_LOG_CONTROL_FILE, logger_file },
436 #if DETECT_OS_POSIX
437       { MESA_LOG_CONTROL_SYSLOG, logger_syslog },
438 #endif
439 #if DETECT_OS_ANDROID
440       { MESA_LOG_CONTROL_ANDROID, logger_android },
441 #endif
442 #if DETECT_OS_WINDOWS
443       { MESA_LOG_CONTROL_WINDBG, logger_windbg },
444 #endif
445 #if DETECT_OS_OHOS
446       { MESA_LOG_CONTROL_OHOS, logger_ohos },
447 #endif
448    };
449 
450    mesa_log_init();
451 
452    for (uint32_t i = 0; i < ARRAY_SIZE(loggers); i++) {
453       if (mesa_log_control & loggers[i].bit) {
454          va_list copy;
455          va_copy(copy, va);
456          loggers[i].log(level, tag, format, copy);
457          va_end(copy);
458       }
459    }
460 }
461 
462 void
_mesa_log(const char * fmtString,...)463 _mesa_log(const char *fmtString, ...)
464 {
465    char s[MAX_LOG_MESSAGE_LENGTH];
466    va_list args;
467    va_start(args, fmtString);
468    vsnprintf(s, MAX_LOG_MESSAGE_LENGTH, fmtString, args);
469    va_end(args);
470    mesa_log_if_debug(MESA_LOG_INFO, s);
471 }
472 
473 void
_mesa_log_direct(const char * string)474 _mesa_log_direct(const char *string)
475 {
476    mesa_log_if_debug(MESA_LOG_INFO, string);
477 }
478 
479 struct log_stream *
_mesa_log_stream_create(enum mesa_log_level level,const char * tag)480 _mesa_log_stream_create(enum mesa_log_level level, const char *tag)
481 {
482    struct log_stream *stream = ralloc(NULL, struct log_stream);
483    stream->level = level;
484    stream->tag = tag;
485    stream->msg = ralloc_strdup(stream, "");
486    stream->pos = 0;
487    return stream;
488 }
489 
490 void
mesa_log_stream_destroy(struct log_stream * stream)491 mesa_log_stream_destroy(struct log_stream *stream)
492 {
493    /* If you left trailing stuff in the log stream, flush it out as a line. */
494    if (stream->pos != 0)
495       mesa_log(stream->level, stream->tag, "%s", stream->msg);
496 
497    ralloc_free(stream);
498 }
499 
500 static void
mesa_log_stream_flush(struct log_stream * stream,size_t scan_offset)501 mesa_log_stream_flush(struct log_stream *stream, size_t scan_offset)
502 {
503    char *end;
504    char *next = stream->msg;
505    while ((end = strchr(stream->msg + scan_offset, '\n'))) {
506       *end = 0;
507       mesa_log(stream->level, stream->tag, "%s", next);
508       next = end + 1;
509       scan_offset = next - stream->msg;
510    }
511    if (next != stream->msg) {
512       /* Clear out the lines we printed and move any trailing chars to the start. */
513       size_t remaining = stream->msg + stream->pos - next;
514       memmove(stream->msg, next, remaining);
515       stream->pos = remaining;
516    }
517 }
518 
mesa_log_stream_printf(struct log_stream * stream,const char * format,...)519 void mesa_log_stream_printf(struct log_stream *stream, const char *format, ...)
520 {
521    size_t old_pos = stream->pos;
522 
523    va_list va;
524    va_start(va, format);
525    ralloc_vasprintf_rewrite_tail(&stream->msg, &stream->pos, format, va);
526    va_end(va);
527 
528    mesa_log_stream_flush(stream, old_pos);
529 }
530 
531 void
_mesa_log_multiline(enum mesa_log_level level,const char * tag,const char * lines)532 _mesa_log_multiline(enum mesa_log_level level, const char *tag, const char *lines)
533 {
534    struct log_stream tmp = {
535       .level = level,
536       .tag = tag,
537       .msg = strdup(lines),
538       .pos = strlen(lines),
539    };
540    mesa_log_stream_flush(&tmp, 0);
541    free(tmp.msg);
542 }
543