Backed out 2 changesets (bug 1943998) for causing wd failures @ phases.py CLOSED...
[gecko.git] / nsprpub / pr / src / io / prlog.c
blob23e8f9fd46ffac8f849a97d487d4252559a6cd17
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 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/. */
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
16 * Lock used to lock the log.
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.
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() \
29 { \
30 _PR_LOCK_LOCK(_pr_logLock)
31 # define _PR_UNLOCK_LOG() \
32 _PR_LOCK_UNLOCK(_pr_logLock); \
34 #else
36 # define _PR_LOCK_LOG() \
37 { \
38 PRIntn _is; \
39 PRThread* _me = _PR_MD_CURRENT_THREAD(); \
40 if (!_PR_IS_NATIVE_THREAD(_me)) _PR_INTSOFF(_is); \
41 _PR_LOCK_LOCK(_pr_logLock)
43 # define _PR_UNLOCK_LOG() \
44 _PR_LOCK_UNLOCK(_pr_logLock); \
45 PR_ASSERT(_me == _PR_MD_CURRENT_THREAD()); \
46 if (!_PR_IS_NATIVE_THREAD(_me)) _PR_INTSON(_is); \
49 #endif
51 #if defined(XP_PC)
52 # define strcasecmp stricmp
53 #endif
56 * On NT, we can't define _PUT_LOG as PR_Write or _PR_MD_WRITE,
57 * because every asynchronous file io operation leads to a fiber context
58 * switch. So we define _PUT_LOG as fputs (from stdio.h). A side
59 * benefit is that fputs handles the LF->CRLF translation. This
60 * code can also be used on other platforms with file stream io.
62 #if defined(WIN32)
63 # define _PR_USE_STDIO_FOR_LOGGING
64 #endif
67 ** Coerce Win32 log output to use OutputDebugString() when
68 ** NSPR_LOG_FILE is set to "WinDebug".
70 #if defined(XP_PC)
71 # define WIN32_DEBUG_FILE (FILE*)-2
72 #endif
74 #ifdef WINCE
75 static void OutputDebugStringA(const char* msg) {
76 int len = MultiByteToWideChar(CP_ACP, 0, msg, -1, 0, 0);
77 WCHAR* wMsg = (WCHAR*)PR_Malloc(len * sizeof(WCHAR));
78 MultiByteToWideChar(CP_ACP, 0, msg, -1, wMsg, len);
79 OutputDebugStringW(wMsg);
80 PR_Free(wMsg);
82 #endif
84 /* Macros used to reduce #ifdef pollution */
86 #if defined(_PR_USE_STDIO_FOR_LOGGING) && defined(XP_PC)
87 # define _PUT_LOG(fd, buf, nb) \
88 PR_BEGIN_MACRO \
89 if (logFile == WIN32_DEBUG_FILE) { \
90 char savebyte = buf[nb]; \
91 buf[nb] = '\0'; \
92 OutputDebugStringA(buf); \
93 buf[nb] = savebyte; \
94 } else { \
95 fwrite(buf, 1, nb, fd); \
96 fflush(fd); \
97 } \
98 PR_END_MACRO
99 #elif defined(_PR_USE_STDIO_FOR_LOGGING)
100 # define _PUT_LOG(fd, buf, nb) \
102 fwrite(buf, 1, nb, fd); \
103 fflush(fd); \
105 #elif defined(ANDROID)
106 # define _PUT_LOG(fd, buf, nb) \
107 PR_BEGIN_MACRO \
108 if (fd == _pr_stderr) { \
109 char savebyte = buf[nb]; \
110 buf[nb] = '\0'; \
111 __android_log_write(ANDROID_LOG_INFO, "PRLog", buf); \
112 buf[nb] = savebyte; \
113 } else { \
114 PR_Write(fd, buf, nb); \
116 PR_END_MACRO
117 #elif defined(_PR_PTHREADS)
118 # define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb)
119 #else
120 # define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb)
121 #endif
123 /************************************************************************/
125 static PRLogModuleInfo* logModules;
127 static char* logBuf = NULL;
128 static char* logp;
129 static char* logEndp;
130 #ifdef _PR_USE_STDIO_FOR_LOGGING
131 static FILE* logFile = NULL;
132 #else
133 static PRFileDesc* logFile = 0;
134 #endif
135 static PRBool outputTimeStamp = PR_FALSE;
136 static PRBool appendToLog = PR_FALSE;
138 #define LINE_BUF_SIZE 512
139 #define DEFAULT_BUF_SIZE 16384
141 #ifdef _PR_NEED_STRCASECMP
144 * strcasecmp is defined in /usr/ucblib/libucb.a on some platforms
145 * such as NCR. Linking with both libc and libucb
146 * may cause some problem, so I just provide our own implementation
147 * of strcasecmp here.
150 static const unsigned char uc[] = {
151 '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', '\010',
152 '\011', '\012', '\013', '\014', '\015', '\016', '\017', '\020', '\021',
153 '\022', '\023', '\024', '\025', '\026', '\027', '\030', '\031', '\032',
154 '\033', '\034', '\035', '\036', '\037', ' ', '!', '"', '#',
155 '$', '%', '&', '\'', '(', ')', '*', '+', ',',
156 '-', '.', '/', '0', '1', '2', '3', '4', '5',
157 '6', '7', '8', '9', ':', ';', '<', '=', '>',
158 '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
159 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
160 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
161 'Z', '[', '\\', ']', '^', '_', '`', 'A', 'B',
162 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
163 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
164 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}',
165 '~', '\177'};
167 PRIntn strcasecmp(const char* a, const char* b) {
168 const unsigned char* ua = (const unsigned char*)a;
169 const unsigned char* ub = (const unsigned char*)b;
171 if (((const char*)0 == a) || (const char*)0 == b) {
172 return (PRIntn)(a - b);
175 while ((uc[*ua] == uc[*ub]) && ('\0' != *a)) {
176 a++;
177 ua++;
178 ub++;
181 return (PRIntn)(uc[*ua] - uc[*ub]);
184 #endif /* _PR_NEED_STRCASECMP */
186 void _PR_InitLog(void) {
187 char* ev;
189 _pr_logLock = PR_NewLock();
191 ev = PR_GetEnv("NSPR_LOG_MODULES");
192 if (ev && ev[0]) {
193 char module[64]; /* Security-Critical: If you change this
194 * size, you must also change the sscanf
195 * format string to be size-1.
197 PRBool isSync = PR_FALSE;
198 PRIntn evlen = strlen(ev), pos = 0;
199 PRInt32 bufSize = DEFAULT_BUF_SIZE;
200 while (pos < evlen) {
201 PRIntn level = 1, count = 0, delta = 0;
202 count = sscanf(
203 &ev[pos],
204 "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"
205 "]%n:%d%n",
206 module, &delta, &level, &delta);
207 pos += delta;
208 if (count == 0) {
209 break;
213 ** If count == 2, then we got module and level. If count
214 ** == 1, then level defaults to 1 (module enabled).
216 if (strcasecmp(module, "sync") == 0) {
217 isSync = PR_TRUE;
218 } else if (strcasecmp(module, "bufsize") == 0) {
219 if (level >= LINE_BUF_SIZE) {
220 bufSize = level;
222 } else if (strcasecmp(module, "timestamp") == 0) {
223 outputTimeStamp = PR_TRUE;
224 } else if (strcasecmp(module, "append") == 0) {
225 appendToLog = PR_TRUE;
226 } else {
227 PRLogModuleInfo* lm = logModules;
228 PRBool skip_modcheck =
229 (0 == strcasecmp(module, "all")) ? PR_TRUE : PR_FALSE;
231 while (lm != NULL) {
232 if (skip_modcheck) {
233 lm->level = (PRLogModuleLevel)level;
234 } else if (strcasecmp(module, lm->name) == 0) {
235 lm->level = (PRLogModuleLevel)level;
236 break;
238 lm = lm->next;
241 /*found:*/
242 count = sscanf(&ev[pos], " , %n", &delta);
243 pos += delta;
244 if (count == EOF) {
245 break;
248 PR_SetLogBuffering(isSync ? 0 : bufSize);
250 ev = PR_GetEnvSecure("NSPR_LOG_FILE");
251 if (ev && ev[0]) {
252 if (!PR_SetLogFile(ev)) {
253 #ifdef XP_PC
254 char* str = PR_smprintf("Unable to create nspr log file '%s'\n", ev);
255 if (str) {
256 OutputDebugStringA(str);
257 PR_smprintf_free(str);
259 #else
260 fprintf(stderr, "Unable to create nspr log file '%s'\n", ev);
261 #endif
263 } else {
264 #ifdef _PR_USE_STDIO_FOR_LOGGING
265 logFile = stderr;
266 #else
267 logFile = _pr_stderr;
268 #endif
273 void _PR_LogCleanup(void) {
274 PRLogModuleInfo* lm = logModules;
276 PR_LogFlush();
278 #ifdef _PR_USE_STDIO_FOR_LOGGING
279 if (logFile && logFile != stdout && logFile != stderr
280 # ifdef XP_PC
281 && logFile != WIN32_DEBUG_FILE
282 # endif
284 fclose(logFile);
286 #else
287 if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
288 PR_Close(logFile);
290 #endif
291 logFile = NULL;
293 if (logBuf) {
294 PR_DELETE(logBuf);
297 while (lm != NULL) {
298 PRLogModuleInfo* next = lm->next;
299 free((/*const*/ char*)lm->name);
300 PR_Free(lm);
301 lm = next;
303 logModules = NULL;
305 if (_pr_logLock) {
306 PR_DestroyLock(_pr_logLock);
307 _pr_logLock = NULL;
311 static void _PR_SetLogModuleLevel(PRLogModuleInfo* lm) {
312 char* ev;
314 ev = PR_GetEnv("NSPR_LOG_MODULES");
315 if (ev && ev[0]) {
316 char module[64]; /* Security-Critical: If you change this
317 * size, you must also change the sscanf
318 * format string to be size-1.
320 PRIntn evlen = strlen(ev), pos = 0;
321 while (pos < evlen) {
322 PRIntn level = 1, count = 0, delta = 0;
324 count = sscanf(
325 &ev[pos],
326 "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"
327 "]%n:%d%n",
328 module, &delta, &level, &delta);
329 pos += delta;
330 if (count == 0) {
331 break;
335 ** If count == 2, then we got module and level. If count
336 ** == 1, then level defaults to 1 (module enabled).
338 if (lm != NULL) {
339 if ((strcasecmp(module, "all") == 0) ||
340 (strcasecmp(module, lm->name) == 0)) {
341 lm->level = (PRLogModuleLevel)level;
344 count = sscanf(&ev[pos], " , %n", &delta);
345 pos += delta;
346 if (count == EOF) {
347 break;
351 } /* end _PR_SetLogModuleLevel() */
353 PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char* name) {
354 PRLogModuleInfo* lm;
356 if (!_pr_initialized) {
357 _PR_ImplicitInitialization();
360 lm = PR_NEWZAP(PRLogModuleInfo);
361 if (lm) {
362 lm->name = strdup(name);
363 lm->level = PR_LOG_NONE;
364 lm->next = logModules;
365 logModules = lm;
366 _PR_SetLogModuleLevel(lm);
368 return lm;
371 PR_IMPLEMENT(PRBool) PR_SetLogFile(const char* file) {
372 #ifdef _PR_USE_STDIO_FOR_LOGGING
373 FILE* newLogFile;
375 # ifdef XP_PC
376 if (strcmp(file, "WinDebug") == 0) {
377 newLogFile = WIN32_DEBUG_FILE;
378 } else
379 # endif
381 const char* mode = appendToLog ? "a" : "w";
382 newLogFile = fopen(file, mode);
383 if (!newLogFile) {
384 return PR_FALSE;
387 # ifndef WINCE /* _IONBF does not exist in the Windows Mobile 6 SDK. */
388 /* We do buffering ourselves. */
389 setvbuf(newLogFile, NULL, _IONBF, 0);
390 # endif
392 if (logFile && logFile != stdout && logFile != stderr
393 # ifdef XP_PC
394 && logFile != WIN32_DEBUG_FILE
395 # endif
397 fclose(logFile);
399 logFile = newLogFile;
400 return PR_TRUE;
401 #else
402 PRFileDesc* newLogFile;
403 PRIntn flags = PR_WRONLY | PR_CREATE_FILE;
404 if (appendToLog) {
405 flags |= PR_APPEND;
406 } else {
407 flags |= PR_TRUNCATE;
410 newLogFile = PR_Open(file, flags, 0666);
411 if (newLogFile) {
412 if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
413 PR_Close(logFile);
415 logFile = newLogFile;
417 return (PRBool)(newLogFile != 0);
418 #endif /* _PR_USE_STDIO_FOR_LOGGING */
421 PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size) {
422 PR_LogFlush();
424 if (logBuf) {
425 PR_DELETE(logBuf);
428 if (buffer_size >= LINE_BUF_SIZE) {
429 logp = logBuf = (char*)PR_MALLOC(buffer_size);
430 logEndp = logp + buffer_size;
434 PR_IMPLEMENT(void) PR_LogPrint(const char* fmt, ...) {
435 va_list ap;
436 char line[LINE_BUF_SIZE];
437 char* line_long = NULL;
438 PRUint32 nb_tid = 0, nb;
439 PRThread* me;
440 PRExplodedTime now;
442 if (!_pr_initialized) {
443 _PR_ImplicitInitialization();
446 if (!logFile) {
447 return;
450 if (outputTimeStamp) {
451 PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now);
452 nb_tid = PR_snprintf(line, sizeof(line) - 1,
453 "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - ",
454 now.tm_year, now.tm_month + 1, now.tm_mday,
455 now.tm_hour, now.tm_min, now.tm_sec, now.tm_usec);
458 me = PR_GetCurrentThread();
459 nb_tid += PR_snprintf(line + nb_tid, sizeof(line) - nb_tid - 1, "%ld[%p]: ",
460 #if defined(_PR_BTHREADS)
461 me, me);
462 #else
463 me ? me->id : 0L, me);
464 #endif
466 va_start(ap, fmt);
467 nb = nb_tid + PR_vsnprintf(line + nb_tid, sizeof(line) - nb_tid - 1, fmt, ap);
468 va_end(ap);
471 * Check if we might have run out of buffer space (in case we have a
472 * long line), and malloc a buffer just this once.
474 if (nb == sizeof(line) - 2) {
475 va_start(ap, fmt);
476 line_long = PR_vsmprintf(fmt, ap);
477 va_end(ap);
478 /* If this failed, we'll fall back to writing the truncated line. */
481 if (line_long) {
482 nb = strlen(line_long);
483 _PR_LOCK_LOG();
484 if (logBuf != 0) {
485 _PUT_LOG(logFile, logBuf, logp - logBuf);
486 logp = logBuf;
489 * Write out the thread id (with an optional timestamp) and the
490 * malloc'ed buffer.
492 _PUT_LOG(logFile, line, nb_tid);
493 _PUT_LOG(logFile, line_long, nb);
494 /* Ensure there is a trailing newline. */
495 if (!nb || (line_long[nb - 1] != '\n')) {
496 char eol[2];
497 eol[0] = '\n';
498 eol[1] = '\0';
499 _PUT_LOG(logFile, eol, 1);
501 _PR_UNLOCK_LOG();
502 PR_smprintf_free(line_long);
503 } else {
504 /* Ensure there is a trailing newline. */
505 if (nb && (line[nb - 1] != '\n')) {
506 line[nb++] = '\n';
507 line[nb] = '\0';
509 _PR_LOCK_LOG();
510 if (logBuf == 0) {
511 _PUT_LOG(logFile, line, nb);
512 } else {
513 /* If nb can't fit into logBuf, write out logBuf first. */
514 if (logp + nb > logEndp) {
515 _PUT_LOG(logFile, logBuf, logp - logBuf);
516 logp = logBuf;
518 /* nb is guaranteed to fit into logBuf. */
519 memcpy(logp, line, nb);
520 logp += nb;
522 _PR_UNLOCK_LOG();
524 PR_LogFlush();
527 PR_IMPLEMENT(void) PR_LogFlush(void) {
528 if (logBuf && logFile) {
529 _PR_LOCK_LOG();
530 if (logp > logBuf) {
531 _PUT_LOG(logFile, logBuf, logp - logBuf);
532 logp = logBuf;
534 _PR_UNLOCK_LOG();
538 PR_IMPLEMENT(void) PR_Abort(void) {
539 PR_LogPrint("Aborting");
540 #ifdef ANDROID
541 __android_log_write(ANDROID_LOG_ERROR, "PRLog", "Aborting");
542 #endif
543 abort();
546 PR_IMPLEMENT(void) PR_Assert(const char* s, const char* file, PRIntn ln) {
547 PR_LogPrint("Assertion failure: %s, at %s:%d\n", s, file, ln);
548 fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln);
549 fflush(stderr);
550 #ifdef WIN32
551 DebugBreak();
552 #elif defined(ANDROID)
553 __android_log_assert(NULL, "PRLog", "Assertion failure: %s, at %s:%d\n", s,
554 file, ln);
555 #endif
556 abort();