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/. */
12 #include <android/log.h>
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() { _PR_LOCK_LOCK(_pr_logLock)
29 #define _PR_UNLOCK_LOG() _PR_LOCK_UNLOCK(_pr_logLock); }
32 #define _PR_LOCK_LOG() \
35 PRThread *_me = _PR_MD_CURRENT_THREAD(); \
36 if (!_PR_IS_NATIVE_THREAD(_me)) \
38 _PR_LOCK_LOCK(_pr_logLock)
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)) \
50 #define strcasecmp stricmp
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.
60 #if defined(WIN32) || defined(XP_OS2)
61 #define _PR_USE_STDIO_FOR_LOGGING
65 ** Coerce Win32 log output to use OutputDebugString() when
66 ** NSPR_LOG_FILE is set to "WinDebug".
69 #define WIN32_DEBUG_FILE (FILE*)-2
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);
82 /* Macros used to reduce #ifdef pollution */
84 #if defined(_PR_USE_STDIO_FOR_LOGGING) && defined(XP_PC)
85 #define _PUT_LOG(fd, buf, nb) \
87 if (logFile == WIN32_DEBUG_FILE) { \
88 char savebyte = buf[nb]; \
90 OutputDebugStringA(buf); \
93 fwrite(buf, 1, nb, fd); \
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) \
102 if (fd == _pr_stderr) { \
103 char savebyte = buf[nb]; \
105 __android_log_write(ANDROID_LOG_INFO, "PRLog", buf); \
106 buf[nb] = savebyte; \
108 PR_Write(fd, buf, nb); \
111 #elif defined(_PR_PTHREADS)
112 #define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb)
114 #define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb)
117 /************************************************************************/
119 static PRLogModuleInfo *logModules;
121 static char *logBuf = NULL;
123 static char *logEndp;
124 #ifdef _PR_USE_STDIO_FOR_LOGGING
125 static FILE *logFile = NULL;
127 static PRFileDesc *logFile = 0;
129 static PRBool outputTimeStamp = PR_FALSE;
130 static PRBool appendToLog = PR_FALSE;
132 #define LINE_BUF_SIZE 512
133 #define DEFAULT_BUF_SIZE 16384
135 #ifdef _PR_NEED_STRCASECMP
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.
144 static const unsigned char uc[] =
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'
164 PRIntn strcasecmp(const char *a, const char *b)
166 const unsigned char *ua = (const unsigned char *)a;
167 const unsigned char *ub = (const unsigned char *)b;
169 if( ((const char *)0 == a) || (const char *)0 == b )
170 return (PRIntn)(a-b);
172 while( (uc[*ua] == uc[*ub]) && ('\0' != *a) )
179 return (PRIntn)(uc[*ua] - uc[*ub]);
182 #endif /* _PR_NEED_STRCASECMP */
184 void _PR_InitLog(void)
188 _pr_logLock = PR_NewLock();
190 ev = PR_GetEnv("NSPR_LOG_MODULES");
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.
196 PRBool isSync = PR_FALSE;
197 PRIntn evlen = strlen(ev), pos = 0;
198 PRInt32 bufSize = DEFAULT_BUF_SIZE;
199 while (pos < evlen) {
200 PRIntn level = 1, count = 0, delta = 0;
201 count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n",
202 module, &delta, &level, &delta);
204 if (count == 0) break;
207 ** If count == 2, then we got module and level. If count
208 ** == 1, then level defaults to 1 (module enabled).
210 if (strcasecmp(module, "sync") == 0) {
212 } else if (strcasecmp(module, "bufsize") == 0) {
213 if (level >= LINE_BUF_SIZE) {
216 } else if (strcasecmp(module, "timestamp") == 0) {
217 outputTimeStamp = PR_TRUE;
218 } else if (strcasecmp(module, "append") == 0) {
219 appendToLog = PR_TRUE;
221 PRLogModuleInfo *lm = logModules;
222 PRBool skip_modcheck =
223 (0 == strcasecmp (module, "all")) ? PR_TRUE : PR_FALSE;
226 if (skip_modcheck) lm -> level = (PRLogModuleLevel)level;
227 else if (strcasecmp(module, lm->name) == 0) {
228 lm->level = (PRLogModuleLevel)level;
235 count = sscanf(&ev[pos], " , %n", &delta);
237 if (count == EOF) break;
239 PR_SetLogBuffering(isSync ? 0 : bufSize);
242 if ((getuid() != geteuid()) || (getgid() != getegid())) {
247 ev = PR_GetEnv("NSPR_LOG_FILE");
249 if (!PR_SetLogFile(ev)) {
251 char* str = PR_smprintf("Unable to create nspr log file '%s'\n", ev);
253 OutputDebugStringA(str);
254 PR_smprintf_free(str);
257 fprintf(stderr, "Unable to create nspr log file '%s'\n", ev);
261 #ifdef _PR_USE_STDIO_FOR_LOGGING
264 logFile = _pr_stderr;
270 void _PR_LogCleanup(void)
272 PRLogModuleInfo *lm = logModules;
276 #ifdef _PR_USE_STDIO_FOR_LOGGING
281 && logFile != WIN32_DEBUG_FILE
287 if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
297 PRLogModuleInfo *next = lm->next;
298 free((/*const*/ char *)lm->name);
305 PR_DestroyLock(_pr_logLock);
310 static void _PR_SetLogModuleLevel( PRLogModuleInfo *lm )
314 ev = PR_GetEnv("NSPR_LOG_MODULES");
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(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n",
325 module, &delta, &level, &delta);
327 if (count == 0) break;
330 ** If count == 2, then we got module and level. If count
331 ** == 1, then level defaults to 1 (module enabled).
335 if ((strcasecmp(module, "all") == 0)
336 || (strcasecmp(module, lm->name) == 0))
338 lm->level = (PRLogModuleLevel)level;
341 count = sscanf(&ev[pos], " , %n", &delta);
343 if (count == EOF) break;
346 } /* end _PR_SetLogModuleLevel() */
348 PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char *name)
352 if (!_pr_initialized) _PR_ImplicitInitialization();
354 lm = PR_NEWZAP(PRLogModuleInfo);
356 lm->name = strdup(name);
357 lm->level = PR_LOG_NONE;
358 lm->next = logModules;
360 _PR_SetLogModuleLevel(lm);
365 PR_IMPLEMENT(PRBool) PR_SetLogFile(const char *file)
367 #ifdef _PR_USE_STDIO_FOR_LOGGING
371 if ( strcmp( file, "WinDebug") == 0)
373 newLogFile = WIN32_DEBUG_FILE;
378 const char *mode = appendToLog ? "a" : "w";
379 newLogFile = fopen(file, mode);
383 #ifndef WINCE /* _IONBF does not exist in the Windows Mobile 6 SDK. */
384 /* We do buffering ourselves. */
385 setvbuf(newLogFile, NULL, _IONBF, 0);
392 && logFile != WIN32_DEBUG_FILE
397 logFile = newLogFile;
400 PRFileDesc *newLogFile;
401 PRIntn flags = PR_WRONLY|PR_CREATE_FILE;
405 flags |= PR_TRUNCATE;
408 newLogFile = PR_Open(file, flags, 0666);
410 if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
413 logFile = newLogFile;
415 return (PRBool) (newLogFile != 0);
416 #endif /* _PR_USE_STDIO_FOR_LOGGING */
419 PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size)
426 if (buffer_size >= LINE_BUF_SIZE) {
427 logp = logBuf = (char*) PR_MALLOC(buffer_size);
428 logEndp = logp + buffer_size;
432 PR_IMPLEMENT(void) PR_LogPrint(const char *fmt, ...)
435 char line[LINE_BUF_SIZE];
436 char *line_long = NULL;
437 PRUint32 nb_tid = 0, nb;
441 if (!_pr_initialized) _PR_ImplicitInitialization();
447 if (outputTimeStamp) {
448 PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now);
449 nb_tid = PR_snprintf(line, sizeof(line)-1,
450 "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - ",
451 now.tm_year, now.tm_month + 1, now.tm_mday,
452 now.tm_hour, now.tm_min, now.tm_sec,
456 me = PR_GetCurrentThread();
457 nb_tid += PR_snprintf(line+nb_tid, sizeof(line)-nb_tid-1, "%ld[%p]: ",
458 #if defined(_PR_BTHREADS)
461 me ? me->id : 0L, me);
465 nb = nb_tid + PR_vsnprintf(line+nb_tid, sizeof(line)-nb_tid-1, fmt, ap);
469 * Check if we might have run out of buffer space (in case we have a
470 * long line), and malloc a buffer just this once.
472 if (nb == sizeof(line)-2) {
474 line_long = PR_vsmprintf(fmt, ap);
476 /* If this failed, we'll fall back to writing the truncated line. */
480 nb = strlen(line_long);
483 _PUT_LOG(logFile, logBuf, logp - logBuf);
487 * Write out the thread id (with an optional timestamp) and the
490 _PUT_LOG(logFile, line, nb_tid);
491 _PUT_LOG(logFile, line_long, nb);
492 /* Ensure there is a trailing newline. */
493 if (!nb || (line_long[nb-1] != '\n')) {
497 _PUT_LOG(logFile, eol, 1);
500 PR_smprintf_free(line_long);
502 /* Ensure there is a trailing newline. */
503 if (nb && (line[nb-1] != '\n')) {
509 _PUT_LOG(logFile, line, nb);
511 /* If nb can't fit into logBuf, write out logBuf first. */
512 if (logp + nb > logEndp) {
513 _PUT_LOG(logFile, logBuf, logp - logBuf);
516 /* nb is guaranteed to fit into logBuf. */
517 memcpy(logp, line, nb);
525 PR_IMPLEMENT(void) PR_LogFlush(void)
527 if (logBuf && logFile) {
530 _PUT_LOG(logFile, logBuf, logp - logBuf);
537 PR_IMPLEMENT(void) PR_Abort(void)
539 PR_LogPrint("Aborting");
543 PR_IMPLEMENT(void) PR_Assert(const char *s, const char *file, PRIntn ln)
545 PR_LogPrint("Assertion failure: %s, at %s:%d\n", s, file, ln);
546 fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln);