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() \
30 _PR_LOCK_LOCK(_pr_logLock)
31 # define _PR_UNLOCK_LOG() \
32 _PR_LOCK_UNLOCK(_pr_logLock); \
36 # define _PR_LOCK_LOG() \
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); \
52 # define strcasecmp stricmp
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.
63 # define _PR_USE_STDIO_FOR_LOGGING
67 ** Coerce Win32 log output to use OutputDebugString() when
68 ** NSPR_LOG_FILE is set to "WinDebug".
71 # define WIN32_DEBUG_FILE (FILE*)-2
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
);
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) \
89 if (logFile == WIN32_DEBUG_FILE) { \
90 char savebyte = buf[nb]; \
92 OutputDebugStringA(buf); \
95 fwrite(buf, 1, nb, fd); \
99 #elif defined(_PR_USE_STDIO_FOR_LOGGING)
100 # define _PUT_LOG(fd, buf, nb) \
102 fwrite(buf, 1, nb, fd); \
105 #elif defined(ANDROID)
106 # define _PUT_LOG(fd, buf, nb) \
108 if (fd == _pr_stderr) { \
109 char savebyte = buf[nb]; \
111 __android_log_write(ANDROID_LOG_INFO, "PRLog", buf); \
112 buf[nb] = savebyte; \
114 PR_Write(fd, buf, nb); \
117 #elif defined(_PR_PTHREADS)
118 # define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb)
120 # define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb)
123 /************************************************************************/
125 static PRLogModuleInfo
* logModules
;
127 static char* logBuf
= NULL
;
129 static char* logEndp
;
130 #ifdef _PR_USE_STDIO_FOR_LOGGING
131 static FILE* logFile
= NULL
;
133 static PRFileDesc
* logFile
= 0;
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', '{', '|', '}',
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
)) {
181 return (PRIntn
)(uc
[*ua
] - uc
[*ub
]);
184 #endif /* _PR_NEED_STRCASECMP */
186 void _PR_InitLog(void) {
189 _pr_logLock
= PR_NewLock();
191 ev
= PR_GetEnv("NSPR_LOG_MODULES");
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;
204 "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"
206 module
, &delta
, &level
, &delta
);
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) {
218 } else if (strcasecmp(module
, "bufsize") == 0) {
219 if (level
>= LINE_BUF_SIZE
) {
222 } else if (strcasecmp(module
, "timestamp") == 0) {
223 outputTimeStamp
= PR_TRUE
;
224 } else if (strcasecmp(module
, "append") == 0) {
225 appendToLog
= PR_TRUE
;
227 PRLogModuleInfo
* lm
= logModules
;
228 PRBool skip_modcheck
=
229 (0 == strcasecmp(module
, "all")) ? PR_TRUE
: PR_FALSE
;
233 lm
->level
= (PRLogModuleLevel
)level
;
234 } else if (strcasecmp(module
, lm
->name
) == 0) {
235 lm
->level
= (PRLogModuleLevel
)level
;
242 count
= sscanf(&ev
[pos
], " , %n", &delta
);
248 PR_SetLogBuffering(isSync
? 0 : bufSize
);
250 ev
= PR_GetEnvSecure("NSPR_LOG_FILE");
252 if (!PR_SetLogFile(ev
)) {
254 char* str
= PR_smprintf("Unable to create nspr log file '%s'\n", ev
);
256 OutputDebugStringA(str
);
257 PR_smprintf_free(str
);
260 fprintf(stderr
, "Unable to create nspr log file '%s'\n", ev
);
264 #ifdef _PR_USE_STDIO_FOR_LOGGING
267 logFile
= _pr_stderr
;
273 void _PR_LogCleanup(void) {
274 PRLogModuleInfo
* lm
= logModules
;
278 #ifdef _PR_USE_STDIO_FOR_LOGGING
279 if (logFile
&& logFile
!= stdout
&& logFile
!= stderr
281 && logFile
!= WIN32_DEBUG_FILE
287 if (logFile
&& logFile
!= _pr_stdout
&& logFile
!= _pr_stderr
) {
298 PRLogModuleInfo
* next
= lm
->next
;
299 free((/*const*/ char*)lm
->name
);
306 PR_DestroyLock(_pr_logLock
);
311 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;
326 "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"
328 module
, &delta
, &level
, &delta
);
335 ** If count == 2, then we got module and level. If count
336 ** == 1, then level defaults to 1 (module enabled).
339 if ((strcasecmp(module
, "all") == 0) ||
340 (strcasecmp(module
, lm
->name
) == 0)) {
341 lm
->level
= (PRLogModuleLevel
)level
;
344 count
= sscanf(&ev
[pos
], " , %n", &delta
);
351 } /* end _PR_SetLogModuleLevel() */
353 PR_IMPLEMENT(PRLogModuleInfo
*) PR_NewLogModule(const char* name
) {
356 if (!_pr_initialized
) {
357 _PR_ImplicitInitialization();
360 lm
= PR_NEWZAP(PRLogModuleInfo
);
362 lm
->name
= strdup(name
);
363 lm
->level
= PR_LOG_NONE
;
364 lm
->next
= logModules
;
366 _PR_SetLogModuleLevel(lm
);
371 PR_IMPLEMENT(PRBool
) PR_SetLogFile(const char* file
) {
372 #ifdef _PR_USE_STDIO_FOR_LOGGING
376 if (strcmp(file
, "WinDebug") == 0) {
377 newLogFile
= WIN32_DEBUG_FILE
;
381 const char* mode
= appendToLog
? "a" : "w";
382 newLogFile
= fopen(file
, mode
);
387 # ifndef WINCE /* _IONBF does not exist in the Windows Mobile 6 SDK. */
388 /* We do buffering ourselves. */
389 setvbuf(newLogFile
, NULL
, _IONBF
, 0);
392 if (logFile
&& logFile
!= stdout
&& logFile
!= stderr
394 && logFile
!= WIN32_DEBUG_FILE
399 logFile
= newLogFile
;
402 PRFileDesc
* newLogFile
;
403 PRIntn flags
= PR_WRONLY
| PR_CREATE_FILE
;
407 flags
|= PR_TRUNCATE
;
410 newLogFile
= PR_Open(file
, flags
, 0666);
412 if (logFile
&& logFile
!= _pr_stdout
&& logFile
!= _pr_stderr
) {
415 logFile
= newLogFile
;
417 return (PRBool
)(newLogFile
!= 0);
418 #endif /* _PR_USE_STDIO_FOR_LOGGING */
421 PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size
) {
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
, ...) {
436 char line
[LINE_BUF_SIZE
];
437 char* line_long
= NULL
;
438 PRUint32 nb_tid
= 0, nb
;
442 if (!_pr_initialized
) {
443 _PR_ImplicitInitialization();
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)
463 me
? me
->id
: 0L, me
);
467 nb
= nb_tid
+ PR_vsnprintf(line
+ nb_tid
, sizeof(line
) - nb_tid
- 1, fmt
, 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) {
476 line_long
= PR_vsmprintf(fmt
, ap
);
478 /* If this failed, we'll fall back to writing the truncated line. */
482 nb
= strlen(line_long
);
485 _PUT_LOG(logFile
, logBuf
, logp
- logBuf
);
489 * Write out the thread id (with an optional timestamp) and the
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')) {
499 _PUT_LOG(logFile
, eol
, 1);
502 PR_smprintf_free(line_long
);
504 /* Ensure there is a trailing newline. */
505 if (nb
&& (line
[nb
- 1] != '\n')) {
511 _PUT_LOG(logFile
, line
, nb
);
513 /* If nb can't fit into logBuf, write out logBuf first. */
514 if (logp
+ nb
> logEndp
) {
515 _PUT_LOG(logFile
, logBuf
, logp
- logBuf
);
518 /* nb is guaranteed to fit into logBuf. */
519 memcpy(logp
, line
, nb
);
527 PR_IMPLEMENT(void) PR_LogFlush(void) {
528 if (logBuf
&& logFile
) {
531 _PUT_LOG(logFile
, logBuf
, logp
- logBuf
);
538 PR_IMPLEMENT(void) PR_Abort(void) {
539 PR_LogPrint("Aborting");
541 __android_log_write(ANDROID_LOG_ERROR
, "PRLog", "Aborting");
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
);
552 #elif defined(ANDROID)
553 __android_log_assert(NULL
, "PRLog", "Assertion failure: %s, at %s:%d\n", s
,