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