]> code.delx.au - pulseaudio/blob - src/pulsecore/log.c
don't show full so path in backtrace
[pulseaudio] / src / pulsecore / log.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32
33 #ifdef HAVE_EXECINFO_H
34 #include <execinfo.h>
35 #endif
36
37 #ifdef HAVE_SYSLOG_H
38 #include <syslog.h>
39 #endif
40
41 #include <pulse/utf8.h>
42 #include <pulse/xmalloc.h>
43 #include <pulse/util.h>
44 #include <pulse/timeval.h>
45
46 #include <pulsecore/macro.h>
47 #include <pulsecore/core-util.h>
48 #include <pulsecore/rtclock.h>
49 #include <pulsecore/once.h>
50 #include <pulsecore/ratelimit.h>
51
52 #include "log.h"
53
54 #define ENV_LOG_SYSLOG "PULSE_LOG_SYSLOG"
55 #define ENV_LOG_LEVEL "PULSE_LOG"
56 #define ENV_LOG_COLORS "PULSE_LOG_COLORS"
57 #define ENV_LOG_PRINT_TIME "PULSE_LOG_TIME"
58 #define ENV_LOG_PRINT_FILE "PULSE_LOG_FILE"
59 #define ENV_LOG_PRINT_META "PULSE_LOG_META"
60 #define ENV_LOG_PRINT_LEVEL "PULSE_LOG_LEVEL"
61 #define ENV_LOG_BACKTRACE "PULSE_LOG_BACKTRACE"
62
63 static char *ident = NULL; /* in local charset format */
64 static pa_log_target_t target = PA_LOG_STDERR, target_override;
65 static pa_bool_t target_override_set = FALSE;
66 static pa_log_level_t maximum_level = PA_LOG_ERROR, maximum_level_override = PA_LOG_ERROR;
67 static unsigned show_backtrace = 0, show_backtrace_override = 0;
68 static pa_log_flags_t flags = 0, flags_override = 0;
69
70 #ifdef HAVE_SYSLOG_H
71 static const int level_to_syslog[] = {
72 [PA_LOG_ERROR] = LOG_ERR,
73 [PA_LOG_WARN] = LOG_WARNING,
74 [PA_LOG_NOTICE] = LOG_NOTICE,
75 [PA_LOG_INFO] = LOG_INFO,
76 [PA_LOG_DEBUG] = LOG_DEBUG
77 };
78 #endif
79
80 static const char level_to_char[] = {
81 [PA_LOG_ERROR] = 'E',
82 [PA_LOG_WARN] = 'W',
83 [PA_LOG_NOTICE] = 'N',
84 [PA_LOG_INFO] = 'I',
85 [PA_LOG_DEBUG] = 'D'
86 };
87
88 void pa_log_set_ident(const char *p) {
89 pa_xfree(ident);
90
91 if (!(ident = pa_utf8_to_locale(p)))
92 ident = pa_ascii_filter(p);
93 }
94
95 /* To make valgrind shut up. */
96 static void ident_destructor(void) PA_GCC_DESTRUCTOR;
97 static void ident_destructor(void) {
98 if (!pa_in_valgrind())
99 return;
100
101 pa_xfree(ident);
102 }
103
104 void pa_log_set_level(pa_log_level_t l) {
105 pa_assert(l < PA_LOG_LEVEL_MAX);
106
107 maximum_level = l;
108 }
109
110 void pa_log_set_target(pa_log_target_t t) {
111 pa_assert(t < PA_LOG_TARGET_MAX);
112
113 target = t;
114 }
115
116 void pa_log_set_flags(pa_log_flags_t _flags, pa_log_merge_t merge) {
117 pa_assert(!(_flags & ~(PA_LOG_COLORS|PA_LOG_PRINT_TIME|PA_LOG_PRINT_FILE|PA_LOG_PRINT_META|PA_LOG_PRINT_LEVEL)));
118
119 if (merge == PA_LOG_SET)
120 flags |= _flags;
121 else if (merge == PA_LOG_UNSET)
122 flags &= ~_flags;
123 else
124 flags = _flags;
125 }
126
127 void pa_log_set_show_backtrace(unsigned nlevels) {
128 show_backtrace = nlevels;
129 }
130
131 #ifdef HAVE_EXECINFO_H
132
133 static char* get_backtrace(unsigned show_nframes) {
134 void* trace[32];
135 int n_frames;
136 char **symbols, *e, *r;
137 unsigned j, n;
138 size_t a;
139
140 pa_assert(show_nframes > 0);
141
142 n_frames = backtrace(trace, PA_ELEMENTSOF(trace));
143
144 if (n_frames <= 0)
145 return NULL;
146
147 symbols = backtrace_symbols(trace, n_frames);
148
149 if (!symbols)
150 return NULL;
151
152 n = PA_MIN((unsigned) n_frames, show_nframes);
153
154 a = 4;
155
156 for (j = 0; j < n; j++) {
157 if (j > 0)
158 a += 2;
159 a += strlen(pa_path_get_filename(symbols[j]));
160 }
161
162 r = pa_xnew(char, a);
163
164 strcpy(r, " (");
165 e = r + 2;
166
167 for (j = 0; j < n; j++) {
168 const char *sym;
169
170 if (j > 0) {
171 strcpy(e, "<<");
172 e += 2;
173 }
174
175 sym = pa_path_get_filename(symbols[j]);
176
177 strcpy(e, sym);
178 e += strlen(sym);
179 }
180
181 strcpy(e, ")");
182
183 free(symbols);
184
185 return r;
186 }
187
188 #endif
189
190 static void init_defaults(void) {
191 const char *e;
192
193 if (!ident) {
194 char binary[256];
195 if (pa_get_binary_name(binary, sizeof(binary)))
196 pa_log_set_ident(binary);
197 }
198
199 if (getenv(ENV_LOG_SYSLOG)) {
200 target_override = PA_LOG_SYSLOG;
201 target_override_set = TRUE;
202 }
203
204 if ((e = getenv(ENV_LOG_LEVEL))) {
205 maximum_level_override = (pa_log_level_t) atoi(e);
206
207 if (maximum_level_override >= PA_LOG_LEVEL_MAX)
208 maximum_level_override = PA_LOG_LEVEL_MAX-1;
209 }
210
211 if (getenv(ENV_LOG_COLORS))
212 flags_override |= PA_LOG_COLORS;
213
214 if (getenv(ENV_LOG_PRINT_TIME))
215 flags_override |= PA_LOG_PRINT_TIME;
216
217 if (getenv(ENV_LOG_PRINT_FILE))
218 flags_override |= PA_LOG_PRINT_FILE;
219
220 if (getenv(ENV_LOG_PRINT_META))
221 flags_override |= PA_LOG_PRINT_META;
222
223 if (getenv(ENV_LOG_PRINT_LEVEL))
224 flags_override |= PA_LOG_PRINT_LEVEL;
225
226 if ((e = getenv(ENV_LOG_BACKTRACE))) {
227 show_backtrace_override = (unsigned) atoi(e);
228
229 if (show_backtrace_override <= 0)
230 show_backtrace_override = 0;
231 }
232 }
233
234 void pa_log_levelv_meta(
235 pa_log_level_t level,
236 const char*file,
237 int line,
238 const char *func,
239 const char *format,
240 va_list ap) {
241
242 char *t, *n;
243 int saved_errno = errno;
244 char *bt = NULL;
245 pa_log_target_t _target;
246 pa_log_level_t _maximum_level;
247 unsigned _show_backtrace;
248 pa_log_flags_t _flags;
249
250 /* We don't use dynamic memory allocation here to minimize the hit
251 * in RT threads */
252 char text[4096], location[128], timestamp[32];
253
254 pa_assert(level < PA_LOG_LEVEL_MAX);
255 pa_assert(format);
256
257 PA_ONCE_BEGIN {
258 init_defaults();
259 } PA_ONCE_END;
260
261 _target = target_override_set ? target_override : target;
262 _maximum_level = PA_MAX(maximum_level, maximum_level_override);
263 _show_backtrace = PA_MAX(show_backtrace, show_backtrace_override);
264 _flags = flags | flags_override;
265
266 if (PA_LIKELY(level > _maximum_level)) {
267 errno = saved_errno;
268 return;
269 }
270
271 pa_vsnprintf(text, sizeof(text), format, ap);
272
273 if ((_flags & PA_LOG_PRINT_META) && file && line > 0 && func)
274 pa_snprintf(location, sizeof(location), "[%s:%i %s()] ", file, line, func);
275 else if (_flags & (PA_LOG_PRINT_META|PA_LOG_PRINT_FILE))
276 pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file));
277 else
278 location[0] = 0;
279
280 if (_flags & PA_LOG_PRINT_TIME) {
281 static pa_usec_t start, last;
282 pa_usec_t u, a, r;
283
284 u = pa_rtclock_usec();
285
286 PA_ONCE_BEGIN {
287 start = u;
288 last = u;
289 } PA_ONCE_END;
290
291 r = u - last;
292 a = u - start;
293
294 /* This is not thread safe, but this is a debugging tool only
295 * anyway. */
296 last = u;
297
298 pa_snprintf(timestamp, sizeof(timestamp), "(%4llu.%03llu|%4llu.%03llu) ",
299 (unsigned long long) (a / PA_USEC_PER_SEC),
300 (unsigned long long) (((a / PA_USEC_PER_MSEC)) % 1000),
301 (unsigned long long) (r / PA_USEC_PER_SEC),
302 (unsigned long long) (((r / PA_USEC_PER_MSEC)) % 1000));
303
304 } else
305 timestamp[0] = 0;
306
307 #ifdef HAVE_EXECINFO_H
308 if (_show_backtrace > 0)
309 bt = get_backtrace(_show_backtrace);
310 #endif
311
312 if (!pa_utf8_valid(text))
313 pa_log_level(level, __FILE__": invalid UTF-8 string following below:");
314
315 for (t = text; t; t = n) {
316 if ((n = strchr(t, '\n'))) {
317 *n = 0;
318 n++;
319 }
320
321 /* We ignore strings only made out of whitespace */
322 if (t[strspn(t, "\t ")] == 0)
323 continue;
324
325 switch (_target) {
326
327 case PA_LOG_STDERR: {
328 const char *prefix = "", *suffix = "", *grey = "";
329 char *local_t;
330
331 #ifndef OS_IS_WIN32
332 /* Yes indeed. Useless, but fun! */
333 if ((_flags & PA_LOG_COLORS) && isatty(STDERR_FILENO)) {
334 if (level <= PA_LOG_ERROR)
335 prefix = "\x1B[1;31m";
336 else if (level <= PA_LOG_WARN)
337 prefix = "\x1B[1m";
338
339 if (bt)
340 grey = "\x1B[2m";
341
342 if (grey[0] || prefix[0])
343 suffix = "\x1B[0m";
344 }
345 #endif
346
347 /* We shouldn't be using dynamic allocation here to
348 * minimize the hit in RT threads */
349 if ((local_t = pa_utf8_to_locale(t)))
350 t = local_t;
351
352 if (_flags & PA_LOG_PRINT_LEVEL)
353 fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, grey, pa_strempty(bt), suffix);
354 else
355 fprintf(stderr, "%s%s%s%s%s%s%s\n", timestamp, location, prefix, t, grey, pa_strempty(bt), suffix);
356
357 pa_xfree(local_t);
358
359 break;
360 }
361
362 #ifdef HAVE_SYSLOG_H
363 case PA_LOG_SYSLOG: {
364 char *local_t;
365
366 openlog(ident, LOG_PID, LOG_USER);
367
368 if ((local_t = pa_utf8_to_locale(t)))
369 t = local_t;
370
371 syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
372 pa_xfree(local_t);
373
374 break;
375 }
376 #endif
377
378 case PA_LOG_NULL:
379 default:
380 break;
381 }
382 }
383
384 pa_xfree(bt);
385 errno = saved_errno;
386 }
387
388 void pa_log_level_meta(
389 pa_log_level_t level,
390 const char*file,
391 int line,
392 const char *func,
393 const char *format, ...) {
394
395 va_list ap;
396 va_start(ap, format);
397 pa_log_levelv_meta(level, file, line, func, format, ap);
398 va_end(ap);
399 }
400
401 void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
402 pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
403 }
404
405 void pa_log_level(pa_log_level_t level, const char *format, ...) {
406 va_list ap;
407
408 va_start(ap, format);
409 pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
410 va_end(ap);
411 }
412
413 pa_bool_t pa_log_ratelimit(void) {
414 /* Not more than 10 messages every 5s */
415 static PA_DEFINE_RATELIMIT(ratelimit, 5 * PA_USEC_PER_SEC, 10);
416
417 return pa_ratelimit_test(&ratelimit);
418 }