1 : /*
2 : * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : /*
8 : * NaCl Server Runtime logging code.
9 : */
10 : #include "native_client/src/include/nacl_compiler_annotations.h"
11 : #include "native_client/src/include/portability.h"
12 : #include "native_client/src/include/portability_io.h"
13 : #include "native_client/src/include/portability_process.h"
14 : #include "native_client/src/include/portability_string.h"
15 :
16 : #include <stdio.h>
17 : #include <stdlib.h>
18 : #include <stdarg.h>
19 : #include <string.h>
20 : #include <limits.h>
21 : #include <sys/stat.h>
22 : #include <fcntl.h>
23 : #include <errno.h>
24 :
25 : #include "native_client/src/shared/platform/nacl_log.h"
26 : #include "native_client/src/shared/platform/nacl_log_intern.h"
27 :
28 : #define THREAD_SAFE_DETAIL_CHECK 0
29 : /*
30 : * If set, check detail_level without grabbing a mutex. This makes
31 : * logging much cheaper, but implies that the verbosity level should
32 : * only be changed prior to going multithreaded.
33 : */
34 :
35 : #include "native_client/src/shared/gio/gio.h"
36 : #include "native_client/src/shared/platform/nacl_exit.h"
37 : #include "native_client/src/shared/platform/nacl_sync.h"
38 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
39 : #include "native_client/src/shared/platform/nacl_threads.h"
40 : #include "native_client/src/shared/platform/nacl_timestamp.h"
41 :
42 : static int g_initialized = 0;
43 :
44 : /*
45 : * Three implementation strategies for module-specific logging:
46 : *
47 : * If Thread Local Storage is available, NaClLogSetModule sets a TLS
48 : * variable to the current module name and NaClLogDoLogAndUnsetModule
49 : * will use that variable to determine whether to log or not, without
50 : * taking a lock.
51 : *
52 : * If Thread-Specific Data is available (pthread.h), then
53 : * NaClLogModuleInit allocates a pthread_[sg]etspecific key, and
54 : * NaClLogSetModule / NaClLogDoLogAndUnsetModule uses the TSD variable
55 : * in much the same way that a TLS variable would be used.
56 : *
57 : * If neither TLS nor TSD is available, then a global variable is used
58 : * to hold the module name, and a NaClMutex lock is used to prevent
59 : * another thread from changing it until the detail level vs verbosity
60 : * level check has fired.
61 : */
62 : #if NACL_PLATFORM_HAS_TLS
63 : THREAD char const *gTls_ModuleName;
64 : #elif NACL_PLATFORM_HAS_TSD
65 : # include <pthread.h>
66 : pthread_key_t gModuleNameKey;
67 : #else
68 : static char const *nacl_log_module_name = NULL;
69 : #endif
70 :
71 : /*
72 : * All logging output is protected by this mutex, so that the logging
73 : * code may output using multiple calls to the gio functions and not
74 : * have logging output from multiple threads get intermixed.
75 : */
76 : static struct NaClMutex log_mu;
77 : static int tag_output = 0;
78 : static int g_abort_count = 0;
79 : /*
80 : * g_abort_count is incremented prior to calling
81 : * gNaClLogAbortBehavior, so that the abort hook can invoke other
82 : * functions that might need to log, without those log entries
83 : * deadlocking or polluting the crash log output.
84 : */
85 : static int g_abort_behavior_active = 0;
86 :
87 : #define NACL_VERBOSITY_UNSET INT_MAX
88 :
89 : static int verbosity = NACL_VERBOSITY_UNSET;
90 : static struct Gio *log_stream = NULL;
91 : static struct GioFile log_file_stream;
92 : static int timestamp_enabled = 1;
93 :
94 : /* global, but explicitly not exposed in non-test header file */
95 : void (*gNaClLogAbortBehavior)(void) = NaClAbort;
96 :
97 : /*
98 : * For now, we use a simple linked list. New entries are pushed to
99 : * the front; search starts at front. So last entry for a particular
100 : * module wins, and we don't bother to eliminate duplicates. The
101 : * expected number of modules is small, so we don't do anything
102 : * fancier. TODO(bsy): measure performance loss and consider
103 : * alternatives.
104 : */
105 :
106 : struct NaClLogModuleVerbosity {
107 : struct NaClLogModuleVerbosity *next;
108 : char const *module_name; /* strdup'd */
109 : int verbosity;
110 : };
111 :
112 : static struct NaClLogModuleVerbosity *gNaClLogModuleVerbosity = NULL;
113 :
114 0 : static FILE *NaClLogFileIoBufferFromFile(char const *log_file) {
115 : int log_desc;
116 : FILE *log_iob;
117 :
118 0 : log_desc = open(log_file, O_WRONLY | O_APPEND | O_CREAT, 0777);
119 0 : if (-1 == log_desc) {
120 0 : perror("NaClLogSetFile");
121 0 : fprintf(stderr, "Could not create log file\n");
122 0 : NaClAbort();
123 : }
124 :
125 0 : log_iob = FDOPEN(log_desc, "a");
126 0 : if (NULL == log_iob) {
127 0 : perror("NaClLogSetFile");
128 0 : fprintf(stderr, "Could not fdopen log stream\n");
129 0 : NaClAbort();
130 : }
131 0 : return log_iob;
132 0 : }
133 :
134 : /*
135 : * Setting the log stream buffering to fully buffered, so that the
136 : * write of the tag string will be less likely to be separated
137 : * from the write of the actual log message.
138 : */
139 25 : static FILE *NaClLogDupFileIo(FILE *orig) {
140 : int d;
141 : FILE *copy;
142 :
143 : /*
144 : * On windows (at least on a win7 machine which i tested on),
145 : * fileno(stderr) is -2. I/O to the stderr stream appears to
146 : * succeed -- though who knows, maybe fclose(stderr) would actually
147 : * report an error? -- but DUP of -2 fails. We don't try to detect
148 : * -2 (or other windows magic values) as a special case here, since
149 : * in the future other FILE* might be used here. Instead, we just
150 : * check for DUP failure and trundle on as best as we could.
151 : */
152 25 : if (-1 == (d = DUP(fileno(orig)))) {
153 0 : copy = orig;
154 : /* this means that setvbuf later will affect the shared stream */
155 25 : } else if (NULL == (copy = FDOPEN(d, "a"))) {
156 0 : copy = orig;
157 : /* ditto */
158 : }
159 25 : (void) setvbuf(copy, (char *) NULL, _IOFBF, 1024);
160 25 : return copy;
161 25 : }
162 :
163 25 : static struct Gio *NaClLogGioFromFileIoBuffer(FILE *log_iob) {
164 : struct GioFile *log_gio;
165 :
166 25 : log_gio = malloc(sizeof *log_gio);
167 25 : if (NULL == log_gio) {
168 0 : perror("NaClLogSetFile");
169 0 : fprintf(stderr, "No memory for log buffers\n");
170 0 : NaClAbort();
171 : }
172 25 : if (!GioFileRefCtor(log_gio, log_iob)) {
173 0 : fprintf(stderr, "NaClLog module internal error: GioFileRefCtor failed\n");
174 0 : NaClAbort();
175 : }
176 25 : return (struct Gio *) log_gio;
177 25 : }
178 :
179 0 : void NaClLogSetFile(char const *log_file) {
180 : NaClLogSetGio(NaClLogGioFromFileIoBuffer(
181 0 : NaClLogFileIoBufferFromFile(log_file)));
182 0 : }
183 :
184 25 : int NaClLogDefaultLogVerbosity(void) {
185 : char *env_verbosity;
186 :
187 25 : if (NULL != (env_verbosity = getenv("NACLVERBOSITY"))) {
188 0 : int v = strtol(env_verbosity, (char **) 0, 0);
189 :
190 0 : if (v >= 0) {
191 0 : return v;
192 : }
193 : }
194 25 : return 0;
195 25 : }
196 :
197 25 : struct Gio *NaClLogDefaultLogGio(void) {
198 : char *log_file;
199 : FILE *log_iob;
200 :
201 25 : log_file = getenv("NACLLOG");
202 :
203 25 : if (NULL == log_file) {
204 25 : log_iob = NaClLogDupFileIo(stderr);
205 25 : } else {
206 0 : log_iob = NaClLogFileIoBufferFromFile(log_file);
207 : }
208 25 : return NaClLogGioFromFileIoBuffer(log_iob);
209 25 : }
210 :
211 27 : void NaClLogParseAndSetModuleVerbosityMap(char const *module_verbosity_map) {
212 : char entry_buf[256];
213 : size_t entry_len;
214 : char const *sep;
215 : char const *next;
216 : char *assign;
217 27 : int seen_global = 0;
218 : char *module_name;
219 : int module_verbosity;
220 :
221 27 : if (NULL == module_verbosity_map) {
222 27 : return;
223 : }
224 :
225 0 : while (*module_verbosity_map != '\0') {
226 0 : sep = strpbrk(module_verbosity_map, ",:");
227 0 : if (NULL == sep) {
228 0 : sep = module_verbosity_map + strlen(module_verbosity_map);
229 0 : next = sep;
230 0 : } else {
231 0 : next = sep + 1;
232 : }
233 : /* post: sep points to comma or termination NUL */
234 0 : entry_len = sep - module_verbosity_map;
235 0 : if (entry_len > sizeof entry_buf - 1) {
236 : NaClLog(LOG_ERROR,
237 : "NaClLog: entry too long in module verbosity map \"%.*s\".\n",
238 : (int) entry_len,
239 0 : module_verbosity_map);
240 0 : entry_len = sizeof entry_buf - 1;
241 : }
242 0 : strncpy(entry_buf, module_verbosity_map, entry_len);
243 0 : entry_buf[entry_len] = '\0';
244 0 : assign = strchr(entry_buf, '=');
245 0 : if (NULL == assign && !seen_global) {
246 0 : verbosity = strtol(entry_buf, (char **) 0, 0);
247 0 : seen_global = 1;
248 0 : } else {
249 0 : *assign = '\0';
250 :
251 0 : module_verbosity = strtol(assign+1, (char **) 0, 0);
252 :
253 0 : while (entry_buf < assign && (' ' == assign[-1] || '\t' == assign[-1])) {
254 0 : *--assign = '\0';
255 0 : }
256 0 : if (entry_buf == assign) {
257 : NaClLog(LOG_FATAL,
258 : "NaClLog: Bad module name in \"%s\".\n",
259 0 : module_verbosity_map);
260 : }
261 :
262 : for (module_name = entry_buf;
263 : ' ' == *module_name || '\t' == *module_name;
264 0 : ++module_name) {
265 : ;
266 0 : }
267 0 : NaClLogSetModuleVerbosity(module_name, module_verbosity);
268 : }
269 0 : module_verbosity_map = next;
270 0 : }
271 27 : }
272 :
273 : void NaClLogModuleInitExtended2(int default_verbosity,
274 : char const *module_verbosity_map,
275 27 : struct Gio *log_gio) {
276 27 : if (!g_initialized) {
277 : #if !THREAD_SAFE_DETAIL_CHECK && !NACL_PLATFORM_HAS_TLS && NACL_PLATFORM_HAS_TSD
278 : int errnum;
279 :
280 : if (0 != (errnum = pthread_key_create(&gModuleNameKey, NULL))) {
281 : fprintf(stderr, "NaClLogModuleInitExtended2: "
282 : "pthread_key_create failed\n");
283 : abort();
284 : }
285 : #endif
286 27 : NaClXMutexCtor(&log_mu);
287 27 : g_initialized = 1;
288 : }
289 27 : NaClLogSetVerbosity(default_verbosity);
290 27 : NaClLogParseAndSetModuleVerbosityMap(module_verbosity_map);
291 27 : NaClLogSetGio(log_gio);
292 27 : }
293 :
294 : void NaClLogModuleInitExtended(int initial_verbosity,
295 27 : struct Gio *log_gio) {
296 :
297 : NaClLogModuleInitExtended2(initial_verbosity,
298 : getenv("NACLVERBOSITY"),
299 27 : log_gio);
300 27 : }
301 :
302 25 : void NaClLogModuleInit(void) {
303 : NaClLogModuleInitExtended(NaClLogDefaultLogVerbosity(),
304 25 : NaClLogDefaultLogGio());
305 25 : }
306 :
307 18 : void NaClLogModuleFini(void) {
308 : struct NaClLogModuleVerbosity *entry;
309 : struct NaClLogModuleVerbosity *next;
310 :
311 18 : entry = gNaClLogModuleVerbosity;
312 18 : while (entry != NULL) {
313 0 : next = entry->next;
314 0 : free(entry);
315 0 : entry = next;
316 0 : }
317 18 : gNaClLogModuleVerbosity = NULL;
318 18 : NaClMutexDtor(&log_mu);
319 18 : g_initialized = 0;
320 18 : }
321 :
322 27 : void NaClLogTagNext_mu(void) {
323 27 : tag_output = 1;
324 27 : }
325 :
326 27 : void NaClLogLock(void) {
327 27 : NaClXMutexLock(&log_mu);
328 27 : NaClLogTagNext_mu();
329 27 : }
330 :
331 27 : void NaClLogUnlock(void) {
332 27 : int run_abort_behavior = 0;
333 27 : switch (g_abort_count) {
334 : case 0:
335 27 : NaClXMutexUnlock(&log_mu);
336 27 : break;
337 : case 1:
338 : /*
339 : * include an easy-to-recognize output for the fuzzer to recognize
340 : */
341 1 : if (!g_abort_behavior_active) {
342 1 : NaClLog_mu(LOG_ERROR, "LOG_FATAL abort exit\n");
343 1 : g_abort_behavior_active = 1;
344 1 : run_abort_behavior = 1;
345 : /*
346 : * run abort behavior only on edge transition when
347 : * g_abort_behavior_active is first set.
348 : */
349 : }
350 1 : NaClXMutexUnlock(&log_mu);
351 1 : if (run_abort_behavior) {
352 : #ifdef __COVERITY__
353 : NaClAbort(); /* help coverity figure out that this is the default */
354 : #else
355 1 : (*gNaClLogAbortBehavior)();
356 : #endif
357 0 : NaClAbort();
358 : }
359 0 : break;
360 : default:
361 : /*
362 : * Abort handling code in turn aborted. Eeep!
363 : */
364 0 : NaClAbort();
365 : break;
366 : }
367 27 : }
368 :
369 9 : static INLINE struct Gio *NaClLogGetGio_mu(void) {
370 9 : if (NULL == log_stream) {
371 0 : (void) GioFileRefCtor(&log_file_stream, NaClLogDupFileIo(stderr));
372 0 : log_stream = (struct Gio *) &log_file_stream;
373 : }
374 9 : return log_stream;
375 9 : }
376 :
377 27 : static void NaClLogSetVerbosity_mu(int verb) {
378 27 : verbosity = verb;
379 27 : }
380 :
381 0 : void NaClLogPreInitSetVerbosity(int verb) {
382 : /*
383 : * The lock used by NaClLogLock has not been initialized and cannot
384 : * be used; however, prior to initialization we are not going to be
385 : * invoked from multiple threads, since the caller is responsible
386 : * for not invoking NaClLog module functions (except for the PreInit
387 : * ones, obviously) at all, let alone from multiple threads. Ergo,
388 : * it is safe to manipulate the module globals without locking.
389 : */
390 0 : NaClLogSetVerbosity_mu(verb);
391 0 : }
392 :
393 27 : void NaClLogSetVerbosity(int verb) {
394 27 : NaClLogLock();
395 27 : NaClLogSetVerbosity_mu(verb);
396 27 : NaClLogUnlock();
397 27 : }
398 :
399 0 : void NaClLogIncrVerbosity(void) {
400 0 : NaClLogLock();
401 0 : if (NACL_VERBOSITY_UNSET == verbosity) {
402 0 : verbosity = 0;
403 : }
404 0 : ++verbosity;
405 0 : NaClLogUnlock();
406 0 : }
407 :
408 0 : int NaClLogGetVerbosity(void) {
409 : int v;
410 :
411 0 : NaClLogLock();
412 0 : if (NACL_VERBOSITY_UNSET == verbosity) {
413 0 : verbosity = 0;
414 : }
415 0 : v = verbosity;
416 0 : NaClLogUnlock();
417 :
418 0 : return v;
419 0 : }
420 :
421 27 : static void NaClLogSetGio_mu(struct Gio *stream) {
422 27 : if (NULL != log_stream) {
423 2 : (void) (*log_stream->vtbl->Flush)(log_stream);
424 : }
425 27 : log_stream = stream;
426 27 : }
427 :
428 0 : void NaClLogPreInitSetGio(struct Gio *out_stream) {
429 : /*
430 : * See thread safety comment in NaClLogPreInitSetVerbosity.
431 : */
432 0 : NaClLogSetGio_mu(out_stream);
433 0 : }
434 :
435 27 : void NaClLogSetGio(struct Gio *stream) {
436 27 : NaClLogLock();
437 27 : NaClLogSetGio_mu(stream);
438 27 : NaClLogUnlock();
439 27 : }
440 :
441 2 : struct Gio *NaClLogGetGio(void) {
442 : struct Gio *s;
443 :
444 2 : NaClLogLock();
445 2 : s = NaClLogGetGio_mu();
446 2 : NaClLogUnlock();
447 :
448 2 : return s;
449 2 : }
450 :
451 0 : void NaClLogEnableTimestamp(void) {
452 0 : timestamp_enabled = 1;
453 0 : }
454 :
455 0 : void NaClLogDisableTimestamp(void) {
456 0 : timestamp_enabled = 0;
457 0 : }
458 :
459 7 : static void NaClLogOutputTag_mu(struct Gio *s) {
460 : char timestamp[128];
461 : int pid;
462 :
463 7 : if (timestamp_enabled && tag_output) {
464 7 : pid = GETPID();
465 : gprintf(s, "[%d,%u:%s] ",
466 : pid,
467 : NaClThreadId(),
468 7 : NaClTimeStampString(timestamp, sizeof timestamp));
469 7 : tag_output = 0;
470 : }
471 7 : }
472 :
473 : /*
474 : * Output a printf-style formatted message if the log verbosity level
475 : * is set higher than the log output's detail level. Note that since
476 : * this is not a macro, log message arguments that have side effects
477 : * will have their side effects regardless of whether the
478 : * corresponding log message is printed or not. This is good from a
479 : * consistency point of view, but it means that should a logging
480 : * argument be expensive to compute, the log statement needs to be
481 : * surrounded by something like
482 : *
483 : * if (detail_level <= NaClLogGetVerbosity()) {
484 : * NaClLog(detail_level, "format string", expensive_arg(), ...);
485 : * }
486 : *
487 : * The log message, if written, is prepended by a microsecond
488 : * resolution timestamp on linux and a millisecond resolution
489 : * timestamp on windows. This means that if the NaCl app can read its
490 : * own logs, it can distinguish which host OS it is running on.
491 : */
492 : void NaClLogDoLogV_mu(int detail_level,
493 : char const *fmt,
494 7 : va_list ap) {
495 : struct Gio *s;
496 :
497 7 : if (0 == g_abort_count) {
498 7 : s = NaClLogGetGio_mu();
499 :
500 7 : NaClLogOutputTag_mu(s);
501 7 : (void) gvprintf(s, fmt, ap);
502 7 : (void) (*s->vtbl->Flush)(s);
503 7 : } else {
504 1 : (void) fprintf(stderr, "POST-ABORT: ");
505 1 : (void) vfprintf(stderr, fmt, ap);
506 1 : (void) fflush(stderr);
507 : }
508 :
509 7 : if (LOG_FATAL == detail_level) {
510 1 : ++g_abort_count;
511 : }
512 7 : }
513 :
514 : void NaClLogV_mu(int detail_level,
515 : char const *fmt,
516 7 : va_list ap) {
517 7 : if (NACL_VERBOSITY_UNSET == verbosity) {
518 0 : verbosity = NaClLogDefaultLogVerbosity();
519 : }
520 :
521 7 : if (detail_level <= verbosity) {
522 7 : NaClLogDoLogV_mu(detail_level, fmt, ap);
523 : }
524 7 : }
525 :
526 : void NaClLogV(int detail_level,
527 : char const *fmt,
528 0 : va_list ap) {
529 : #if !THREAD_SAFE_DETAIL_CHECK
530 0 : if (detail_level > verbosity) {
531 0 : return;
532 : }
533 : #endif
534 0 : NaClLogLock();
535 0 : NaClLogV_mu(detail_level, fmt, ap);
536 0 : NaClLogUnlock();
537 0 : }
538 :
539 : void NaClLogSetModuleVerbosity_mu(char const *module_name,
540 0 : int verbosity) {
541 : struct NaClLogModuleVerbosity *entry;
542 :
543 0 : entry = (struct NaClLogModuleVerbosity *) malloc(sizeof *entry);
544 0 : if (NULL == entry) {
545 : NaClLog_mu(LOG_FATAL,
546 : ("NaClLogSetModuleVerbosity_mu: Out of memory while setting"
547 : " module record for module: %s, verbosity: %d\n"),
548 0 : module_name, verbosity);
549 : }
550 0 : entry->module_name = STRDUP(module_name);
551 0 : if (NULL == entry->module_name) {
552 : NaClLog_mu(LOG_FATAL,
553 : ("NaClLogSetModuleVerbosity_mu: Out of memory while duplicating"
554 : " module name: %s, verbosity: %d\n"),
555 0 : module_name, verbosity);
556 : }
557 0 : entry->verbosity = verbosity;
558 0 : entry->next = gNaClLogModuleVerbosity;
559 0 : gNaClLogModuleVerbosity = entry;
560 0 : }
561 :
562 : void NaClLogSetModuleVerbosity(char const *module_name,
563 0 : int verbosity) {
564 0 : NaClLogLock();
565 0 : NaClLogSetModuleVerbosity_mu(module_name, verbosity);
566 0 : NaClLogUnlock();
567 0 : }
568 :
569 : /*
570 : * After initialization, gNaClLogModuleVerbosity is read-only, so can
571 : * be examined sans locking.
572 : */
573 0 : int NaClLogGetModuleVerbosity_mu(char const *module_name) {
574 : struct NaClLogModuleVerbosity *p;
575 :
576 0 : if (NULL != module_name) {
577 0 : for (p = gNaClLogModuleVerbosity; NULL != p; p = p->next) {
578 0 : if (!strcmp(p->module_name, module_name)) {
579 0 : return p->verbosity;
580 : }
581 0 : }
582 : }
583 0 : return verbosity;
584 0 : }
585 :
586 0 : int NaClLogGetModuleVerbosity(char const *module_name) {
587 : int rv;
588 0 : NaClLogLock();
589 0 : rv = NaClLogGetModuleVerbosity_mu(module_name);
590 0 : NaClLogUnlock();
591 0 : return rv;
592 0 : }
593 :
594 : #if NACL_PLATFORM_HAS_TLS
595 : int NaClLogSetModule(char const *module_name) {
596 : gTls_ModuleName = module_name;
597 : return 0;
598 : }
599 :
600 : static void NaClLogDoLogAndUnsetModuleV(int detail_level,
601 : const char *fmt,
602 : va_list ap) {
603 : int module_verbosity;
604 :
605 : module_verbosity = NaClLogGetModuleVerbosity_mu(gTls_ModuleName);
606 : if (detail_level <= module_verbosity) {
607 : NaClLogLock();
608 : NaClLogDoLogV_mu(detail_level, fmt, ap);
609 : NaClLogUnlock();
610 : }
611 : gTls_ModuleName = (char const *) NULL;
612 : }
613 :
614 : #elif NACL_PLATFORM_HAS_TSD
615 : int NaClLogSetModule(char const *module_name) {
616 : (void) pthread_setspecific(gModuleNameKey, (void const *) module_name);
617 : return 0;
618 : }
619 :
620 : static void NaClLogDoLogAndUnsetModuleV(int detail_level,
621 : const char *fmt,
622 : va_list ap) {
623 : char const *module_name = (char const *) pthread_getspecific(gModuleNameKey);
624 : int module_verbosity;
625 :
626 : module_verbosity = NaClLogGetModuleVerbosity_mu(module_name);
627 : if (detail_level <= module_verbosity) {
628 : NaClLogLock();
629 : NaClLogDoLogV_mu(detail_level, fmt, ap);
630 : NaClLogUnlock();
631 : }
632 : (void) pthread_setspecific(gModuleNameKey, (void const *) NULL);
633 : }
634 :
635 : #else
636 : /* !NACL_PLATFORM_HAS_TLS && !NACL_PLATFORM_HAS_TSD */
637 :
638 0 : int NaClLogSetModule(char const *module_name) {
639 0 : NaClLogLock();
640 0 : nacl_log_module_name = module_name;
641 0 : return 0;
642 0 : }
643 :
644 : static void NaClLogDoLogAndUnsetModuleV(int detail_level,
645 : char const *fmt,
646 0 : va_list ap) {
647 : int module_verbosity;
648 :
649 0 : module_verbosity = NaClLogGetModuleVerbosity_mu(nacl_log_module_name);
650 0 : if (detail_level <= module_verbosity) {
651 0 : NaClLogDoLogV_mu(detail_level, fmt, ap);
652 : }
653 0 : nacl_log_module_name = NULL;
654 0 : NaClLogUnlock();
655 0 : }
656 : #endif
657 :
658 : void NaClLogDoLogAndUnsetModule(int detail_level,
659 : char const *fmt,
660 0 : ...) {
661 : va_list ap;
662 :
663 0 : va_start(ap, fmt);
664 0 : NaClLogDoLogAndUnsetModuleV(detail_level, fmt, ap);
665 0 : va_end(ap);
666 0 : }
667 :
668 : void NaClLog(int detail_level,
669 : char const *fmt,
670 19 : ...) {
671 : va_list ap;
672 :
673 : #if !THREAD_SAFE_DETAIL_CHECK
674 19 : if (NACL_LIKELY(detail_level > verbosity)) {
675 18 : return;
676 : }
677 : #endif
678 :
679 7 : NaClLogLock();
680 7 : va_start(ap, fmt);
681 7 : NaClLogV_mu(detail_level, fmt, ap);
682 7 : va_end(ap);
683 7 : NaClLogUnlock();
684 18 : }
685 :
686 : void NaClLog_mu(int detail_level,
687 : char const *fmt,
688 1 : ...) {
689 : va_list ap;
690 :
691 : #if THREAD_SAFE_DETAIL_CHECK
692 : if (detail_level > verbosity) {
693 : return;
694 : }
695 : #endif
696 :
697 1 : va_start(ap, fmt);
698 1 : NaClLogV_mu(detail_level, fmt, ap);
699 1 : va_end(ap);
700 1 : }
701 :
702 : void NaClLog2(char const *module_name,
703 : int detail_level,
704 : char const *fmt,
705 0 : ...) {
706 : va_list ap;
707 :
708 0 : NaClLogSetModule(module_name);
709 0 : va_start(ap, fmt);
710 0 : NaClLogDoLogAndUnsetModuleV(detail_level, fmt, ap);
711 0 : va_end(ap);
712 0 : }
713 :
714 0 : void NaClLogSetAbortBehavior(void (*fn)(void)) {
715 0 : NaClXMutexLock(&log_mu);
716 0 : gNaClLogAbortBehavior = fn;
717 0 : NaClXMutexUnlock(&log_mu);
718 0 : }
|