Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
#include "primpl.h"
#include "prenv.h"
#include "prprf.h"
#include <string.h>
#ifdef ANDROID
# include <android/log.h>
#endif
/*
* Lock used to lock the log.
*
* We can't define _PR_LOCK_LOG simply as PR_Lock because PR_Lock may
* contain assertions. We have to avoid assertions in _PR_LOCK_LOG
* because PR_ASSERT calls PR_LogPrint, which in turn calls _PR_LOCK_LOG.
* This can lead to infinite recursion.
*/
static PRLock* _pr_logLock;
#if defined(_PR_PTHREADS) || defined(_PR_BTHREADS)
# define _PR_LOCK_LOG() PR_Lock(_pr_logLock);
# define _PR_UNLOCK_LOG() PR_Unlock(_pr_logLock);
#elif defined(_PR_GLOBAL_THREADS_ONLY)
# define _PR_LOCK_LOG() \
{ \
_PR_LOCK_LOCK(_pr_logLock)
# define _PR_UNLOCK_LOG() \
_PR_LOCK_UNLOCK(_pr_logLock); \
}
#else
# define _PR_LOCK_LOG() \
{ \
PRIntn _is; \
PRThread* _me = _PR_MD_CURRENT_THREAD(); \
if (!_PR_IS_NATIVE_THREAD(_me)) _PR_INTSOFF(_is); \
_PR_LOCK_LOCK(_pr_logLock)
# define _PR_UNLOCK_LOG() \
_PR_LOCK_UNLOCK(_pr_logLock); \
PR_ASSERT(_me == _PR_MD_CURRENT_THREAD()); \
if (!_PR_IS_NATIVE_THREAD(_me)) _PR_INTSON(_is); \
}
#endif
#if defined(XP_PC)
# define strcasecmp stricmp
#endif
/*
* On NT, we can't define _PUT_LOG as PR_Write or _PR_MD_WRITE,
* because every asynchronous file io operation leads to a fiber context
* switch. So we define _PUT_LOG as fputs (from stdio.h). A side
* benefit is that fputs handles the LF->CRLF translation. This
* code can also be used on other platforms with file stream io.
*/
#if defined(WIN32)
# define _PR_USE_STDIO_FOR_LOGGING
#endif
/*
** Coerce Win32 log output to use OutputDebugString() when
** NSPR_LOG_FILE is set to "WinDebug".
*/
#if defined(XP_PC)
# define WIN32_DEBUG_FILE (FILE*)-2
#endif
#ifdef WINCE
static void OutputDebugStringA(const char* msg) {
int len = MultiByteToWideChar(CP_ACP, 0, msg, -1, 0, 0);
WCHAR* wMsg = (WCHAR*)PR_Malloc(len * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, msg, -1, wMsg, len);
OutputDebugStringW(wMsg);
PR_Free(wMsg);
}
#endif
/* Macros used to reduce #ifdef pollution */
#if defined(_PR_USE_STDIO_FOR_LOGGING) && defined(XP_PC)
# define _PUT_LOG(fd, buf, nb) \
PR_BEGIN_MACRO \
if (logFile == WIN32_DEBUG_FILE) { \
char savebyte = buf[nb]; \
buf[nb] = '\0'; \
OutputDebugStringA(buf); \
buf[nb] = savebyte; \
} else { \
fwrite(buf, 1, nb, fd); \
fflush(fd); \
} \
PR_END_MACRO
#elif defined(_PR_USE_STDIO_FOR_LOGGING)
# define _PUT_LOG(fd, buf, nb) \
{ \
fwrite(buf, 1, nb, fd); \
fflush(fd); \
}
#elif defined(ANDROID)
# define _PUT_LOG(fd, buf, nb) \
PR_BEGIN_MACRO \
if (fd == _pr_stderr) { \
char savebyte = buf[nb]; \
buf[nb] = '\0'; \
__android_log_write(ANDROID_LOG_INFO, "PRLog", buf); \
buf[nb] = savebyte; \
} else { \
PR_Write(fd, buf, nb); \
} \
PR_END_MACRO
#elif defined(_PR_PTHREADS)
# define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb)
#else
# define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb)
#endif
/************************************************************************/
static PRLogModuleInfo* logModules;
static char* logBuf = NULL;
static char* logp;
static char* logEndp;
#ifdef _PR_USE_STDIO_FOR_LOGGING
static FILE* logFile = NULL;
#else
static PRFileDesc* logFile = 0;
#endif
static PRBool outputTimeStamp = PR_FALSE;
static PRBool appendToLog = PR_FALSE;
#define LINE_BUF_SIZE 512
#define DEFAULT_BUF_SIZE 16384
#ifdef _PR_NEED_STRCASECMP
/*
* strcasecmp is defined in /usr/ucblib/libucb.a on some platforms
* such as NCR. Linking with both libc and libucb
* may cause some problem, so I just provide our own implementation
* of strcasecmp here.
*/
static const unsigned char uc[] = {
'\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', '\010',
'\011', '\012', '\013', '\014', '\015', '\016', '\017', '\020', '\021',
'\022', '\023', '\024', '\025', '\026', '\027', '\030', '\031', '\032',
'\033', '\034', '\035', '\036', '\037', ' ', '!', '"', '#',
'$', '%', '&', '\'', '(', ')', '*', '+', ',',
'-', '.', '/', '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', ':', ';', '<', '=', '>',
'?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
'Z', '[', '\\', ']', '^', '_', '`', 'A', 'B',
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}',
'~', '\177'};
PRIntn strcasecmp(const char* a, const char* b) {
const unsigned char* ua = (const unsigned char*)a;
const unsigned char* ub = (const unsigned char*)b;
if (((const char*)0 == a) || (const char*)0 == b) {
return (PRIntn)(a - b);
}
while ((uc[*ua] == uc[*ub]) && ('\0' != *a)) {
a++;
ua++;
ub++;
}
return (PRIntn)(uc[*ua] - uc[*ub]);
}
#endif /* _PR_NEED_STRCASECMP */
void _PR_InitLog(void) {
char* ev;
_pr_logLock = PR_NewLock();
ev = PR_GetEnv("NSPR_LOG_MODULES");
if (ev && ev[0]) {
char module[64]; /* Security-Critical: If you change this
* size, you must also change the sscanf
* format string to be size-1.
*/
PRBool isSync = PR_FALSE;
PRIntn evlen = strlen(ev), pos = 0;
PRInt32 bufSize = DEFAULT_BUF_SIZE;
while (pos < evlen) {
PRIntn level = 1, count = 0, delta = 0;
count = sscanf(
&ev[pos],
"%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"
"]%n:%d%n",
module, &delta, &level, &delta);
pos += delta;
if (count == 0) {
break;
}
/*
** If count == 2, then we got module and level. If count
** == 1, then level defaults to 1 (module enabled).
*/
if (strcasecmp(module, "sync") == 0) {
isSync = PR_TRUE;
} else if (strcasecmp(module, "bufsize") == 0) {
if (level >= LINE_BUF_SIZE) {
bufSize = level;
}
} else if (strcasecmp(module, "timestamp") == 0) {
outputTimeStamp = PR_TRUE;
} else if (strcasecmp(module, "append") == 0) {
appendToLog = PR_TRUE;
} else {
PRLogModuleInfo* lm = logModules;
PRBool skip_modcheck =
(0 == strcasecmp(module, "all")) ? PR_TRUE : PR_FALSE;
while (lm != NULL) {
if (skip_modcheck) {
lm->level = (PRLogModuleLevel)level;
} else if (strcasecmp(module, lm->name) == 0) {
lm->level = (PRLogModuleLevel)level;
break;
}
lm = lm->next;
}
}
/*found:*/
count = sscanf(&ev[pos], " , %n", &delta);
pos += delta;
if (count == EOF) {
break;
}
}
PR_SetLogBuffering(isSync ? 0 : bufSize);
ev = PR_GetEnvSecure("NSPR_LOG_FILE");
if (ev && ev[0]) {
if (!PR_SetLogFile(ev)) {
#ifdef XP_PC
char* str = PR_smprintf("Unable to create nspr log file '%s'\n", ev);
if (str) {
OutputDebugStringA(str);
PR_smprintf_free(str);
}
#else
fprintf(stderr, "Unable to create nspr log file '%s'\n", ev);
#endif
}
} else {
#ifdef _PR_USE_STDIO_FOR_LOGGING
logFile = stderr;
#else
logFile = _pr_stderr;
#endif
}
}
}
void _PR_LogCleanup(void) {
PRLogModuleInfo* lm = logModules;
PR_LogFlush();
#ifdef _PR_USE_STDIO_FOR_LOGGING
if (logFile && logFile != stdout && logFile != stderr
# ifdef XP_PC
&& logFile != WIN32_DEBUG_FILE
# endif
) {
fclose(logFile);
}
#else
if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
PR_Close(logFile);
}
#endif
logFile = NULL;
if (logBuf) {
PR_DELETE(logBuf);
}
while (lm != NULL) {
PRLogModuleInfo* next = lm->next;
free((/*const*/ char*)lm->name);
PR_Free(lm);
lm = next;
}
logModules = NULL;
if (_pr_logLock) {
PR_DestroyLock(_pr_logLock);
_pr_logLock = NULL;
}
}
static void _PR_SetLogModuleLevel(PRLogModuleInfo* lm) {
char* ev;
ev = PR_GetEnv("NSPR_LOG_MODULES");
if (ev && ev[0]) {
char module[64]; /* Security-Critical: If you change this
* size, you must also change the sscanf
* format string to be size-1.
*/
PRIntn evlen = strlen(ev), pos = 0;
while (pos < evlen) {
PRIntn level = 1, count = 0, delta = 0;
count = sscanf(
&ev[pos],
"%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"
"]%n:%d%n",
module, &delta, &level, &delta);
pos += delta;
if (count == 0) {
break;
}
/*
** If count == 2, then we got module and level. If count
** == 1, then level defaults to 1 (module enabled).
*/
if (lm != NULL) {
if ((strcasecmp(module, "all") == 0) ||
(strcasecmp(module, lm->name) == 0)) {
lm->level = (PRLogModuleLevel)level;
}
}
count = sscanf(&ev[pos], " , %n", &delta);
pos += delta;
if (count == EOF) {
break;
}
}
}
} /* end _PR_SetLogModuleLevel() */
PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char* name) {
PRLogModuleInfo* lm;
if (!_pr_initialized) {
_PR_ImplicitInitialization();
}
lm = PR_NEWZAP(PRLogModuleInfo);
if (lm) {
lm->name = strdup(name);
lm->level = PR_LOG_NONE;
lm->next = logModules;
logModules = lm;
_PR_SetLogModuleLevel(lm);
}
return lm;
}
PR_IMPLEMENT(PRBool) PR_SetLogFile(const char* file) {
#ifdef _PR_USE_STDIO_FOR_LOGGING
FILE* newLogFile;
# ifdef XP_PC
if (strcmp(file, "WinDebug") == 0) {
newLogFile = WIN32_DEBUG_FILE;
} else
# endif
{
const char* mode = appendToLog ? "a" : "w";
newLogFile = fopen(file, mode);
if (!newLogFile) {
return PR_FALSE;
}
# ifndef WINCE /* _IONBF does not exist in the Windows Mobile 6 SDK. */
/* We do buffering ourselves. */
setvbuf(newLogFile, NULL, _IONBF, 0);
# endif
}
if (logFile && logFile != stdout && logFile != stderr
# ifdef XP_PC
&& logFile != WIN32_DEBUG_FILE
# endif
) {
fclose(logFile);
}
logFile = newLogFile;
return PR_TRUE;
#else
PRFileDesc* newLogFile;
PRIntn flags = PR_WRONLY | PR_CREATE_FILE;
if (appendToLog) {
flags |= PR_APPEND;
} else {
flags |= PR_TRUNCATE;
}
newLogFile = PR_Open(file, flags, 0666);
if (newLogFile) {
if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) {
PR_Close(logFile);
}
logFile = newLogFile;
}
return (PRBool)(newLogFile != 0);
#endif /* _PR_USE_STDIO_FOR_LOGGING */
}
PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size) {
PR_LogFlush();
if (logBuf) {
PR_DELETE(logBuf);
}
if (buffer_size >= LINE_BUF_SIZE) {
logp = logBuf = (char*)PR_MALLOC(buffer_size);
logEndp = logp + buffer_size;
}
}
PR_IMPLEMENT(void) PR_LogPrint(const char* fmt, ...) {
va_list ap;
char line[LINE_BUF_SIZE];
char* line_long = NULL;
PRUint32 nb_tid = 0, nb;
PRThread* me;
PRExplodedTime now;
if (!_pr_initialized) {
_PR_ImplicitInitialization();
}
if (!logFile) {
return;
}
if (outputTimeStamp) {
PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now);
nb_tid = PR_snprintf(line, sizeof(line) - 1,
"%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - ",
now.tm_year, now.tm_month + 1, now.tm_mday,
now.tm_hour, now.tm_min, now.tm_sec, now.tm_usec);
}
me = PR_GetCurrentThread();
nb_tid += PR_snprintf(line + nb_tid, sizeof(line) - nb_tid - 1, "%ld[%p]: ",
#if defined(_PR_BTHREADS)
me, me);
#else
me ? me->id : 0L, me);
#endif
va_start(ap, fmt);
nb = nb_tid + PR_vsnprintf(line + nb_tid, sizeof(line) - nb_tid - 1, fmt, ap);
va_end(ap);
/*
* Check if we might have run out of buffer space (in case we have a
* long line), and malloc a buffer just this once.
*/
if (nb == sizeof(line) - 2) {
va_start(ap, fmt);
line_long = PR_vsmprintf(fmt, ap);
va_end(ap);
/* If this failed, we'll fall back to writing the truncated line. */
}
if (line_long) {
nb = strlen(line_long);
_PR_LOCK_LOG();
if (logBuf != 0) {
_PUT_LOG(logFile, logBuf, logp - logBuf);
logp = logBuf;
}
/*
* Write out the thread id (with an optional timestamp) and the
* malloc'ed buffer.
*/
_PUT_LOG(logFile, line, nb_tid);
_PUT_LOG(logFile, line_long, nb);
/* Ensure there is a trailing newline. */
if (!nb || (line_long[nb - 1] != '\n')) {
char eol[2];
eol[0] = '\n';
eol[1] = '\0';
_PUT_LOG(logFile, eol, 1);
}
_PR_UNLOCK_LOG();
PR_smprintf_free(line_long);
} else {
/* Ensure there is a trailing newline. */
if (nb && (line[nb - 1] != '\n')) {
line[nb++] = '\n';
line[nb] = '\0';
}
_PR_LOCK_LOG();
if (logBuf == 0) {
_PUT_LOG(logFile, line, nb);
} else {
/* If nb can't fit into logBuf, write out logBuf first. */
if (logp + nb > logEndp) {
_PUT_LOG(logFile, logBuf, logp - logBuf);
logp = logBuf;
}
/* nb is guaranteed to fit into logBuf. */
memcpy(logp, line, nb);
logp += nb;
}
_PR_UNLOCK_LOG();
}
PR_LogFlush();
}
PR_IMPLEMENT(void) PR_LogFlush(void) {
if (logBuf && logFile) {
_PR_LOCK_LOG();
if (logp > logBuf) {
_PUT_LOG(logFile, logBuf, logp - logBuf);
logp = logBuf;
}
_PR_UNLOCK_LOG();
}
}
PR_IMPLEMENT(void) PR_Abort(void) {
PR_LogPrint("Aborting");
#ifdef ANDROID
__android_log_write(ANDROID_LOG_ERROR, "PRLog", "Aborting");
#endif
abort();
}
PR_IMPLEMENT(void) PR_Assert(const char* s, const char* file, PRIntn ln) {
PR_LogPrint("Assertion failure: %s, at %s:%d\n", s, file, ln);
fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln);
fflush(stderr);
#ifdef WIN32
DebugBreak();
#elif defined(ANDROID)
__android_log_assert(NULL, "PRLog", "Assertion failure: %s, at %s:%d\n", s,
file, ln);
#endif
abort();
}