1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/src/io/prlog.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,555 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 + 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "primpl.h" 1.11 +#include "prenv.h" 1.12 +#include "prprf.h" 1.13 +#include <string.h> 1.14 +#ifdef ANDROID 1.15 +#include <android/log.h> 1.16 +#endif 1.17 + 1.18 +/* 1.19 + * Lock used to lock the log. 1.20 + * 1.21 + * We can't define _PR_LOCK_LOG simply as PR_Lock because PR_Lock may 1.22 + * contain assertions. We have to avoid assertions in _PR_LOCK_LOG 1.23 + * because PR_ASSERT calls PR_LogPrint, which in turn calls _PR_LOCK_LOG. 1.24 + * This can lead to infinite recursion. 1.25 + */ 1.26 +static PRLock *_pr_logLock; 1.27 +#if defined(_PR_PTHREADS) || defined(_PR_BTHREADS) 1.28 +#define _PR_LOCK_LOG() PR_Lock(_pr_logLock); 1.29 +#define _PR_UNLOCK_LOG() PR_Unlock(_pr_logLock); 1.30 +#elif defined(_PR_GLOBAL_THREADS_ONLY) 1.31 +#define _PR_LOCK_LOG() { _PR_LOCK_LOCK(_pr_logLock) 1.32 +#define _PR_UNLOCK_LOG() _PR_LOCK_UNLOCK(_pr_logLock); } 1.33 +#else 1.34 + 1.35 +#define _PR_LOCK_LOG() \ 1.36 +{ \ 1.37 + PRIntn _is; \ 1.38 + PRThread *_me = _PR_MD_CURRENT_THREAD(); \ 1.39 + if (!_PR_IS_NATIVE_THREAD(_me)) \ 1.40 + _PR_INTSOFF(_is); \ 1.41 + _PR_LOCK_LOCK(_pr_logLock) 1.42 + 1.43 +#define _PR_UNLOCK_LOG() \ 1.44 + _PR_LOCK_UNLOCK(_pr_logLock); \ 1.45 + PR_ASSERT(_me == _PR_MD_CURRENT_THREAD()); \ 1.46 + if (!_PR_IS_NATIVE_THREAD(_me)) \ 1.47 + _PR_INTSON(_is); \ 1.48 +} 1.49 + 1.50 +#endif 1.51 + 1.52 +#if defined(XP_PC) 1.53 +#define strcasecmp stricmp 1.54 +#endif 1.55 + 1.56 +/* 1.57 + * On NT, we can't define _PUT_LOG as PR_Write or _PR_MD_WRITE, 1.58 + * because every asynchronous file io operation leads to a fiber context 1.59 + * switch. So we define _PUT_LOG as fputs (from stdio.h). A side 1.60 + * benefit is that fputs handles the LF->CRLF translation. This 1.61 + * code can also be used on other platforms with file stream io. 1.62 + */ 1.63 +#if defined(WIN32) || defined(XP_OS2) 1.64 +#define _PR_USE_STDIO_FOR_LOGGING 1.65 +#endif 1.66 + 1.67 +/* 1.68 +** Coerce Win32 log output to use OutputDebugString() when 1.69 +** NSPR_LOG_FILE is set to "WinDebug". 1.70 +*/ 1.71 +#if defined(XP_PC) 1.72 +#define WIN32_DEBUG_FILE (FILE*)-2 1.73 +#endif 1.74 + 1.75 +#ifdef WINCE 1.76 +static void OutputDebugStringA(const char* msg) { 1.77 + int len = MultiByteToWideChar(CP_ACP, 0, msg, -1, 0, 0); 1.78 + WCHAR *wMsg = (WCHAR *)PR_Malloc(len * sizeof(WCHAR)); 1.79 + MultiByteToWideChar(CP_ACP, 0, msg, -1, wMsg, len); 1.80 + OutputDebugStringW(wMsg); 1.81 + PR_Free(wMsg); 1.82 +} 1.83 +#endif 1.84 + 1.85 +/* Macros used to reduce #ifdef pollution */ 1.86 + 1.87 +#if defined(_PR_USE_STDIO_FOR_LOGGING) && defined(XP_PC) 1.88 +#define _PUT_LOG(fd, buf, nb) \ 1.89 + PR_BEGIN_MACRO \ 1.90 + if (logFile == WIN32_DEBUG_FILE) { \ 1.91 + char savebyte = buf[nb]; \ 1.92 + buf[nb] = '\0'; \ 1.93 + OutputDebugStringA(buf); \ 1.94 + buf[nb] = savebyte; \ 1.95 + } else { \ 1.96 + fwrite(buf, 1, nb, fd); \ 1.97 + fflush(fd); \ 1.98 + } \ 1.99 + PR_END_MACRO 1.100 +#elif defined(_PR_USE_STDIO_FOR_LOGGING) 1.101 +#define _PUT_LOG(fd, buf, nb) {fwrite(buf, 1, nb, fd); fflush(fd);} 1.102 +#elif defined(ANDROID) 1.103 +#define _PUT_LOG(fd, buf, nb) \ 1.104 + PR_BEGIN_MACRO \ 1.105 + if (fd == _pr_stderr) { \ 1.106 + char savebyte = buf[nb]; \ 1.107 + buf[nb] = '\0'; \ 1.108 + __android_log_write(ANDROID_LOG_INFO, "PRLog", buf); \ 1.109 + buf[nb] = savebyte; \ 1.110 + } else { \ 1.111 + PR_Write(fd, buf, nb); \ 1.112 + } \ 1.113 + PR_END_MACRO 1.114 +#elif defined(_PR_PTHREADS) 1.115 +#define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb) 1.116 +#else 1.117 +#define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb) 1.118 +#endif 1.119 + 1.120 +/************************************************************************/ 1.121 + 1.122 +static PRLogModuleInfo *logModules; 1.123 + 1.124 +static char *logBuf = NULL; 1.125 +static char *logp; 1.126 +static char *logEndp; 1.127 +#ifdef _PR_USE_STDIO_FOR_LOGGING 1.128 +static FILE *logFile = NULL; 1.129 +#else 1.130 +static PRFileDesc *logFile = 0; 1.131 +#endif 1.132 +static PRBool outputTimeStamp = PR_FALSE; 1.133 +static PRBool appendToLog = PR_FALSE; 1.134 + 1.135 +#define LINE_BUF_SIZE 512 1.136 +#define DEFAULT_BUF_SIZE 16384 1.137 + 1.138 +#ifdef _PR_NEED_STRCASECMP 1.139 + 1.140 +/* 1.141 + * strcasecmp is defined in /usr/ucblib/libucb.a on some platforms 1.142 + * such as NCR and Unixware. Linking with both libc and libucb 1.143 + * may cause some problem, so I just provide our own implementation 1.144 + * of strcasecmp here. 1.145 + */ 1.146 + 1.147 +static const unsigned char uc[] = 1.148 +{ 1.149 + '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', 1.150 + '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017', 1.151 + '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027', 1.152 + '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037', 1.153 + ' ', '!', '"', '#', '$', '%', '&', '\'', 1.154 + '(', ')', '*', '+', ',', '-', '.', '/', 1.155 + '0', '1', '2', '3', '4', '5', '6', '7', 1.156 + '8', '9', ':', ';', '<', '=', '>', '?', 1.157 + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 1.158 + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 1.159 + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 1.160 + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', 1.161 + '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 1.162 + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 1.163 + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 1.164 + 'X', 'Y', 'Z', '{', '|', '}', '~', '\177' 1.165 +}; 1.166 + 1.167 +PRIntn strcasecmp(const char *a, const char *b) 1.168 +{ 1.169 + const unsigned char *ua = (const unsigned char *)a; 1.170 + const unsigned char *ub = (const unsigned char *)b; 1.171 + 1.172 + if( ((const char *)0 == a) || (const char *)0 == b ) 1.173 + return (PRIntn)(a-b); 1.174 + 1.175 + while( (uc[*ua] == uc[*ub]) && ('\0' != *a) ) 1.176 + { 1.177 + a++; 1.178 + ua++; 1.179 + ub++; 1.180 + } 1.181 + 1.182 + return (PRIntn)(uc[*ua] - uc[*ub]); 1.183 +} 1.184 + 1.185 +#endif /* _PR_NEED_STRCASECMP */ 1.186 + 1.187 +void _PR_InitLog(void) 1.188 +{ 1.189 + char *ev; 1.190 + 1.191 + _pr_logLock = PR_NewLock(); 1.192 + 1.193 + ev = PR_GetEnv("NSPR_LOG_MODULES"); 1.194 + if (ev && ev[0]) { 1.195 + char module[64]; /* Security-Critical: If you change this 1.196 + * size, you must also change the sscanf 1.197 + * format string to be size-1. 1.198 + */ 1.199 + PRBool isSync = PR_FALSE; 1.200 + PRIntn evlen = strlen(ev), pos = 0; 1.201 + PRInt32 bufSize = DEFAULT_BUF_SIZE; 1.202 + while (pos < evlen) { 1.203 + PRIntn level = 1, count = 0, delta = 0; 1.204 + count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n", 1.205 + module, &delta, &level, &delta); 1.206 + pos += delta; 1.207 + if (count == 0) break; 1.208 + 1.209 + /* 1.210 + ** If count == 2, then we got module and level. If count 1.211 + ** == 1, then level defaults to 1 (module enabled). 1.212 + */ 1.213 + if (strcasecmp(module, "sync") == 0) { 1.214 + isSync = PR_TRUE; 1.215 + } else if (strcasecmp(module, "bufsize") == 0) { 1.216 + if (level >= LINE_BUF_SIZE) { 1.217 + bufSize = level; 1.218 + } 1.219 + } else if (strcasecmp(module, "timestamp") == 0) { 1.220 + outputTimeStamp = PR_TRUE; 1.221 + } else if (strcasecmp(module, "append") == 0) { 1.222 + appendToLog = PR_TRUE; 1.223 + } else { 1.224 + PRLogModuleInfo *lm = logModules; 1.225 + PRBool skip_modcheck = 1.226 + (0 == strcasecmp (module, "all")) ? PR_TRUE : PR_FALSE; 1.227 + 1.228 + while (lm != NULL) { 1.229 + if (skip_modcheck) lm -> level = (PRLogModuleLevel)level; 1.230 + else if (strcasecmp(module, lm->name) == 0) { 1.231 + lm->level = (PRLogModuleLevel)level; 1.232 + break; 1.233 + } 1.234 + lm = lm->next; 1.235 + } 1.236 + } 1.237 + /*found:*/ 1.238 + count = sscanf(&ev[pos], " , %n", &delta); 1.239 + pos += delta; 1.240 + if (count == EOF) break; 1.241 + } 1.242 + PR_SetLogBuffering(isSync ? 0 : bufSize); 1.243 + 1.244 +#ifdef XP_UNIX 1.245 + if ((getuid() != geteuid()) || (getgid() != getegid())) { 1.246 + return; 1.247 + } 1.248 +#endif /* XP_UNIX */ 1.249 + 1.250 + ev = PR_GetEnv("NSPR_LOG_FILE"); 1.251 + if (ev && ev[0]) { 1.252 + if (!PR_SetLogFile(ev)) { 1.253 +#ifdef XP_PC 1.254 + char* str = PR_smprintf("Unable to create nspr log file '%s'\n", ev); 1.255 + if (str) { 1.256 + OutputDebugStringA(str); 1.257 + PR_smprintf_free(str); 1.258 + } 1.259 +#else 1.260 + fprintf(stderr, "Unable to create nspr log file '%s'\n", ev); 1.261 +#endif 1.262 + } 1.263 + } else { 1.264 +#ifdef _PR_USE_STDIO_FOR_LOGGING 1.265 + logFile = stderr; 1.266 +#else 1.267 + logFile = _pr_stderr; 1.268 +#endif 1.269 + } 1.270 + } 1.271 +} 1.272 + 1.273 +void _PR_LogCleanup(void) 1.274 +{ 1.275 + PRLogModuleInfo *lm = logModules; 1.276 + 1.277 + PR_LogFlush(); 1.278 + 1.279 +#ifdef _PR_USE_STDIO_FOR_LOGGING 1.280 + if (logFile 1.281 + && logFile != stdout 1.282 + && logFile != stderr 1.283 +#ifdef XP_PC 1.284 + && logFile != WIN32_DEBUG_FILE 1.285 +#endif 1.286 + ) { 1.287 + fclose(logFile); 1.288 + } 1.289 +#else 1.290 + if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) { 1.291 + PR_Close(logFile); 1.292 + } 1.293 +#endif 1.294 + logFile = NULL; 1.295 + 1.296 + if (logBuf) 1.297 + PR_DELETE(logBuf); 1.298 + 1.299 + while (lm != NULL) { 1.300 + PRLogModuleInfo *next = lm->next; 1.301 + free((/*const*/ char *)lm->name); 1.302 + PR_Free(lm); 1.303 + lm = next; 1.304 + } 1.305 + logModules = NULL; 1.306 + 1.307 + if (_pr_logLock) { 1.308 + PR_DestroyLock(_pr_logLock); 1.309 + _pr_logLock = NULL; 1.310 + } 1.311 +} 1.312 + 1.313 +static void _PR_SetLogModuleLevel( PRLogModuleInfo *lm ) 1.314 +{ 1.315 + char *ev; 1.316 + 1.317 + ev = PR_GetEnv("NSPR_LOG_MODULES"); 1.318 + if (ev && ev[0]) { 1.319 + char module[64]; /* Security-Critical: If you change this 1.320 + * size, you must also change the sscanf 1.321 + * format string to be size-1. 1.322 + */ 1.323 + PRIntn evlen = strlen(ev), pos = 0; 1.324 + while (pos < evlen) { 1.325 + PRIntn level = 1, count = 0, delta = 0; 1.326 + 1.327 + count = sscanf(&ev[pos], "%63[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-]%n:%d%n", 1.328 + module, &delta, &level, &delta); 1.329 + pos += delta; 1.330 + if (count == 0) break; 1.331 + 1.332 + /* 1.333 + ** If count == 2, then we got module and level. If count 1.334 + ** == 1, then level defaults to 1 (module enabled). 1.335 + */ 1.336 + if (lm != NULL) 1.337 + { 1.338 + if ((strcasecmp(module, "all") == 0) 1.339 + || (strcasecmp(module, lm->name) == 0)) 1.340 + { 1.341 + lm->level = (PRLogModuleLevel)level; 1.342 + } 1.343 + } 1.344 + count = sscanf(&ev[pos], " , %n", &delta); 1.345 + pos += delta; 1.346 + if (count == EOF) break; 1.347 + } 1.348 + } 1.349 +} /* end _PR_SetLogModuleLevel() */ 1.350 + 1.351 +PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char *name) 1.352 +{ 1.353 + PRLogModuleInfo *lm; 1.354 + 1.355 + if (!_pr_initialized) _PR_ImplicitInitialization(); 1.356 + 1.357 + lm = PR_NEWZAP(PRLogModuleInfo); 1.358 + if (lm) { 1.359 + lm->name = strdup(name); 1.360 + lm->level = PR_LOG_NONE; 1.361 + lm->next = logModules; 1.362 + logModules = lm; 1.363 + _PR_SetLogModuleLevel(lm); 1.364 + } 1.365 + return lm; 1.366 +} 1.367 + 1.368 +PR_IMPLEMENT(PRBool) PR_SetLogFile(const char *file) 1.369 +{ 1.370 +#ifdef _PR_USE_STDIO_FOR_LOGGING 1.371 + FILE *newLogFile; 1.372 + 1.373 +#ifdef XP_PC 1.374 + if ( strcmp( file, "WinDebug") == 0) 1.375 + { 1.376 + newLogFile = WIN32_DEBUG_FILE; 1.377 + } 1.378 + else 1.379 +#endif 1.380 + { 1.381 + const char *mode = appendToLog ? "a" : "w"; 1.382 + newLogFile = fopen(file, mode); 1.383 + if (!newLogFile) 1.384 + return PR_FALSE; 1.385 + 1.386 +#ifndef WINCE /* _IONBF does not exist in the Windows Mobile 6 SDK. */ 1.387 + /* We do buffering ourselves. */ 1.388 + setvbuf(newLogFile, NULL, _IONBF, 0); 1.389 +#endif 1.390 + } 1.391 + if (logFile 1.392 + && logFile != stdout 1.393 + && logFile != stderr 1.394 +#ifdef XP_PC 1.395 + && logFile != WIN32_DEBUG_FILE 1.396 +#endif 1.397 + ) { 1.398 + fclose(logFile); 1.399 + } 1.400 + logFile = newLogFile; 1.401 + return PR_TRUE; 1.402 +#else 1.403 + PRFileDesc *newLogFile; 1.404 + PRIntn flags = PR_WRONLY|PR_CREATE_FILE; 1.405 + if (appendToLog) { 1.406 + flags |= PR_APPEND; 1.407 + } else { 1.408 + flags |= PR_TRUNCATE; 1.409 + } 1.410 + 1.411 + newLogFile = PR_Open(file, flags, 0666); 1.412 + if (newLogFile) { 1.413 + if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) { 1.414 + PR_Close(logFile); 1.415 + } 1.416 + logFile = newLogFile; 1.417 + } 1.418 + return (PRBool) (newLogFile != 0); 1.419 +#endif /* _PR_USE_STDIO_FOR_LOGGING */ 1.420 +} 1.421 + 1.422 +PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size) 1.423 +{ 1.424 + PR_LogFlush(); 1.425 + 1.426 + if (logBuf) 1.427 + PR_DELETE(logBuf); 1.428 + 1.429 + if (buffer_size >= LINE_BUF_SIZE) { 1.430 + logp = logBuf = (char*) PR_MALLOC(buffer_size); 1.431 + logEndp = logp + buffer_size; 1.432 + } 1.433 +} 1.434 + 1.435 +PR_IMPLEMENT(void) PR_LogPrint(const char *fmt, ...) 1.436 +{ 1.437 + va_list ap; 1.438 + char line[LINE_BUF_SIZE]; 1.439 + char *line_long = NULL; 1.440 + PRUint32 nb_tid = 0, nb; 1.441 + PRThread *me; 1.442 + PRExplodedTime now; 1.443 + 1.444 + if (!_pr_initialized) _PR_ImplicitInitialization(); 1.445 + 1.446 + if (!logFile) { 1.447 + return; 1.448 + } 1.449 + 1.450 + if (outputTimeStamp) { 1.451 + PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now); 1.452 + nb_tid = PR_snprintf(line, sizeof(line)-1, 1.453 + "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - ", 1.454 + now.tm_year, now.tm_month + 1, now.tm_mday, 1.455 + now.tm_hour, now.tm_min, now.tm_sec, 1.456 + now.tm_usec); 1.457 + } 1.458 + 1.459 + me = PR_GetCurrentThread(); 1.460 + nb_tid += PR_snprintf(line+nb_tid, sizeof(line)-nb_tid-1, "%ld[%p]: ", 1.461 +#if defined(_PR_BTHREADS) 1.462 + me, me); 1.463 +#else 1.464 + me ? me->id : 0L, me); 1.465 +#endif 1.466 + 1.467 + va_start(ap, fmt); 1.468 + nb = nb_tid + PR_vsnprintf(line+nb_tid, sizeof(line)-nb_tid-1, fmt, ap); 1.469 + va_end(ap); 1.470 + 1.471 + /* 1.472 + * Check if we might have run out of buffer space (in case we have a 1.473 + * long line), and malloc a buffer just this once. 1.474 + */ 1.475 + if (nb == sizeof(line)-2) { 1.476 + va_start(ap, fmt); 1.477 + line_long = PR_vsmprintf(fmt, ap); 1.478 + va_end(ap); 1.479 + /* If this failed, we'll fall back to writing the truncated line. */ 1.480 + } 1.481 + 1.482 + if (line_long) { 1.483 + nb = strlen(line_long); 1.484 + _PR_LOCK_LOG(); 1.485 + if (logBuf != 0) { 1.486 + _PUT_LOG(logFile, logBuf, logp - logBuf); 1.487 + logp = logBuf; 1.488 + } 1.489 + /* 1.490 + * Write out the thread id (with an optional timestamp) and the 1.491 + * malloc'ed buffer. 1.492 + */ 1.493 + _PUT_LOG(logFile, line, nb_tid); 1.494 + _PUT_LOG(logFile, line_long, nb); 1.495 + /* Ensure there is a trailing newline. */ 1.496 + if (!nb || (line_long[nb-1] != '\n')) { 1.497 + char eol[2]; 1.498 + eol[0] = '\n'; 1.499 + eol[1] = '\0'; 1.500 + _PUT_LOG(logFile, eol, 1); 1.501 + } 1.502 + _PR_UNLOCK_LOG(); 1.503 + PR_smprintf_free(line_long); 1.504 + } else { 1.505 + /* Ensure there is a trailing newline. */ 1.506 + if (nb && (line[nb-1] != '\n')) { 1.507 + line[nb++] = '\n'; 1.508 + line[nb] = '\0'; 1.509 + } 1.510 + _PR_LOCK_LOG(); 1.511 + if (logBuf == 0) { 1.512 + _PUT_LOG(logFile, line, nb); 1.513 + } else { 1.514 + /* If nb can't fit into logBuf, write out logBuf first. */ 1.515 + if (logp + nb > logEndp) { 1.516 + _PUT_LOG(logFile, logBuf, logp - logBuf); 1.517 + logp = logBuf; 1.518 + } 1.519 + /* nb is guaranteed to fit into logBuf. */ 1.520 + memcpy(logp, line, nb); 1.521 + logp += nb; 1.522 + } 1.523 + _PR_UNLOCK_LOG(); 1.524 + } 1.525 + PR_LogFlush(); 1.526 +} 1.527 + 1.528 +PR_IMPLEMENT(void) PR_LogFlush(void) 1.529 +{ 1.530 + if (logBuf && logFile) { 1.531 + _PR_LOCK_LOG(); 1.532 + if (logp > logBuf) { 1.533 + _PUT_LOG(logFile, logBuf, logp - logBuf); 1.534 + logp = logBuf; 1.535 + } 1.536 + _PR_UNLOCK_LOG(); 1.537 + } 1.538 +} 1.539 + 1.540 +PR_IMPLEMENT(void) PR_Abort(void) 1.541 +{ 1.542 + PR_LogPrint("Aborting"); 1.543 + abort(); 1.544 +} 1.545 + 1.546 +PR_IMPLEMENT(void) PR_Assert(const char *s, const char *file, PRIntn ln) 1.547 +{ 1.548 + PR_LogPrint("Assertion failure: %s, at %s:%d\n", s, file, ln); 1.549 + fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); 1.550 + fflush(stderr); 1.551 +#ifdef WIN32 1.552 + DebugBreak(); 1.553 +#endif 1.554 +#ifdef XP_OS2 1.555 + asm("int $3"); 1.556 +#endif 1.557 + abort(); 1.558 +}