prlog.c (15594B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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/. */ 6 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 14 15 /* 16 * Lock used to lock the log. 17 * 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. 22 */ 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); \ 33 } 34 #else 35 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) 42 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); \ 47 } 48 49 #endif 50 51 #if defined(XP_PC) 52 # define strcasecmp stricmp 53 #endif 54 55 /* 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. 61 */ 62 #if defined(WIN32) 63 # define _PR_USE_STDIO_FOR_LOGGING 64 #endif 65 66 /* 67 ** Coerce Win32 log output to use OutputDebugString() when 68 ** NSPR_LOG_FILE is set to "WinDebug". 69 */ 70 #if defined(XP_PC) 71 # define WIN32_DEBUG_FILE (FILE*)-2 72 #endif 73 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); 81 } 82 #endif 83 84 /* Macros used to reduce #ifdef pollution */ 85 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) \ 101 { \ 102 fwrite(buf, 1, nb, fd); \ 103 fflush(fd); \ 104 } 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); \ 115 } \ 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 122 123 /************************************************************************/ 124 125 static PRLogModuleInfo* logModules; 126 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; 137 138 #define LINE_BUF_SIZE 512 139 #define DEFAULT_BUF_SIZE 16384 140 141 #ifdef _PR_NEED_STRCASECMP 142 143 /* 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. 148 */ 149 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'}; 166 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; 170 171 if (((const char*)0 == a) || (const char*)0 == b) { 172 return (PRIntn)(a - b); 173 } 174 175 while ((uc[*ua] == uc[*ub]) && ('\0' != *a)) { 176 a++; 177 ua++; 178 ub++; 179 } 180 181 return (PRIntn)(uc[*ua] - uc[*ub]); 182 } 183 184 #endif /* _PR_NEED_STRCASECMP */ 185 186 void _PR_InitLog(void) { 187 char* ev; 188 189 _pr_logLock = PR_NewLock(); 190 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. 196 */ 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; 210 } 211 212 /* 213 ** If count == 2, then we got module and level. If count 214 ** == 1, then level defaults to 1 (module enabled). 215 */ 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; 221 } 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; 230 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; 237 } 238 lm = lm->next; 239 } 240 } 241 /*found:*/ 242 count = sscanf(&ev[pos], " , %n", &delta); 243 pos += delta; 244 if (count == EOF) { 245 break; 246 } 247 } 248 PR_SetLogBuffering(isSync ? 0 : bufSize); 249 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); 258 } 259 #else 260 fprintf(stderr, "Unable to create nspr log file '%s'\n", ev); 261 #endif 262 } 263 } else { 264 #ifdef _PR_USE_STDIO_FOR_LOGGING 265 logFile = stderr; 266 #else 267 logFile = _pr_stderr; 268 #endif 269 } 270 } 271 } 272 273 void _PR_LogCleanup(void) { 274 PRLogModuleInfo* lm = logModules; 275 276 PR_LogFlush(); 277 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 283 ) { 284 fclose(logFile); 285 } 286 #else 287 if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) { 288 PR_Close(logFile); 289 } 290 #endif 291 logFile = NULL; 292 293 if (logBuf) { 294 PR_DELETE(logBuf); 295 } 296 297 while (lm != NULL) { 298 PRLogModuleInfo* next = lm->next; 299 free((/*const*/ char*)lm->name); 300 PR_Free(lm); 301 lm = next; 302 } 303 logModules = NULL; 304 305 if (_pr_logLock) { 306 PR_DestroyLock(_pr_logLock); 307 _pr_logLock = NULL; 308 } 309 } 310 311 static void _PR_SetLogModuleLevel(PRLogModuleInfo* lm) { 312 char* ev; 313 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. 319 */ 320 PRIntn evlen = strlen(ev), pos = 0; 321 while (pos < evlen) { 322 PRIntn level = 1, count = 0, delta = 0; 323 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; 332 } 333 334 /* 335 ** If count == 2, then we got module and level. If count 336 ** == 1, then level defaults to 1 (module enabled). 337 */ 338 if (lm != NULL) { 339 if ((strcasecmp(module, "all") == 0) || 340 (strcasecmp(module, lm->name) == 0)) { 341 lm->level = (PRLogModuleLevel)level; 342 } 343 } 344 count = sscanf(&ev[pos], " , %n", &delta); 345 pos += delta; 346 if (count == EOF) { 347 break; 348 } 349 } 350 } 351 } /* end _PR_SetLogModuleLevel() */ 352 353 PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char* name) { 354 PRLogModuleInfo* lm; 355 356 if (!_pr_initialized) { 357 _PR_ImplicitInitialization(); 358 } 359 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); 367 } 368 return lm; 369 } 370 371 PR_IMPLEMENT(PRBool) PR_SetLogFile(const char* file) { 372 #ifdef _PR_USE_STDIO_FOR_LOGGING 373 FILE* newLogFile; 374 375 # ifdef XP_PC 376 if (strcmp(file, "WinDebug") == 0) { 377 newLogFile = WIN32_DEBUG_FILE; 378 } else 379 # endif 380 { 381 const char* mode = appendToLog ? "a" : "w"; 382 newLogFile = fopen(file, mode); 383 if (!newLogFile) { 384 return PR_FALSE; 385 } 386 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 391 } 392 if (logFile && logFile != stdout && logFile != stderr 393 # ifdef XP_PC 394 && logFile != WIN32_DEBUG_FILE 395 # endif 396 ) { 397 fclose(logFile); 398 } 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; 408 } 409 410 newLogFile = PR_Open(file, flags, 0666); 411 if (newLogFile) { 412 if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) { 413 PR_Close(logFile); 414 } 415 logFile = newLogFile; 416 } 417 return (PRBool)(newLogFile != 0); 418 #endif /* _PR_USE_STDIO_FOR_LOGGING */ 419 } 420 421 PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size) { 422 PR_LogFlush(); 423 424 if (logBuf) { 425 PR_DELETE(logBuf); 426 } 427 428 if (buffer_size >= LINE_BUF_SIZE) { 429 logp = logBuf = (char*)PR_MALLOC(buffer_size); 430 logEndp = logp + buffer_size; 431 } 432 } 433 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; 441 442 if (!_pr_initialized) { 443 _PR_ImplicitInitialization(); 444 } 445 446 if (!logFile) { 447 return; 448 } 449 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); 456 } 457 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 465 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); 469 470 /* 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. 473 */ 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. */ 479 } 480 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; 487 } 488 /* 489 * Write out the thread id (with an optional timestamp) and the 490 * malloc'ed buffer. 491 */ 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); 500 } 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'; 508 } 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; 517 } 518 /* nb is guaranteed to fit into logBuf. */ 519 memcpy(logp, line, nb); 520 logp += nb; 521 } 522 _PR_UNLOCK_LOG(); 523 } 524 PR_LogFlush(); 525 } 526 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; 533 } 534 _PR_UNLOCK_LOG(); 535 } 536 } 537 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(); 544 } 545 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(); 557 }