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