Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 :
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "primpl.h"
8 : #include "prenv.h"
9 : #include "prprf.h"
10 : #include <string.h>
11 : #ifdef ANDROID
12 : #include <android/log.h>
13 : #endif
14 :
15 : /*
16 : * Lock used to lock the log.
17 : *
18 : * We can't define _PR_LOCK_LOG simply as PR_Lock because PR_Lock may
19 : * contain assertions. We have to avoid assertions in _PR_LOCK_LOG
20 : * because PR_ASSERT calls PR_LogPrint, which in turn calls _PR_LOCK_LOG.
21 : * This can lead to infinite recursion.
22 : */
23 : static PRLock *_pr_logLock;
24 : #if defined(_PR_PTHREADS) || defined(_PR_BTHREADS)
25 : #define _PR_LOCK_LOG() PR_Lock(_pr_logLock);
26 : #define _PR_UNLOCK_LOG() PR_Unlock(_pr_logLock);
27 : #elif defined(_PR_GLOBAL_THREADS_ONLY)
28 : #define _PR_LOCK_LOG() { _PR_LOCK_LOCK(_pr_logLock)
29 : #define _PR_UNLOCK_LOG() _PR_LOCK_UNLOCK(_pr_logLock); }
30 : #else
31 :
32 : #define _PR_LOCK_LOG() \
33 : { \
34 : PRIntn _is; \
35 : PRThread *_me = _PR_MD_CURRENT_THREAD(); \
36 : if (!_PR_IS_NATIVE_THREAD(_me)) \
37 : _PR_INTSOFF(_is); \
38 : _PR_LOCK_LOCK(_pr_logLock)
39 :
40 : #define _PR_UNLOCK_LOG() \
41 : _PR_LOCK_UNLOCK(_pr_logLock); \
42 : PR_ASSERT(_me == _PR_MD_CURRENT_THREAD()); \
43 : if (!_PR_IS_NATIVE_THREAD(_me)) \
44 : _PR_INTSON(_is); \
45 : }
46 :
47 : #endif
48 :
49 : #if defined(XP_PC)
50 : #define strcasecmp stricmp
51 : #endif
52 :
53 : /*
54 : * On NT, we can't define _PUT_LOG as PR_Write or _PR_MD_WRITE,
55 : * because every asynchronous file io operation leads to a fiber context
56 : * switch. So we define _PUT_LOG as fputs (from stdio.h). A side
57 : * benefit is that fputs handles the LF->CRLF translation. This
58 : * code can also be used on other platforms with file stream io.
59 : */
60 : #if defined(WIN32) || defined(XP_OS2)
61 : #define _PR_USE_STDIO_FOR_LOGGING
62 : #endif
63 :
64 : /*
65 : ** Coerce Win32 log output to use OutputDebugString() when
66 : ** NSPR_LOG_FILE is set to "WinDebug".
67 : */
68 : #if defined(XP_PC)
69 : #define WIN32_DEBUG_FILE (FILE*)-2
70 : #endif
71 :
72 : #ifdef WINCE
73 : static void OutputDebugStringA(const char* msg) {
74 : int len = MultiByteToWideChar(CP_ACP, 0, msg, -1, 0, 0);
75 : WCHAR *wMsg = (WCHAR *)PR_Malloc(len * sizeof(WCHAR));
76 : MultiByteToWideChar(CP_ACP, 0, msg, -1, wMsg, len);
77 : OutputDebugStringW(wMsg);
78 : PR_Free(wMsg);
79 : }
80 : #endif
81 :
82 : /* Macros used to reduce #ifdef pollution */
83 :
84 : #if defined(_PR_USE_STDIO_FOR_LOGGING) && defined(XP_PC)
85 : #define _PUT_LOG(fd, buf, nb) \
86 : PR_BEGIN_MACRO \
87 : if (logFile == WIN32_DEBUG_FILE) { \
88 : char savebyte = buf[nb]; \
89 : buf[nb] = '\0'; \
90 : OutputDebugStringA(buf); \
91 : buf[nb] = savebyte; \
92 : } else { \
93 : fwrite(buf, 1, nb, fd); \
94 : fflush(fd); \
95 : } \
96 : PR_END_MACRO
97 : #elif defined(_PR_USE_STDIO_FOR_LOGGING)
98 : #define _PUT_LOG(fd, buf, nb) {fwrite(buf, 1, nb, fd); fflush(fd);}
99 : #elif defined(ANDROID)
100 : #define _PUT_LOG(fd, buf, nb) \
101 : PR_BEGIN_MACRO \
102 : if (fd == _pr_stderr) { \
103 : char savebyte = buf[nb]; \
104 : buf[nb] = '\0'; \
105 : __android_log_write(ANDROID_LOG_INFO, "PRLog", buf); \
106 : buf[nb] = savebyte; \
107 : } else { \
108 : PR_Write(fd, buf, nb); \
109 : } \
110 : PR_END_MACRO
111 : #elif defined(_PR_PTHREADS)
112 : #define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb)
113 : #else
114 : #define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb)
115 : #endif
116 :
117 : /************************************************************************/
118 :
119 : static PRLogModuleInfo *logModules;
120 :
121 : static char *logBuf = NULL;
122 : static char *logp;
123 : static char *logEndp;
124 : #ifdef _PR_USE_STDIO_FOR_LOGGING
125 : static FILE *logFile = NULL;
126 : #else
127 : static PRFileDesc *logFile = 0;
128 : #endif
129 : static PRBool outputTimeStamp = PR_FALSE;
130 : static PRBool appendToLog = PR_FALSE;
131 :
132 : #define LINE_BUF_SIZE 512
133 : #define DEFAULT_BUF_SIZE 16384
134 :
135 : #ifdef _PR_NEED_STRCASECMP
136 :
137 : /*
138 : * strcasecmp is defined in /usr/ucblib/libucb.a on some platforms
139 : * such as NCR and Unixware. Linking with both libc and libucb
140 : * may cause some problem, so I just provide our own implementation
141 : * of strcasecmp here.
142 : */
143 :
144 : static const unsigned char uc[] =
145 : {
146 : '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
147 : '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
148 : '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
149 : '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
150 : ' ', '!', '"', '#', '$', '%', '&', '\'',
151 : '(', ')', '*', '+', ',', '-', '.', '/',
152 : '0', '1', '2', '3', '4', '5', '6', '7',
153 : '8', '9', ':', ';', '<', '=', '>', '?',
154 : '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
155 : 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
156 : 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
157 : 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
158 : '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
159 : 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
160 : 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
161 : 'X', 'Y', 'Z', '{', '|', '}', '~', '\177'
162 : };
163 :
164 : PRIntn strcasecmp(const char *a, const char *b)
165 : {
166 : const unsigned char *ua = (const unsigned char *)a;
167 : const unsigned char *ub = (const unsigned char *)b;
168 :
169 : if( ((const char *)0 == a) || (const char *)0 == b )
170 : return (PRIntn)(a-b);
171 :
172 : while( (uc[*ua] == uc[*ub]) && ('\0' != *a) )
173 : {
174 : a++;
175 : ua++;
176 : ub++;
177 : }
178 :
179 : return (PRIntn)(uc[*ua] - uc[*ub]);
180 : }
181 :
182 : #endif /* _PR_NEED_STRCASECMP */
183 :
184 3 : void _PR_InitLog(void)
185 : {
186 : char *ev;
187 :
188 3 : _pr_logLock = PR_NewLock();
189 :
190 3 : ev = PR_GetEnv("NSPR_LOG_MODULES");
191 3 : if (ev && ev[0]) {
192 : char module[64]; /* Security-Critical: If you change this
193 : * size, you must also change the sscanf
194 : * format string to be size-1.
195 : */
196 0 : PRBool isSync = PR_FALSE;
197 0 : PRIntn evlen = strlen(ev), pos = 0;
198 0 : PRInt32 bufSize = DEFAULT_BUF_SIZE;
199 0 : while (pos < evlen) {
200 0 : PRIntn level = 1, count = 0, delta = 0;
201 0 : count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n",
202 : module, &delta, &level, &delta);
203 0 : pos += delta;
204 0 : if (count == 0) break;
205 :
206 : /*
207 : ** If count == 2, then we got module and level. If count
208 : ** == 1, then level defaults to 1 (module enabled).
209 : */
210 0 : if (strcasecmp(module, "sync") == 0) {
211 0 : isSync = PR_TRUE;
212 0 : } else if (strcasecmp(module, "bufsize") == 0) {
213 0 : if (level >= LINE_BUF_SIZE) {
214 0 : bufSize = level;
215 : }
216 0 : } else if (strcasecmp(module, "timestamp") == 0) {
217 0 : outputTimeStamp = PR_TRUE;
218 0 : } else if (strcasecmp(module, "append") == 0) {
219 0 : appendToLog = PR_TRUE;
220 : } else {
221 0 : PRLogModuleInfo *lm = logModules;
222 0 : PRBool skip_modcheck =
223 0 : (0 == strcasecmp (module, "all")) ? PR_TRUE : PR_FALSE;
224 :
225 0 : while (lm != NULL) {
226 0 : if (skip_modcheck) lm -> level = (PRLogModuleLevel)level;
227 0 : else if (strcasecmp(module, lm->name) == 0) {
228 0 : lm->level = (PRLogModuleLevel)level;
229 0 : break;
230 : }
231 0 : lm = lm->next;
232 : }
233 : }
234 : /*found:*/
235 0 : count = sscanf(&ev[pos], " , %n", &delta);
236 0 : pos += delta;
237 0 : if (count == EOF) break;
238 : }
239 0 : PR_SetLogBuffering(isSync ? 0 : bufSize);
240 :
241 0 : ev = PR_GetEnvSecure("NSPR_LOG_FILE");
242 0 : if (ev && ev[0]) {
243 0 : if (!PR_SetLogFile(ev)) {
244 : #ifdef XP_PC
245 : char* str = PR_smprintf("Unable to create nspr log file '%s'\n", ev);
246 : if (str) {
247 : OutputDebugStringA(str);
248 : PR_smprintf_free(str);
249 : }
250 : #else
251 0 : fprintf(stderr, "Unable to create nspr log file '%s'\n", ev);
252 : #endif
253 : }
254 : } else {
255 : #ifdef _PR_USE_STDIO_FOR_LOGGING
256 : logFile = stderr;
257 : #else
258 0 : logFile = _pr_stderr;
259 : #endif
260 : }
261 : }
262 3 : }
263 :
264 0 : void _PR_LogCleanup(void)
265 : {
266 0 : PRLogModuleInfo *lm = logModules;
267 :
268 0 : PR_LogFlush();
269 :
270 : #ifdef _PR_USE_STDIO_FOR_LOGGING
271 : if (logFile
272 : && logFile != stdout
273 : && logFile != stderr
274 : #ifdef XP_PC
275 : && logFile != WIN32_DEBUG_FILE
276 : #endif
277 : ) {
278 : fclose(logFile);
279 : }
280 : #else
281 0 : if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
282 0 : PR_Close(logFile);
283 : }
284 : #endif
285 0 : logFile = NULL;
286 :
287 0 : if (logBuf)
288 0 : PR_DELETE(logBuf);
289 :
290 0 : while (lm != NULL) {
291 0 : PRLogModuleInfo *next = lm->next;
292 0 : free((/*const*/ char *)lm->name);
293 0 : PR_Free(lm);
294 0 : lm = next;
295 : }
296 0 : logModules = NULL;
297 :
298 0 : if (_pr_logLock) {
299 0 : PR_DestroyLock(_pr_logLock);
300 0 : _pr_logLock = NULL;
301 : }
302 0 : }
303 :
304 33 : static void _PR_SetLogModuleLevel( PRLogModuleInfo *lm )
305 : {
306 : char *ev;
307 :
308 33 : ev = PR_GetEnv("NSPR_LOG_MODULES");
309 33 : if (ev && ev[0]) {
310 : char module[64]; /* Security-Critical: If you change this
311 : * size, you must also change the sscanf
312 : * format string to be size-1.
313 : */
314 0 : PRIntn evlen = strlen(ev), pos = 0;
315 0 : while (pos < evlen) {
316 0 : PRIntn level = 1, count = 0, delta = 0;
317 :
318 0 : count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n",
319 : module, &delta, &level, &delta);
320 0 : pos += delta;
321 0 : if (count == 0) break;
322 :
323 : /*
324 : ** If count == 2, then we got module and level. If count
325 : ** == 1, then level defaults to 1 (module enabled).
326 : */
327 0 : if (lm != NULL)
328 : {
329 0 : if ((strcasecmp(module, "all") == 0)
330 0 : || (strcasecmp(module, lm->name) == 0))
331 : {
332 0 : lm->level = (PRLogModuleLevel)level;
333 : }
334 : }
335 0 : count = sscanf(&ev[pos], " , %n", &delta);
336 0 : pos += delta;
337 0 : if (count == EOF) break;
338 : }
339 : }
340 33 : } /* end _PR_SetLogModuleLevel() */
341 :
342 : PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char *name)
343 : {
344 : PRLogModuleInfo *lm;
345 :
346 33 : if (!_pr_initialized) _PR_ImplicitInitialization();
347 :
348 33 : lm = PR_NEWZAP(PRLogModuleInfo);
349 33 : if (lm) {
350 33 : lm->name = strdup(name);
351 33 : lm->level = PR_LOG_NONE;
352 33 : lm->next = logModules;
353 33 : logModules = lm;
354 33 : _PR_SetLogModuleLevel(lm);
355 : }
356 33 : return lm;
357 : }
358 :
359 : PR_IMPLEMENT(PRBool) PR_SetLogFile(const char *file)
360 : {
361 : #ifdef _PR_USE_STDIO_FOR_LOGGING
362 : FILE *newLogFile;
363 :
364 : #ifdef XP_PC
365 : if ( strcmp( file, "WinDebug") == 0)
366 : {
367 : newLogFile = WIN32_DEBUG_FILE;
368 : }
369 : else
370 : #endif
371 : {
372 : const char *mode = appendToLog ? "a" : "w";
373 : newLogFile = fopen(file, mode);
374 : if (!newLogFile)
375 : return PR_FALSE;
376 :
377 : #ifndef WINCE /* _IONBF does not exist in the Windows Mobile 6 SDK. */
378 : /* We do buffering ourselves. */
379 : setvbuf(newLogFile, NULL, _IONBF, 0);
380 : #endif
381 : }
382 : if (logFile
383 : && logFile != stdout
384 : && logFile != stderr
385 : #ifdef XP_PC
386 : && logFile != WIN32_DEBUG_FILE
387 : #endif
388 : ) {
389 : fclose(logFile);
390 : }
391 : logFile = newLogFile;
392 : return PR_TRUE;
393 : #else
394 : PRFileDesc *newLogFile;
395 0 : PRIntn flags = PR_WRONLY|PR_CREATE_FILE;
396 0 : if (appendToLog) {
397 0 : flags |= PR_APPEND;
398 : } else {
399 0 : flags |= PR_TRUNCATE;
400 : }
401 :
402 0 : newLogFile = PR_Open(file, flags, 0666);
403 0 : if (newLogFile) {
404 0 : if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
405 0 : PR_Close(logFile);
406 : }
407 0 : logFile = newLogFile;
408 : }
409 0 : return (PRBool) (newLogFile != 0);
410 : #endif /* _PR_USE_STDIO_FOR_LOGGING */
411 : }
412 :
413 : PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size)
414 : {
415 0 : PR_LogFlush();
416 :
417 0 : if (logBuf)
418 0 : PR_DELETE(logBuf);
419 :
420 0 : if (buffer_size >= LINE_BUF_SIZE) {
421 0 : logp = logBuf = (char*) PR_MALLOC(buffer_size);
422 0 : logEndp = logp + buffer_size;
423 : }
424 0 : }
425 :
426 : PR_IMPLEMENT(void) PR_LogPrint(const char *fmt, ...)
427 : {
428 : va_list ap;
429 : char line[LINE_BUF_SIZE];
430 0 : char *line_long = NULL;
431 0 : PRUint32 nb_tid = 0, nb;
432 : PRThread *me;
433 : PRExplodedTime now;
434 :
435 0 : if (!_pr_initialized) _PR_ImplicitInitialization();
436 :
437 0 : if (!logFile) {
438 0 : return;
439 : }
440 :
441 0 : if (outputTimeStamp) {
442 0 : PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now);
443 0 : nb_tid = PR_snprintf(line, sizeof(line)-1,
444 : "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - ",
445 0 : now.tm_year, now.tm_month + 1, now.tm_mday,
446 : now.tm_hour, now.tm_min, now.tm_sec,
447 : now.tm_usec);
448 : }
449 :
450 0 : me = PR_GetCurrentThread();
451 0 : nb_tid += PR_snprintf(line+nb_tid, sizeof(line)-nb_tid-1, "%ld[%p]: ",
452 : #if defined(_PR_BTHREADS)
453 : me, me);
454 : #else
455 : me ? me->id : 0L, me);
456 : #endif
457 :
458 0 : va_start(ap, fmt);
459 0 : nb = nb_tid + PR_vsnprintf(line+nb_tid, sizeof(line)-nb_tid-1, fmt, ap);
460 0 : va_end(ap);
461 :
462 : /*
463 : * Check if we might have run out of buffer space (in case we have a
464 : * long line), and malloc a buffer just this once.
465 : */
466 0 : if (nb == sizeof(line)-2) {
467 0 : va_start(ap, fmt);
468 0 : line_long = PR_vsmprintf(fmt, ap);
469 0 : va_end(ap);
470 : /* If this failed, we'll fall back to writing the truncated line. */
471 : }
472 :
473 0 : if (line_long) {
474 0 : nb = strlen(line_long);
475 0 : _PR_LOCK_LOG();
476 0 : if (logBuf != 0) {
477 0 : _PUT_LOG(logFile, logBuf, logp - logBuf);
478 0 : logp = logBuf;
479 : }
480 : /*
481 : * Write out the thread id (with an optional timestamp) and the
482 : * malloc'ed buffer.
483 : */
484 0 : _PUT_LOG(logFile, line, nb_tid);
485 0 : _PUT_LOG(logFile, line_long, nb);
486 : /* Ensure there is a trailing newline. */
487 0 : if (!nb || (line_long[nb-1] != '\n')) {
488 : char eol[2];
489 0 : eol[0] = '\n';
490 0 : eol[1] = '\0';
491 0 : _PUT_LOG(logFile, eol, 1);
492 : }
493 0 : _PR_UNLOCK_LOG();
494 0 : PR_smprintf_free(line_long);
495 : } else {
496 : /* Ensure there is a trailing newline. */
497 0 : if (nb && (line[nb-1] != '\n')) {
498 0 : line[nb++] = '\n';
499 0 : line[nb] = '\0';
500 : }
501 0 : _PR_LOCK_LOG();
502 0 : if (logBuf == 0) {
503 0 : _PUT_LOG(logFile, line, nb);
504 : } else {
505 : /* If nb can't fit into logBuf, write out logBuf first. */
506 0 : if (logp + nb > logEndp) {
507 0 : _PUT_LOG(logFile, logBuf, logp - logBuf);
508 0 : logp = logBuf;
509 : }
510 : /* nb is guaranteed to fit into logBuf. */
511 0 : memcpy(logp, line, nb);
512 0 : logp += nb;
513 : }
514 0 : _PR_UNLOCK_LOG();
515 : }
516 0 : PR_LogFlush();
517 : }
518 :
519 : PR_IMPLEMENT(void) PR_LogFlush(void)
520 : {
521 0 : if (logBuf && logFile) {
522 0 : _PR_LOCK_LOG();
523 0 : if (logp > logBuf) {
524 0 : _PUT_LOG(logFile, logBuf, logp - logBuf);
525 0 : logp = logBuf;
526 : }
527 0 : _PR_UNLOCK_LOG();
528 : }
529 0 : }
530 :
531 : PR_IMPLEMENT(void) PR_Abort(void)
532 : {
533 0 : PR_LogPrint("Aborting");
534 : #ifdef ANDROID
535 : __android_log_write(ANDROID_LOG_ERROR, "PRLog", "Aborting");
536 : #endif
537 0 : abort();
538 : }
539 :
540 : PR_IMPLEMENT(void) PR_Assert(const char *s, const char *file, PRIntn ln)
541 : {
542 0 : PR_LogPrint("Assertion failure: %s, at %s:%d\n", s, file, ln);
543 0 : fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln);
544 0 : fflush(stderr);
545 : #ifdef WIN32
546 : DebugBreak();
547 : #elif defined(XP_OS2)
548 : asm("int $3");
549 : #elif defined(ANDROID)
550 : __android_log_assert(NULL, "PRLog", "Assertion failure: %s, at %s:%d\n",
551 : s, file, ln);
552 : #endif
553 0 : abort();
554 : }
|