VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/log/log.cpp@ 11853

Last change on this file since 11853 was 11853, checked in by vboxsync, 16 years ago

IPRT: Added a 'append' flag to the logger.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 78.8 KB
Line 
1/* $Id: log.cpp 11853 2008-08-29 23:11:54Z vboxsync $ */
2/** @file
3 * Runtime VBox - Logger.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 *
26 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#include <iprt/log.h>
36#ifndef IN_GC
37# include <iprt/alloc.h>
38# include <iprt/process.h>
39# include <iprt/semaphore.h>
40# include <iprt/thread.h>
41# include <iprt/mp.h>
42#endif
43#ifdef IN_RING3
44# include <iprt/file.h>
45# include <iprt/path.h>
46#endif
47#include <iprt/time.h>
48#include <iprt/asm.h>
49#include <iprt/assert.h>
50#include <iprt/err.h>
51#include <iprt/param.h>
52
53#include <iprt/stdarg.h>
54#include <iprt/string.h>
55#include <iprt/ctype.h>
56#ifdef IN_RING3
57# include <iprt/alloca.h>
58# include <stdio.h>
59#endif
60
61
62/*******************************************************************************
63* Structures and Typedefs *
64*******************************************************************************/
65/**
66 * Arguments passed to the output function.
67 */
68typedef struct RTLOGOUTPUTPREFIXEDARGS
69{
70 /** The logger instance. */
71 PRTLOGGER pLogger;
72 /** The flags. (used for prefixing.) */
73 unsigned fFlags;
74 /** The group. (used for prefixing.) */
75 unsigned iGroup;
76} RTLOGOUTPUTPREFIXEDARGS, *PRTLOGOUTPUTPREFIXEDARGS;
77
78
79/*******************************************************************************
80* Internal Functions *
81*******************************************************************************/
82#ifndef IN_GC
83static unsigned rtlogGroupFlags(const char *psz);
84#endif
85static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args);
86static void rtlogFlush(PRTLOGGER pLogger);
87static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars);
88static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars);
89
90
91/*******************************************************************************
92* Global Variables *
93*******************************************************************************/
94#ifdef IN_GC
95/** Default logger instance. */
96extern "C" DECLIMPORT(RTLOGGERRC) g_Logger;
97/** Default relese logger instance. */
98extern "C" DECLIMPORT(RTLOGGERRC) g_RelLogger;
99#else /* !IN_GC */
100/** Default logger instance. */
101static PRTLOGGER g_pLogger;
102/** Default release logger instance. */
103static PRTLOGGER g_pRelLogger;
104#endif /* !IN_GC */
105#ifdef IN_RING3
106/** The RTThreadGetWriteLockCount() change caused by the logger mutex semaphore. */
107static uint32_t volatile g_cLoggerLockCount;
108#endif
109#ifdef IN_RING0
110/** Number of per-thread loggers. */
111static int32_t volatile g_cPerThreadLoggers;
112/** Per-thread loggers.
113 * This is just a quick TLS hack suitable for debug logging only.
114 * If we run out of entries, just unload and reload the driver. */
115static struct RTLOGGERPERTHREAD
116{
117 /** The thread. */
118 RTNATIVETHREAD volatile NativeThread;
119 /** The (process / session) key. */
120 uintptr_t volatile uKey;
121 /** The logger instance.*/
122 PRTLOGGER volatile pLogger;
123} g_aPerThreadLoggers[8] =
124{ { NIL_RTNATIVETHREAD, 0, 0},
125 { NIL_RTNATIVETHREAD, 0, 0},
126 { NIL_RTNATIVETHREAD, 0, 0},
127 { NIL_RTNATIVETHREAD, 0, 0},
128 { NIL_RTNATIVETHREAD, 0, 0},
129 { NIL_RTNATIVETHREAD, 0, 0},
130 { NIL_RTNATIVETHREAD, 0, 0},
131 { NIL_RTNATIVETHREAD, 0, 0}
132};
133#endif /* IN_RING0 */
134
135
136/**
137 * Locks the logger instance.
138 *
139 * @returns See RTSemFastMutexRequest().
140 * @param pLogger The logger instance.
141 */
142DECLINLINE(int) rtlogLock(PRTLOGGER pLogger)
143{
144#ifndef IN_GC
145 if (pLogger->MutexSem != NIL_RTSEMFASTMUTEX)
146 {
147 int rc = RTSemFastMutexRequest(pLogger->MutexSem);
148 AssertRCReturn(rc, rc);
149 }
150#endif
151 return VINF_SUCCESS;
152}
153
154
155/**
156 * Unlocks the logger instance.
157 * @param pLogger The logger instance.
158 */
159DECLINLINE(void) rtlogUnlock(PRTLOGGER pLogger)
160{
161#ifndef IN_GC
162 if (pLogger->MutexSem != NIL_RTSEMFASTMUTEX)
163 RTSemFastMutexRelease(pLogger->MutexSem);
164#endif
165 return;
166}
167
168
169#ifndef IN_GC
170/**
171 * Create a logger instance, comprehensive version.
172 *
173 * @returns iprt status code.
174 *
175 * @param ppLogger Where to store the logger instance.
176 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
177 * @param pszGroupSettings The initial group settings.
178 * @param pszEnvVarBase Base name for the environment variables for this instance.
179 * @param cGroups Number of groups in the array.
180 * @param papszGroups Pointer to array of groups. This must stick around for the life of the
181 * logger instance.
182 * @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
183 * @param pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.
184 * @param cchErrorMsg The size of the error message buffer.
185 * @param pszFilenameFmt Log filename format string. Standard RTStrFormat().
186 * @param ... Format arguments.
187 */
188RTDECL(int) RTLogCreateExV(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,
189 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
190 RTUINT fDestFlags, char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, va_list args)
191{
192 int rc;
193 size_t cb;
194 PRTLOGGER pLogger;
195
196 /*
197 * Validate input.
198 */
199 if ( (cGroups && !papszGroups)
200 || !VALID_PTR(ppLogger)
201 )
202 {
203 AssertMsgFailed(("Invalid parameters!\n"));
204 return VERR_INVALID_PARAMETER;
205 }
206 *ppLogger = NULL;
207
208 if (pszErrorMsg)
209 RTStrPrintf(pszErrorMsg, cchErrorMsg, "unknown error");
210
211 /*
212 * Allocate a logger instance.
213 */
214 cb = RT_OFFSETOF(RTLOGGER, afGroups[cGroups + 1]) + RTPATH_MAX;
215 pLogger = (PRTLOGGER)RTMemAllocZ(cb);
216 if (pLogger)
217 {
218 uint8_t *pu8Code;
219
220 pLogger->u32Magic = RTLOGGER_MAGIC;
221 pLogger->papszGroups = papszGroups;
222 pLogger->cMaxGroups = cGroups;
223 pLogger->cGroups = cGroups;
224 pLogger->pszFilename = (char *)&pLogger->afGroups[cGroups + 1];
225 pLogger->File = NIL_RTFILE;
226 pLogger->fFlags = fFlags;
227 pLogger->fDestFlags = fDestFlags;
228 pLogger->fPendingPrefix = true;
229 if (pszGroupSettings)
230 RTLogGroupSettings(pLogger, pszGroupSettings);
231
232 /*
233 * Emit wrapper code.
234 */
235 pu8Code = (uint8_t *)RTMemExecAlloc(64);
236 if (pu8Code)
237 {
238 pLogger->pfnLogger = *(PFNRTLOGGER*)&pu8Code;
239#ifdef RT_ARCH_AMD64
240 /* this wrapper will not be used on AMD64, we will be requiring C99 compilers there. */
241 *pu8Code++ = 0xcc;
242#else
243 *pu8Code++ = 0x68; /* push imm32 */
244 *(void **)pu8Code = pLogger;
245 pu8Code += sizeof(void *);
246 *pu8Code++ = 0xe8; /* call rel32 */
247 *(uint32_t *)pu8Code = (uintptr_t)RTLogLogger - ((uintptr_t)pu8Code + sizeof(uint32_t));
248 pu8Code += sizeof(uint32_t);
249 *pu8Code++ = 0x8d; /* lea esp, [esp + 4] */
250 *pu8Code++ = 0x64;
251 *pu8Code++ = 0x24;
252 *pu8Code++ = 0x04;
253 *pu8Code++ = 0xc3; /* ret near */
254#endif
255 AssertMsg((uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger <= 64,
256 ("Wrapper assembly is too big! %d bytes\n", (uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger));
257
258
259#ifdef IN_RING3 /* files and env.vars. are only accessible when in R3 at the present time. */
260 /*
261 * Format the filename.
262 */
263 if (pszFilenameFmt)
264 {
265 RTStrPrintfV(pLogger->pszFilename, RTPATH_MAX, pszFilenameFmt, args);
266 pLogger->fDestFlags |= RTLOGDEST_FILE;
267 }
268
269 /*
270 * Parse the environment variables.
271 */
272 if (pszEnvVarBase)
273 {
274 /* make temp copy of environment variable base. */
275 size_t cchEnvVarBase = strlen(pszEnvVarBase);
276 char *pszEnvVar = (char *)alloca(cchEnvVarBase + 16);
277 memcpy(pszEnvVar, pszEnvVarBase, cchEnvVarBase);
278
279 /*
280 * Destination.
281 */
282 strcpy(pszEnvVar + cchEnvVarBase, "_DEST");
283 const char *pszVar = getenv(pszEnvVar);
284 if (pszVar)
285 {
286 while (*pszVar)
287 {
288 /* skip blanks. */
289 while (RT_C_IS_SPACE(*pszVar))
290 pszVar++;
291 if (!*pszVar)
292 break;
293
294 /* parse instruction. */
295 static struct
296 {
297 const char *pszInstr;
298 unsigned fFlag;
299 } const aDest[] =
300 {
301 { "file", RTLOGDEST_FILE }, /* Must be 1st! */
302 { "dir", RTLOGDEST_FILE }, /* Must be 2nd! */
303 { "stdout", RTLOGDEST_STDOUT },
304 { "stderr", RTLOGDEST_STDERR },
305 { "debugger", RTLOGDEST_DEBUGGER },
306 { "com", RTLOGDEST_COM },
307 { "user", RTLOGDEST_USER },
308 };
309
310 /* check no prefix. */
311 bool fNo = false;
312 if (pszVar[0] == 'n' && pszVar[1] == 'o')
313 {
314 fNo = true;
315 pszVar += 2;
316 }
317
318 /* instruction. */
319 unsigned i;
320 for (i = 0; i < ELEMENTS(aDest); i++)
321 {
322 size_t cchInstr = strlen(aDest[i].pszInstr);
323 if (!strncmp(pszVar, aDest[i].pszInstr, cchInstr))
324 {
325 if (!fNo)
326 pLogger->fDestFlags |= aDest[i].fFlag;
327 else
328 pLogger->fDestFlags &= ~aDest[i].fFlag;
329 pszVar += cchInstr;
330
331 /* check for value. */
332 while (RT_C_IS_SPACE(*pszVar))
333 pszVar++;
334 if (*pszVar == '=' || *pszVar == ':')
335 {
336 pszVar++;
337 const char *pszEnd = strchr(pszVar, ';');
338 if (!pszEnd)
339 pszEnd = strchr(pszVar, '\0');
340
341 /* log file name */
342 size_t cch = pszEnd - pszVar;
343 if (i == 0 /* file */ && !fNo)
344 {
345 memcpy(pLogger->pszFilename, pszVar, cch);
346 pLogger->pszFilename[cch] = '\0';
347 }
348 /* log directory */
349 else if (i == 1 /* dir */ && !fNo)
350 {
351 char szTmp[RTPATH_MAX];
352 const char *pszFile = RTPathFilename(pLogger->pszFilename);
353 if (pszFile)
354 strcpy(szTmp, pszFile);
355 else
356 pszFile = ""; /* you've screwed up, sir. */
357
358 memcpy(pLogger->pszFilename, pszVar, cch);
359 pLogger->pszFilename[cch] = '\0';
360 RTPathStripTrailingSlash(pLogger->pszFilename);
361
362 cch = strlen(pLogger->pszFilename);
363 pLogger->pszFilename[cch++] = '/';
364 strcpy(&pLogger->pszFilename[cch], szTmp);
365 }
366 else
367 AssertMsgFailed(("Invalid %s_DEST! %s%s doesn't take a value!\n", pszEnvVarBase, fNo ? "no" : "", aDest[i].pszInstr));
368 pszVar = pszEnd + (*pszEnd != '\0');
369 }
370 break;
371 }
372 }
373 /* unknown instruction? */
374 if (i >= ELEMENTS(aDest))
375 {
376 AssertMsgFailed(("Invalid %s_DEST! unknown instruction %.20s\n", pszEnvVarBase, pszVar));
377 pszVar++;
378 }
379
380 /* skip blanks and delimiters. */
381 while (RT_C_IS_SPACE(*pszVar) || *pszVar == ';')
382 pszVar++;
383 } /* while more environment variable value left */
384 }
385
386 /*
387 * The flags.
388 */
389 strcpy(pszEnvVar + cchEnvVarBase, "_FLAGS");
390 pszVar = getenv(pszEnvVar);
391 if (pszVar)
392 RTLogFlags(pLogger, pszVar);
393
394 /*
395 * The group settings.
396 */
397 pszEnvVar[cchEnvVarBase] = '\0';
398 pszVar = getenv(pszEnvVar);
399 if (pszVar)
400 RTLogGroupSettings(pLogger, pszVar);
401 }
402#endif /* IN_RING3 */
403
404 /*
405 * Open the destination(s).
406 */
407 rc = VINF_SUCCESS;
408#ifdef IN_RING3
409 if (pLogger->fDestFlags & RTLOGDEST_FILE)
410 {
411 if (!(pLogger->fFlags & RTLOGFLAGS_APPEND))
412 rc = RTFileOpen(&pLogger->File, pLogger->pszFilename,
413 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE);
414 else
415 {
416 /** @todo RTFILE_O_APPEND. */
417 rc = RTFileOpen(&pLogger->File, pLogger->pszFilename,
418 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE);
419 if (RT_SUCCESS(rc))
420 {
421 rc = RTFileSeek(pLogger->File, 0, RTFILE_SEEK_END, NULL);
422 if (RT_FAILURE(rc))
423 {
424 RTFileClose(pLogger->File);
425 pLogger->File = NIL_RTFILE;
426 }
427 }
428 }
429 if (RT_FAILURE(rc) && pszErrorMsg)
430 RTStrPrintf(pszErrorMsg, cchErrorMsg, "could not open file '%s'", pLogger->pszFilename);
431 }
432#endif /* IN_RING3 */
433
434 /*
435 * Create mutex and check how much it counts when entering the lock
436 * so that we can report the values for RTLOGFLAGS_PREFIX_LOCK_COUNTS.
437 */
438 if (RT_SUCCESS(rc))
439 {
440 rc = RTSemFastMutexCreate(&pLogger->MutexSem);
441 if (RT_SUCCESS(rc))
442 {
443#ifdef IN_RING3 /** @todo do counters in ring-0 too? */
444 RTTHREAD Thread = RTThreadSelf();
445 if (Thread != NIL_RTTHREAD)
446 {
447 int32_t c = RTThreadGetWriteLockCount(Thread);
448 RTSemFastMutexRequest(pLogger->MutexSem);
449 c = RTThreadGetWriteLockCount(Thread) - c;
450 RTSemFastMutexRelease(pLogger->MutexSem);
451 ASMAtomicWriteU32(&g_cLoggerLockCount, c);
452 }
453#endif
454 *ppLogger = pLogger;
455 return VINF_SUCCESS;
456 }
457
458 if (pszErrorMsg)
459 RTStrPrintf(pszErrorMsg, cchErrorMsg, "failed to create sempahore");
460 }
461#ifdef IN_RING3
462 RTFileClose(pLogger->File);
463#endif
464 RTMemExecFree(*(void **)&pLogger->pfnLogger);
465 }
466 else
467 {
468#ifdef RT_OS_LINUX
469 /*
470 * RTMemAlloc() succeeded but RTMemExecAlloc() failed -- most probably an SELinux problem.
471 */
472 if (pszErrorMsg)
473 RTStrPrintf(pszErrorMsg, cchErrorMsg, "mmap(PROT_WRITE | PROT_EXEC) failed -- SELinux?");
474#endif /* RT_OS_LINUX */
475 rc = VERR_NO_MEMORY;
476 }
477 RTMemFree(pLogger);
478 }
479 else
480 rc = VERR_NO_MEMORY;
481
482 return rc;
483}
484
485/**
486 * Create a logger instance.
487 *
488 * @returns iprt status code.
489 *
490 * @param ppLogger Where to store the logger instance.
491 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
492 * @param pszGroupSettings The initial group settings.
493 * @param pszEnvVarBase Base name for the environment variables for this instance.
494 * @param cGroups Number of groups in the array.
495 * @param papszGroups Pointer to array of groups. This must stick around for the life of the
496 * logger instance.
497 * @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
498 * @param pszFilenameFmt Log filename format string. Standard RTStrFormat().
499 * @param ... Format arguments.
500 */
501RTDECL(int) RTLogCreate(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,
502 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
503 RTUINT fDestFlags, const char *pszFilenameFmt, ...)
504{
505 va_list args;
506 int rc;
507
508 va_start(args, pszFilenameFmt);
509 rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, NULL, 0, pszFilenameFmt, args);
510 va_end(args);
511 return rc;
512}
513
514/**
515 * Create a logger instance.
516 *
517 * @returns iprt status code.
518 *
519 * @param ppLogger Where to store the logger instance.
520 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
521 * @param pszGroupSettings The initial group settings.
522 * @param pszEnvVarBase Base name for the environment variables for this instance.
523 * @param cGroups Number of groups in the array.
524 * @param papszGroups Pointer to array of groups. This must stick around for the life of the
525 * logger instance.
526 * @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
527 * @param pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.
528 * @param cchErrorMsg The size of the error message buffer.
529 * @param pszFilenameFmt Log filename format string. Standard RTStrFormat().
530 * @param ... Format arguments.
531 */
532RTDECL(int) RTLogCreateEx(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,
533 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
534 RTUINT fDestFlags, char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, ...)
535{
536 va_list args;
537 int rc;
538
539 va_start(args, pszFilenameFmt);
540 rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, pszErrorMsg, cchErrorMsg, pszFilenameFmt, args);
541 va_end(args);
542 return rc;
543}
544
545/**
546 * Destroys a logger instance.
547 *
548 * The instance is flushed and all output destinations closed (where applicable).
549 *
550 * @returns iprt status code.
551 * @param pLogger The logger instance which close destroyed. NULL is fine.
552 */
553RTDECL(int) RTLogDestroy(PRTLOGGER pLogger)
554{
555 int rc;
556 RTUINT iGroup;
557 RTSEMFASTMUTEX MutexSem;
558
559 /*
560 * Validate input.
561 */
562 if (!pLogger)
563 return VINF_SUCCESS;
564 AssertReturn(VALID_PTR(pLogger), VERR_INVALID_POINTER);
565 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
566
567 /*
568 * Acquire logger instance sem and disable all logging. (paranoia)
569 */
570 rc = rtlogLock(pLogger);
571 if (RT_FAILURE(rc))
572 return rc;
573
574 pLogger->fFlags |= RTLOGFLAGS_DISABLED;
575 iGroup = pLogger->cGroups;
576 while (iGroup-- > 0)
577 pLogger->afGroups[iGroup] = 0;
578
579 /*
580 * Flush it.
581 */
582 RTLogFlush(pLogger);
583
584 /*
585 * Close output stuffs.
586 */
587#ifdef IN_RING3
588 if (pLogger->File != NIL_RTFILE)
589 {
590 int rc2 = RTFileClose(pLogger->File);
591 AssertRC(rc2);
592 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
593 rc = rc2;
594 pLogger->File = NIL_RTFILE;
595 }
596#endif
597
598 /*
599 * Free the mutex and the instance memory.
600 */
601 MutexSem = pLogger->MutexSem;
602 pLogger->MutexSem = NIL_RTSEMFASTMUTEX;
603 if (MutexSem != NIL_RTSEMFASTMUTEX)
604 {
605 int rc2;
606 RTSemFastMutexRelease(MutexSem);
607 rc2 = RTSemFastMutexDestroy(MutexSem);
608 AssertRC(rc2);
609 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
610 rc = rc2;
611 }
612
613 RTMemFree(pLogger);
614
615 return rc;
616}
617
618
619/**
620 * Create a logger instance clone for RC usage.
621 *
622 * @returns iprt status code.
623 *
624 * @param pLogger The logger instance to be cloned.
625 * @param pLoggerGC Where to create the GC logger instance.
626 * @param cbLoggerGC Amount of memory allocated to for the GC logger instance clone.
627 * @param pfnLoggerGCPtr Pointer to logger wrapper function for this instance (GC Ptr).
628 * @param pfnFlushGCPtr Pointer to flush function (GC Ptr).
629 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
630 */
631RTDECL(int) RTLogCloneRC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerGC, size_t cbLoggerGC,
632 RTRCPTR pfnLoggerGCPtr, RTRCPTR pfnFlushGCPtr, RTUINT fFlags)
633{
634 /*
635 * Validate input.
636 */
637 if ( !pLoggerGC
638 || !pfnFlushGCPtr
639 || !pfnLoggerGCPtr)
640 {
641 AssertMsgFailed(("Invalid parameters!\n"));
642 return VERR_INVALID_PARAMETER;
643 }
644 if (cbLoggerGC < sizeof(*pLoggerGC))
645 {
646 AssertMsgFailed(("%d min=%d\n", cbLoggerGC, sizeof(*pLoggerGC)));
647 return VERR_INVALID_PARAMETER;
648 }
649
650 /*
651 * Initialize GC instance.
652 */
653 pLoggerGC->offScratch = 0;
654 pLoggerGC->fPendingPrefix = false;
655 pLoggerGC->pfnLogger = pfnLoggerGCPtr;
656 pLoggerGC->pfnFlush = pfnFlushGCPtr;
657 pLoggerGC->u32Magic = RTLOGGERRC_MAGIC;
658 pLoggerGC->fFlags = fFlags | RTLOGFLAGS_DISABLED;
659 pLoggerGC->cGroups = 1;
660 pLoggerGC->afGroups[0] = 0;
661
662 /*
663 * Resolve defaults.
664 */
665 if (!pLogger)
666 {
667 pLogger = RTLogDefaultInstance();
668 if (!pLogger)
669 return VINF_SUCCESS;
670 }
671
672 /*
673 * Check if there's enough space for the groups.
674 */
675 if (cbLoggerGC < (size_t)RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]))
676 {
677 AssertMsgFailed(("%d req=%d cGroups=%d\n", cbLoggerGC, RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]), pLogger->cGroups));
678 return VERR_INVALID_PARAMETER;
679 }
680 memcpy(&pLoggerGC->afGroups[0], &pLogger->afGroups[0], pLogger->cGroups * sizeof(pLoggerGC->afGroups[0]));
681 pLoggerGC->cGroups = pLogger->cGroups;
682
683 /*
684 * Copy bits from the HC instance.
685 */
686 pLoggerGC->fPendingPrefix = pLogger->fPendingPrefix;
687 pLoggerGC->fFlags |= pLogger->fFlags;
688
689 /*
690 * Check if we can remove the disabled flag.
691 */
692 if ( pLogger->fDestFlags
693 && !((pLogger->fFlags | fFlags) & RTLOGFLAGS_DISABLED))
694 pLoggerGC->fFlags &= ~RTLOGFLAGS_DISABLED;
695
696 return VINF_SUCCESS;
697}
698
699
700/**
701 * Flushes a GC logger instance to a HC logger.
702 *
703 *
704 * @returns iprt status code.
705 * @param pLogger The HC logger instance to flush pLoggerGC to.
706 * If NULL the default logger is used.
707 * @param pLoggerGC The GC logger instance to flush.
708 */
709RTDECL(void) RTLogFlushGC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerGC)
710{
711 /*
712 * Resolve defaults.
713 */
714 if (!pLogger)
715 {
716 pLogger = RTLogDefaultInstance();
717 if (!pLogger)
718 {
719 pLoggerGC->offScratch = 0;
720 return;
721 }
722 }
723
724 /*
725 * Any thing to flush?
726 */
727 if ( pLogger->offScratch
728 || pLoggerGC->offScratch)
729 {
730 /*
731 * Acquire logger instance sem.
732 */
733 int rc = rtlogLock(pLogger);
734 if (RT_FAILURE(rc))
735 return;
736
737 /*
738 * Write whatever the GC instance contains to the HC one, and then
739 * flush the HC instance.
740 */
741 if (pLoggerGC->offScratch)
742 {
743 rtLogOutput(pLogger, pLoggerGC->achScratch, pLoggerGC->offScratch);
744 rtLogOutput(pLogger, NULL, 0);
745 pLoggerGC->offScratch = 0;
746 }
747
748 /*
749 * Release the semaphore.
750 */
751 rtlogUnlock(pLogger);
752 }
753}
754
755
756#ifdef IN_RING3
757/**
758 * Create a logger instance for singled threaded ring-0 usage.
759 *
760 * @returns iprt status code.
761 *
762 * @param pLogger Where to create the logger instance.
763 * @param cbLogger The amount of memory available for the logger instance.
764 * @param pfnLogger Pointer to logger wrapper function for the clone.
765 * @param pfnFlush Pointer to flush function for the clone.
766 * @param fFlags Logger instance flags for the clone, a combination of the RTLOGFLAGS_* values.
767 * @param fDestFlags The destination flags.
768 */
769RTDECL(int) RTLogCreateForR0(PRTLOGGER pLogger, size_t cbLogger, PFNRTLOGGER pfnLogger, PFNRTLOGFLUSH pfnFlush, RTUINT fFlags, RTUINT fDestFlags)
770{
771 /*
772 * Validate input.
773 */
774 AssertPtrReturn(pLogger, VERR_INVALID_PARAMETER);
775 AssertReturn(cbLogger >= sizeof(*pLogger), VERR_INVALID_PARAMETER);
776 AssertReturn(pfnLogger, VERR_INVALID_PARAMETER);
777 AssertReturn(pfnFlush, VERR_INVALID_PARAMETER);
778
779 /*
780 * Initialize the ring-0 instance.
781 */
782 pLogger->offScratch = 0;
783 pLogger->fPendingPrefix = false;
784 pLogger->pfnLogger = pfnLogger;
785 pLogger->pfnFlush = pfnFlush;
786 pLogger->MutexSem = NIL_RTSEMFASTMUTEX; /* Not serialized. */
787 pLogger->u32Magic = RTLOGGER_MAGIC;
788 pLogger->fFlags = fFlags;
789 pLogger->fDestFlags = fDestFlags & ~RTLOGDEST_FILE;
790 pLogger->File = NIL_RTFILE;
791 pLogger->pszFilename = NULL;
792 pLogger->papszGroups = NULL;
793 pLogger->cMaxGroups = (cbLogger - RT_OFFSETOF(RTLOGGER, afGroups[0])) / sizeof(pLogger->afGroups[0]);
794 pLogger->cGroups = 1;
795 pLogger->afGroups[0] = 0;
796 return VINF_SUCCESS;
797}
798#endif /* IN_RING3 */
799
800
801/**
802 * Copies the group settings and flags from logger instance to another.
803 *
804 * @returns IPRT status code.
805 * @param pDstLogger The destination logger instance.
806 * @param pSrcLogger The source logger instance. If NULL the default one is used.
807 * @param fFlagsOr OR mask for the flags.
808 * @param fFlagsAnd AND mask for the flags.
809 */
810RTDECL(int) RTLogCopyGroupsAndFlags(PRTLOGGER pDstLogger, PCRTLOGGER pSrcLogger, unsigned fFlagsOr, unsigned fFlagsAnd)
811{
812 int rc;
813 unsigned cGroups;
814
815 /*
816 * Validate input.
817 */
818 AssertPtrReturn(pDstLogger, VERR_INVALID_PARAMETER);
819 AssertPtrNullReturn(pSrcLogger, VERR_INVALID_PARAMETER);
820
821 /*
822 * Resolve defaults.
823 */
824 if (!pSrcLogger)
825 {
826 pSrcLogger = RTLogDefaultInstance();
827 if (!pSrcLogger)
828 {
829 pDstLogger->fFlags |= RTLOGFLAGS_DISABLED;
830 pDstLogger->cGroups = 1;
831 pDstLogger->afGroups[0] = 0;
832 return VINF_SUCCESS;
833 }
834 }
835
836 /*
837 * Copy flags and group settings.
838 */
839 pDstLogger->fFlags = (pSrcLogger->fFlags & fFlagsAnd) | fFlagsOr;
840
841 rc = VINF_SUCCESS;
842 cGroups = pSrcLogger->cGroups;
843 if (cGroups < pDstLogger->cMaxGroups)
844 {
845 AssertMsgFailed(("cMaxGroups=%zd cGroups=%zd (min size %d)\n", pDstLogger->cMaxGroups,
846 pSrcLogger->cGroups, RT_OFFSETOF(RTLOGGER, afGroups[pSrcLogger->cGroups])));
847 rc = VERR_INVALID_PARAMETER;
848 cGroups = pDstLogger->cMaxGroups;
849 }
850 memcpy(&pDstLogger->afGroups[0], &pSrcLogger->afGroups[0], cGroups * sizeof(pDstLogger->afGroups[0]));
851 pDstLogger->cGroups = cGroups;
852
853 return rc;
854}
855
856
857/**
858 * Flushes the buffer in one logger instance onto another logger.
859 *
860 * @returns iprt status code.
861 *
862 * @param pSrcLogger The logger instance to flush.
863 * @param pDstLogger The logger instance to flush onto.
864 * If NULL the default logger will be used.
865 */
866RTDECL(void) RTLogFlushToLogger(PRTLOGGER pSrcLogger, PRTLOGGER pDstLogger)
867{
868 /*
869 * Resolve defaults.
870 */
871 if (!pDstLogger)
872 {
873 pDstLogger = RTLogDefaultInstance();
874 if (!pDstLogger)
875 {
876 /* flushing to "/dev/null". */
877 if (pSrcLogger->offScratch)
878 {
879 int rc = rtlogLock(pSrcLogger);
880 if (RT_SUCCESS(rc))
881 {
882 pSrcLogger->offScratch = 0;
883 rtlogLock(pSrcLogger);
884 }
885 }
886 return;
887 }
888 }
889
890 /*
891 * Any thing to flush?
892 */
893 if ( pSrcLogger->offScratch
894 || pDstLogger->offScratch)
895 {
896 /*
897 * Acquire logger semaphores.
898 */
899 int rc = rtlogLock(pDstLogger);
900 if (RT_FAILURE(rc))
901 return;
902 rc = rtlogLock(pSrcLogger);
903 if (RT_SUCCESS(rc))
904 {
905 /*
906 * Write whatever the GC instance contains to the HC one, and then
907 * flush the HC instance.
908 */
909 if (pSrcLogger->offScratch)
910 {
911 rtLogOutput(pDstLogger, pSrcLogger->achScratch, pSrcLogger->offScratch);
912 rtLogOutput(pDstLogger, NULL, 0);
913 pSrcLogger->offScratch = 0;
914 }
915
916 /*
917 * Release the semaphores.
918 */
919 rtlogUnlock(pSrcLogger);
920 }
921 rtlogUnlock(pDstLogger);
922 }
923}
924
925
926/**
927 * Matches a group name with a pattern mask in an case insensitive manner (ASCII).
928 *
929 * @returns true if matching and *ppachMask set to the end of the pattern.
930 * @returns false if no match.
931 * @param pszGrp The group name.
932 * @param ppachMask Pointer to the pointer to the mask. Only wildcard supported is '*'.
933 * @param cchMask The length of the mask, including modifiers. The modifiers is why
934 * we update *ppachMask on match.
935 */
936static bool rtlogIsGroupMatching(const char *pszGrp, const char **ppachMask, unsigned cchMask)
937{
938 const char *pachMask;
939
940 if (!pszGrp || !*pszGrp)
941 return false;
942 pachMask = *ppachMask;
943 for (;;)
944 {
945 if (RT_C_TO_LOWER(*pszGrp) != RT_C_TO_LOWER(*pachMask))
946 {
947 const char *pszTmp;
948
949 /*
950 * Check for wildcard and do a minimal match if found.
951 */
952 if (*pachMask != '*')
953 return false;
954
955 /* eat '*'s. */
956 do pachMask++;
957 while (--cchMask && *pachMask == '*');
958
959 /* is there more to match? */
960 if ( !cchMask
961 || *pachMask == '.'
962 || *pachMask == '=')
963 break; /* we're good */
964
965 /* do extremely minimal matching (fixme) */
966 pszTmp = strchr(pszGrp, RT_C_TO_LOWER(*pachMask));
967 if (!pszTmp)
968 pszTmp = strchr(pszGrp, RT_C_TO_UPPER(*pachMask));
969 if (!pszTmp)
970 return false;
971 pszGrp = pszTmp;
972 continue;
973 }
974
975 /* done? */
976 if (!*++pszGrp)
977 {
978 /* trailing wildcard is ok. */
979 do
980 {
981 pachMask++;
982 cchMask--;
983 } while (cchMask && *pachMask == '*');
984 if ( !cchMask
985 || *pachMask == '.'
986 || *pachMask == '=')
987 break; /* we're good */
988 return false;
989 }
990
991 if (!--cchMask)
992 return false;
993 pachMask++;
994 }
995
996 /* match */
997 *ppachMask = pachMask;
998 return true;
999}
1000
1001
1002/**
1003 * Updates the group settings for the logger instance using the specified
1004 * specification string.
1005 *
1006 * @returns iprt status code.
1007 * Failures can safely be ignored.
1008 * @param pLogger Logger instance.
1009 * @param pszVar Value to parse.
1010 */
1011RTDECL(int) RTLogGroupSettings(PRTLOGGER pLogger, const char *pszVar)
1012{
1013 /*
1014 * Resolve defaults.
1015 */
1016 if (!pLogger)
1017 {
1018 pLogger = RTLogDefaultInstance();
1019 if (!pLogger)
1020 return VINF_SUCCESS;
1021 }
1022
1023 /*
1024 * Iterate the string.
1025 */
1026 while (*pszVar)
1027 {
1028 /*
1029 * Skip prefixes (blanks, ;, + and -).
1030 */
1031 bool fEnabled = true;
1032 char ch;
1033 const char *pszStart;
1034 unsigned i;
1035 size_t cch;
1036
1037 while ((ch = *pszVar) == '+' || ch == '-' || ch == ' ' || ch == '\t' || ch == '\n' || ch == ';')
1038 {
1039 if (ch == '+' || ch == '-' || ';')
1040 fEnabled = ch != '-';
1041 pszVar++;
1042 }
1043 if (!*pszVar)
1044 break;
1045
1046 /*
1047 * Find end.
1048 */
1049 pszStart = pszVar;
1050 while ((ch = *pszVar) != '\0' && ch != '+' && ch != '-' && ch != ' ' && ch != '\t')
1051 pszVar++;
1052
1053 /*
1054 * Find the group (ascii case insensitive search).
1055 * Special group 'all'.
1056 */
1057 cch = pszVar - pszStart;
1058 if ( cch >= 3
1059 && (pszStart[0] == 'a' || pszStart[0] == 'A')
1060 && (pszStart[1] == 'l' || pszStart[1] == 'L')
1061 && (pszStart[2] == 'l' || pszStart[2] == 'L')
1062 && (cch == 3 || pszStart[3] == '.' || pszStart[3] == '='))
1063 {
1064 /*
1065 * All.
1066 */
1067 unsigned fFlags = cch == 3
1068 ? RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1
1069 : rtlogGroupFlags(&pszStart[3]);
1070 for (i = 0; i < pLogger->cGroups; i++)
1071 {
1072 if (fEnabled)
1073 pLogger->afGroups[i] |= fFlags;
1074 else
1075 pLogger->afGroups[i] &= ~fFlags;
1076 }
1077 }
1078 else
1079 {
1080 /*
1081 * Specific group(s).
1082 */
1083 for (i = 0; i < pLogger->cGroups; i++)
1084 {
1085 const char *psz2 = (const char*)pszStart;
1086 if (rtlogIsGroupMatching(pLogger->papszGroups[i], &psz2, cch))
1087 {
1088 unsigned fFlags = RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1;
1089 if (*psz2 == '.' || *psz2 == '=')
1090 fFlags = rtlogGroupFlags(psz2);
1091 if (fEnabled)
1092 pLogger->afGroups[i] |= fFlags;
1093 else
1094 pLogger->afGroups[i] &= ~fFlags;
1095 }
1096 } /* for each group */
1097 }
1098
1099 } /* parse specification */
1100
1101 return VINF_SUCCESS;
1102}
1103
1104
1105/**
1106 * Interprets the group flags suffix.
1107 *
1108 * @returns Flags specified. (0 is possible!)
1109 * @param psz Start of Suffix. (Either dot or equal sign.)
1110 */
1111static unsigned rtlogGroupFlags(const char *psz)
1112{
1113 unsigned fFlags = 0;
1114
1115 /*
1116 * Litteral flags.
1117 */
1118 while (*psz == '.')
1119 {
1120 static struct
1121 {
1122 const char *pszFlag; /* lowercase!! */
1123 unsigned fFlag;
1124 } aFlags[] =
1125 {
1126 { "eo", RTLOGGRPFLAGS_ENABLED },
1127 { "enabledonly",RTLOGGRPFLAGS_ENABLED },
1128 { "e", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },
1129 { "enabled", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },
1130 { "l1", RTLOGGRPFLAGS_LEVEL_1 },
1131 { "level1", RTLOGGRPFLAGS_LEVEL_1 },
1132 { "l", RTLOGGRPFLAGS_LEVEL_2 },
1133 { "l2", RTLOGGRPFLAGS_LEVEL_2 },
1134 { "level2", RTLOGGRPFLAGS_LEVEL_2 },
1135 { "l3", RTLOGGRPFLAGS_LEVEL_3 },
1136 { "level3", RTLOGGRPFLAGS_LEVEL_3 },
1137 { "l4", RTLOGGRPFLAGS_LEVEL_4 },
1138 { "level4", RTLOGGRPFLAGS_LEVEL_4 },
1139 { "l5", RTLOGGRPFLAGS_LEVEL_5 },
1140 { "level5", RTLOGGRPFLAGS_LEVEL_5 },
1141 { "l6", RTLOGGRPFLAGS_LEVEL_6 },
1142 { "level6", RTLOGGRPFLAGS_LEVEL_6 },
1143 { "f", RTLOGGRPFLAGS_FLOW },
1144 { "flow", RTLOGGRPFLAGS_FLOW },
1145
1146 { "lelik", RTLOGGRPFLAGS_LELIK },
1147 { "michael", RTLOGGRPFLAGS_MICHAEL },
1148 { "dmik", RTLOGGRPFLAGS_DMIK },
1149 { "sunlover", RTLOGGRPFLAGS_SUNLOVER },
1150 { "achim", RTLOGGRPFLAGS_ACHIM },
1151 { "achimha", RTLOGGRPFLAGS_ACHIM },
1152 { "s", RTLOGGRPFLAGS_SANDER },
1153 { "sander", RTLOGGRPFLAGS_SANDER },
1154 { "sandervl", RTLOGGRPFLAGS_SANDER },
1155 { "klaus", RTLOGGRPFLAGS_KLAUS },
1156 { "frank", RTLOGGRPFLAGS_FRANK },
1157 { "b", RTLOGGRPFLAGS_BIRD },
1158 { "bird", RTLOGGRPFLAGS_BIRD },
1159 { "aleksey", RTLOGGRPFLAGS_ALEKSEY },
1160 { "n", RTLOGGRPFLAGS_NONAME },
1161 { "noname", RTLOGGRPFLAGS_NONAME }
1162 };
1163 unsigned i;
1164 bool fFound = false;
1165 psz++;
1166 for (i = 0; i < ELEMENTS(aFlags) && !fFound; i++)
1167 {
1168 const char *psz1 = aFlags[i].pszFlag;
1169 const char *psz2 = psz;
1170 while (*psz1 == RT_C_TO_LOWER(*psz2))
1171 {
1172 psz1++;
1173 psz2++;
1174 if (!*psz1)
1175 {
1176 if ( (*psz2 >= 'a' && *psz2 <= 'z')
1177 || (*psz2 >= 'A' && *psz2 <= 'Z')
1178 || (*psz2 >= '0' && *psz2 <= '9') )
1179 break;
1180 fFlags |= aFlags[i].fFlag;
1181 fFound = true;
1182 psz = psz2;
1183 break;
1184 }
1185 } /* strincmp */
1186 } /* for each flags */
1187 }
1188
1189 /*
1190 * Flag value.
1191 */
1192 if (*psz == '=')
1193 {
1194 psz++;
1195 if (*psz == '~')
1196 fFlags = ~RTStrToInt32(psz + 1);
1197 else
1198 fFlags = RTStrToInt32(psz);
1199 }
1200
1201 return fFlags;
1202}
1203
1204#endif /* !IN_GC */
1205
1206
1207/**
1208 * Updates the flags for the logger instance using the specified
1209 * specification string.
1210 *
1211 * @returns iprt status code.
1212 * Failures can safely be ignored.
1213 * @param pLogger Logger instance (NULL for default logger).
1214 * @param pszVar Value to parse.
1215 */
1216RTDECL(int) RTLogFlags(PRTLOGGER pLogger, const char *pszVar)
1217{
1218 int rc = VINF_SUCCESS;
1219
1220 /*
1221 * Resolve defaults.
1222 */
1223 if (!pLogger)
1224 {
1225 pLogger = RTLogDefaultInstance();
1226 if (!pLogger)
1227 return VINF_SUCCESS;
1228 }
1229
1230 /*
1231 * Iterate the string.
1232 */
1233 while (*pszVar)
1234 {
1235 /* parse instruction. */
1236 static struct
1237 {
1238 const char *pszInstr;
1239 size_t cchInstr;
1240 unsigned fFlag;
1241 bool fInverted;
1242 } const aDest[] =
1243 {
1244 { "disabled", sizeof("disabled" ) - 1, RTLOGFLAGS_DISABLED, false },
1245 { "enabled", sizeof("enabled" ) - 1, RTLOGFLAGS_DISABLED, true },
1246 { "buffered", sizeof("buffered" ) - 1, RTLOGFLAGS_BUFFERED, false },
1247 { "unbuffered", sizeof("unbuffered" ) - 1, RTLOGFLAGS_BUFFERED, true },
1248 { "usecrlf", sizeof("usecrlf" ) - 1, RTLOGFLAGS_USECRLF, true },
1249 { "uself", sizeof("uself" ) - 1, RTLOGFLAGS_USECRLF, false },
1250 { "append", sizeof("append" ) - 1, RTLOGFLAGS_APPEND, false },
1251 { "overwrite", sizeof("overwrite" ) - 1, RTLOGFLAGS_APPEND, true },
1252 { "rel", sizeof("rel" ) - 1, RTLOGFLAGS_REL_TS, false },
1253 { "abs", sizeof("abs" ) - 1, RTLOGFLAGS_REL_TS, true },
1254 { "dec", sizeof("dec" ) - 1, RTLOGFLAGS_DECIMAL_TS, false },
1255 { "hex", sizeof("hex" ) - 1, RTLOGFLAGS_DECIMAL_TS, true },
1256 { "lockcnts", sizeof("lockcnts" ) - 1, RTLOGFLAGS_PREFIX_LOCK_COUNTS, false },
1257 { "cpuid", sizeof("cpuid" ) - 1, RTLOGFLAGS_PREFIX_CPUID, false },
1258 { "pid", sizeof("pid" ) - 1, RTLOGFLAGS_PREFIX_PID, false },
1259 { "flagno", sizeof("flagno" ) - 1, RTLOGFLAGS_PREFIX_FLAG_NO, false },
1260 { "flag", sizeof("flag" ) - 1, RTLOGFLAGS_PREFIX_FLAG, false },
1261 { "groupno", sizeof("groupno" ) - 1, RTLOGFLAGS_PREFIX_GROUP_NO, false },
1262 { "group", sizeof("group" ) - 1, RTLOGFLAGS_PREFIX_GROUP, false },
1263 { "tid", sizeof("tid" ) - 1, RTLOGFLAGS_PREFIX_TID, false },
1264 { "thread", sizeof("thread" ) - 1, RTLOGFLAGS_PREFIX_THREAD, false },
1265 { "timeprog", sizeof("timeprog" ) - 1, RTLOGFLAGS_PREFIX_TIME_PROG, false },
1266 { "time", sizeof("time" ) - 1, RTLOGFLAGS_PREFIX_TIME, false },
1267 { "msprog", sizeof("msprog" ) - 1, RTLOGFLAGS_PREFIX_MS_PROG, false },
1268 { "tsc", sizeof("tsc" ) - 1, RTLOGFLAGS_PREFIX_TSC, false }, /* before ts! */
1269 { "ts", sizeof("ts" ) - 1, RTLOGFLAGS_PREFIX_TS, false },
1270 };
1271
1272 /* check no prefix. */
1273 bool fNo = false;
1274 char ch;
1275 unsigned i;
1276
1277 /* skip blanks. */
1278 while (RT_C_IS_SPACE(*pszVar))
1279 pszVar++;
1280 if (!*pszVar)
1281 return rc;
1282
1283 while ((ch = *pszVar) != '\0')
1284 {
1285 if (ch == 'n' && pszVar[1] == 'o')
1286 {
1287 pszVar += 2;
1288 fNo = !fNo;
1289 }
1290 else if (ch == '+')
1291 {
1292 pszVar++;
1293 fNo = true;
1294 }
1295 else if (ch == '-' || ch == '!' || ch == '~')
1296 {
1297 pszVar++;
1298 fNo = !fNo;
1299 }
1300 else
1301 break;
1302 }
1303
1304 /* instruction. */
1305 for (i = 0; i < ELEMENTS(aDest); i++)
1306 {
1307 if (!strncmp(pszVar, aDest[i].pszInstr, aDest[i].cchInstr))
1308 {
1309 if (fNo == aDest[i].fInverted)
1310 pLogger->fFlags |= aDest[i].fFlag;
1311 else
1312 pLogger->fFlags &= ~aDest[i].fFlag;
1313 pszVar += aDest[i].cchInstr;
1314 break;
1315 }
1316 }
1317
1318 /* unknown instruction? */
1319 if (i >= ELEMENTS(aDest))
1320 {
1321 AssertMsgFailed(("Invalid flags! unknown instruction %.20s\n", pszVar));
1322 pszVar++;
1323 }
1324
1325 /* skip blanks and delimiters. */
1326 while (RT_C_IS_SPACE(*pszVar) || *pszVar == ';')
1327 pszVar++;
1328 } /* while more environment variable value left */
1329
1330 return rc;
1331}
1332
1333
1334/**
1335 * Flushes the specified logger.
1336 *
1337 * @param pLogger The logger instance to flush.
1338 * If NULL the default instance is used. The default instance
1339 * will not be initialized by this call.
1340 */
1341RTDECL(void) RTLogFlush(PRTLOGGER pLogger)
1342{
1343 /*
1344 * Resolve defaults.
1345 */
1346 if (!pLogger)
1347 {
1348#ifdef IN_GC
1349 pLogger = &g_Logger;
1350#else
1351 pLogger = g_pLogger;
1352#endif
1353 if (!pLogger)
1354 return;
1355 }
1356
1357 /*
1358 * Any thing to flush?
1359 */
1360 if (pLogger->offScratch)
1361 {
1362#ifndef IN_GC
1363 /*
1364 * Acquire logger instance sem.
1365 */
1366 int rc = rtlogLock(pLogger);
1367 if (RT_FAILURE(rc))
1368 return;
1369#endif
1370 /*
1371 * Call worker.
1372 */
1373 rtlogFlush(pLogger);
1374
1375#ifndef IN_GC
1376 /*
1377 * Release the semaphore.
1378 */
1379 rtlogUnlock(pLogger);
1380#endif
1381 }
1382}
1383
1384
1385/**
1386 * Gets the default logger instance.
1387 *
1388 * @returns Pointer to default logger instance.
1389 * @returns NULL if no default logger instance available.
1390 */
1391RTDECL(PRTLOGGER) RTLogDefaultInstance(void)
1392{
1393#ifdef IN_GC
1394 return &g_Logger;
1395
1396#else /* !IN_GC */
1397# ifdef IN_RING0
1398 /*
1399 * Check per thread loggers first.
1400 */
1401 if (g_cPerThreadLoggers)
1402 {
1403 const RTNATIVETHREAD Self = RTThreadNativeSelf();
1404 int32_t i = ELEMENTS(g_aPerThreadLoggers);
1405 while (i-- > 0)
1406 if (g_aPerThreadLoggers[i].NativeThread == Self)
1407 return g_aPerThreadLoggers[i].pLogger;
1408 }
1409# endif /* IN_RING0 */
1410
1411 /*
1412 * If no per thread logger, use the default one.
1413 */
1414 if (!g_pLogger)
1415 g_pLogger = RTLogDefaultInit();
1416 return g_pLogger;
1417#endif /* !IN_GC */
1418}
1419
1420
1421#ifndef IN_GC
1422/**
1423 * Sets the default logger instance.
1424 *
1425 * @returns iprt status code.
1426 * @param pLogger The new default logger instance.
1427 */
1428RTDECL(PRTLOGGER) RTLogSetDefaultInstance(PRTLOGGER pLogger)
1429{
1430 return (PRTLOGGER)ASMAtomicXchgPtr((void * volatile *)&g_pLogger, pLogger);
1431}
1432#endif /* !IN_GC */
1433
1434
1435#ifdef IN_RING0
1436/**
1437 * Changes the default logger instance for the current thread.
1438 *
1439 * @returns IPRT status code.
1440 * @param pLogger The logger instance. Pass NULL for deregistration.
1441 * @param uKey Associated key for cleanup purposes. If pLogger is NULL,
1442 * all instances with this key will be deregistered. So in
1443 * order to only deregister the instance associated with the
1444 * current thread use 0.
1445 */
1446RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey)
1447{
1448 int rc;
1449 RTNATIVETHREAD Self = RTThreadNativeSelf();
1450 if (pLogger)
1451 {
1452 int32_t i;
1453 unsigned j;
1454
1455 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
1456
1457 /*
1458 * Iterate the table to see if there is already an entry for this thread.
1459 */
1460 i = ELEMENTS(g_aPerThreadLoggers);
1461 while (i-- > 0)
1462 if (g_aPerThreadLoggers[i].NativeThread == Self)
1463 {
1464 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
1465 g_aPerThreadLoggers[i].pLogger = pLogger;
1466 return VINF_SUCCESS;
1467 }
1468
1469 /*
1470 * Allocate a new table entry.
1471 */
1472 i = ASMAtomicIncS32(&g_cPerThreadLoggers);
1473 if (i > (int32_t)ELEMENTS(g_aPerThreadLoggers))
1474 {
1475 ASMAtomicDecS32(&g_cPerThreadLoggers);
1476 return VERR_BUFFER_OVERFLOW; /* horrible error code! */
1477 }
1478
1479 for (j = 0; j < 10; j++)
1480 {
1481 i = ELEMENTS(g_aPerThreadLoggers);
1482 while (i-- > 0)
1483 {
1484 AssertCompile(sizeof(RTNATIVETHREAD) == sizeof(void*));
1485 if ( g_aPerThreadLoggers[i].NativeThread == NIL_RTNATIVETHREAD
1486 && ASMAtomicCmpXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)Self, (void *)NIL_RTNATIVETHREAD))
1487 {
1488 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
1489 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, pLogger);
1490 return VINF_SUCCESS;
1491 }
1492 }
1493 }
1494
1495 ASMAtomicDecS32(&g_cPerThreadLoggers);
1496 rc = VERR_INTERNAL_ERROR;
1497 }
1498 else
1499 {
1500 /*
1501 * Search the array for the current thread.
1502 */
1503 int32_t i = ELEMENTS(g_aPerThreadLoggers);
1504 while (i-- > 0)
1505 if ( g_aPerThreadLoggers[i].NativeThread == Self
1506 || g_aPerThreadLoggers[i].uKey == uKey)
1507 {
1508 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, NULL);
1509 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, NULL);
1510 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)NIL_RTNATIVETHREAD);
1511 ASMAtomicDecS32(&g_cPerThreadLoggers);
1512 }
1513
1514 rc = VINF_SUCCESS;
1515 }
1516 return rc;
1517}
1518#endif
1519
1520
1521/**
1522 * Gets the default release logger instance.
1523 *
1524 * @returns Pointer to default release logger instance.
1525 * @returns NULL if no default release logger instance available.
1526 */
1527RTDECL(PRTLOGGER) RTLogRelDefaultInstance(void)
1528{
1529#ifdef IN_GC
1530 return &g_RelLogger;
1531#else /* !IN_GC */
1532 return g_pRelLogger;
1533#endif /* !IN_GC */
1534}
1535
1536
1537#ifndef IN_GC
1538/**
1539 * Sets the default logger instance.
1540 *
1541 * @returns iprt status code.
1542 * @param pLogger The new default release logger instance.
1543 */
1544RTDECL(PRTLOGGER) RTLogRelSetDefaultInstance(PRTLOGGER pLogger)
1545{
1546 return (PRTLOGGER)ASMAtomicXchgPtr((void * volatile *)&g_pRelLogger, pLogger);
1547}
1548#endif /* !IN_GC */
1549
1550
1551/**
1552 * Write to a logger instance.
1553 *
1554 * @param pLogger Pointer to logger instance.
1555 * @param pvCallerRet Ignored.
1556 * @param pszFormat Format string.
1557 * @param ... Format arguments.
1558 */
1559RTDECL(void) RTLogLogger(PRTLOGGER pLogger, void *pvCallerRet, const char *pszFormat, ...)
1560{
1561 va_list args;
1562 va_start(args, pszFormat);
1563#if defined(RT_OS_DARWIN) && defined(RT_ARCH_X86) && defined(IN_RING3)
1564 /* manually align the stack before doing the call.
1565 * We boldly assume that there is a stack frame here! */
1566 __asm__ __volatile__("andl $-32, %%esp\t\n" ::: "%esp");
1567 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
1568#else
1569 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
1570#endif
1571 va_end(args);
1572}
1573
1574
1575/**
1576 * Write to a logger instance.
1577 *
1578 * @param pLogger Pointer to logger instance.
1579 * @param pszFormat Format string.
1580 * @param args Format arguments.
1581 */
1582RTDECL(void) RTLogLoggerV(PRTLOGGER pLogger, const char *pszFormat, va_list args)
1583{
1584 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
1585}
1586
1587
1588/**
1589 * Write to a logger instance.
1590 *
1591 * This function will check whether the instance, group and flags makes up a
1592 * logging kind which is currently enabled before writing anything to the log.
1593 *
1594 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.
1595 * @param fFlags The logging flags.
1596 * @param iGroup The group.
1597 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1598 * only for internal usage!
1599 * @param pszFormat Format string.
1600 * @param ... Format arguments.
1601 * @remark This is a worker function of LogIt.
1602 */
1603RTDECL(void) RTLogLoggerEx(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
1604{
1605 va_list args;
1606 va_start(args, pszFormat);
1607 RTLogLoggerExV(pLogger, fFlags, iGroup, pszFormat, args);
1608 va_end(args);
1609}
1610
1611
1612/**
1613 * Write to a logger instance.
1614 *
1615 * This function will check whether the instance, group and flags makes up a
1616 * logging kind which is currently enabled before writing anything to the log.
1617 *
1618 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.
1619 * @param fFlags The logging flags.
1620 * @param iGroup The group.
1621 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1622 * only for internal usage!
1623 * @param pszFormat Format string.
1624 * @param args Format arguments.
1625 */
1626RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
1627{
1628 /*
1629 * A NULL logger means default instance.
1630 */
1631 if (!pLogger)
1632 {
1633 pLogger = RTLogDefaultInstance();
1634 if (!pLogger)
1635 return;
1636 }
1637 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);
1638}
1639
1640
1641/**
1642 * Write to a logger instance, defaulting to the release one.
1643 *
1644 * This function will check whether the instance, group and flags makes up a
1645 * logging kind which is currently enabled before writing anything to the log.
1646 *
1647 * @param pLogger Pointer to logger instance.
1648 * @param fFlags The logging flags.
1649 * @param iGroup The group.
1650 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1651 * only for internal usage!
1652 * @param pszFormat Format string.
1653 * @param ... Format arguments.
1654 * @remark This is a worker function for LogRelIt.
1655 */
1656RTDECL(void) RTLogRelLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
1657{
1658 va_list args;
1659 va_start(args, pszFormat);
1660 RTLogRelLoggerV(pLogger, fFlags, iGroup, pszFormat, args);
1661 va_end(args);
1662}
1663
1664
1665/**
1666 * Write to a logger instance, defaulting to the release one.
1667 *
1668 * This function will check whether the instance, group and flags makes up a
1669 * logging kind which is currently enabled before writing anything to the log.
1670 *
1671 * @param pLogger Pointer to logger instance. If NULL the default release instance is attempted.
1672 * @param fFlags The logging flags.
1673 * @param iGroup The group.
1674 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1675 * only for internal usage!
1676 * @param pszFormat Format string.
1677 * @param args Format arguments.
1678 */
1679RTDECL(void) RTLogRelLoggerV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
1680{
1681 /*
1682 * A NULL logger means default instance.
1683 */
1684 if (!pLogger)
1685 {
1686 pLogger = RTLogRelDefaultInstance();
1687 if (!pLogger)
1688 return;
1689 }
1690 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);
1691}
1692
1693
1694/**
1695 * Worker for the RTLog[Rel]Logger*() functions.
1696 *
1697 * @param pLogger Pointer to logger instance.
1698 * @param fFlags The logging flags.
1699 * @param iGroup The group.
1700 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1701 * only for internal usage!
1702 * @param pszFormat Format string.
1703 * @param args Format arguments.
1704 */
1705static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
1706{
1707 int rc;
1708
1709 /*
1710 * Validate and correct iGroup.
1711 */
1712 if (iGroup != ~0U && iGroup >= pLogger->cGroups)
1713 iGroup = 0;
1714
1715 /*
1716 * If no output, then just skip it.
1717 */
1718 if ( (pLogger->fFlags & RTLOGFLAGS_DISABLED)
1719#ifndef IN_GC
1720 || !pLogger->fDestFlags
1721#endif
1722 || !pszFormat || !*pszFormat)
1723 return;
1724 if ( iGroup != ~0U
1725 && (pLogger->afGroups[iGroup] & (fFlags | RTLOGGRPFLAGS_ENABLED)) != (fFlags | RTLOGGRPFLAGS_ENABLED))
1726 return;
1727
1728 /*
1729 * Acquire logger instance sem.
1730 */
1731 rc = rtlogLock(pLogger);
1732 if (RT_FAILURE(rc))
1733 return;
1734
1735 /*
1736 * Format the message and perhaps flush it.
1737 */
1738 if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF))
1739 {
1740 RTLOGOUTPUTPREFIXEDARGS OutputArgs;
1741 OutputArgs.pLogger = pLogger;
1742 OutputArgs.iGroup = iGroup;
1743 OutputArgs.fFlags = fFlags;
1744 RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);
1745 }
1746 else
1747 RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);
1748 if ( !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)
1749 && pLogger->offScratch)
1750 rtlogFlush(pLogger);
1751
1752 /*
1753 * Release the semaphore.
1754 */
1755 rtlogUnlock(pLogger);
1756}
1757
1758
1759/**
1760 * printf like function for writing to the default log.
1761 *
1762 * @param pszFormat Printf like format string.
1763 * @param ... Optional arguments as specified in pszFormat.
1764 *
1765 * @remark The API doesn't support formatting of floating point numbers at the moment.
1766 */
1767RTDECL(void) RTLogPrintf(const char *pszFormat, ...)
1768{
1769 va_list args;
1770 va_start(args, pszFormat);
1771 RTLogPrintfV(pszFormat, args);
1772 va_end(args);
1773}
1774
1775
1776/**
1777 * vprintf like function for writing to the default log.
1778 *
1779 * @param pszFormat Printf like format string.
1780 * @param args Optional arguments as specified in pszFormat.
1781 *
1782 * @remark The API doesn't support formatting of floating point numbers at the moment.
1783 */
1784RTDECL(void) RTLogPrintfV(const char *pszFormat, va_list args)
1785{
1786 RTLogLoggerV(NULL, pszFormat, args);
1787}
1788
1789
1790/**
1791 * printf like function for writing to the default release log.
1792 *
1793 * @param pszFormat Printf like format string.
1794 * @param ... Optional arguments as specified in pszFormat.
1795 *
1796 * @remark The API doesn't support formatting of floating point numbers at the moment.
1797 */
1798RTDECL(void) RTLogRelPrintf(const char *pszFormat, ...)
1799{
1800 va_list args;
1801 va_start(args, pszFormat);
1802 RTLogRelPrintfV(pszFormat, args);
1803 va_end(args);
1804}
1805
1806
1807/**
1808 * vprintf like function for writing to the default release log.
1809 *
1810 * @param pszFormat Printf like format string.
1811 * @param args Optional arguments as specified in pszFormat.
1812 *
1813 * @remark The API doesn't support formatting of floating point numbers at the moment.
1814 */
1815RTDECL(void) RTLogRelPrintfV(const char *pszFormat, va_list args)
1816{
1817 RTLogRelLoggerV(NULL, 0, ~0U, pszFormat, args);
1818}
1819
1820
1821/**
1822 * Writes the buffer to the given log device without checking for buffered
1823 * data or anything.
1824 * Used by the RTLogFlush() function.
1825 *
1826 * @param pLogger The logger instance to write to. NULL is not allowed!
1827 */
1828static void rtlogFlush(PRTLOGGER pLogger)
1829{
1830#ifndef IN_GC
1831 if (pLogger->fDestFlags & RTLOGDEST_USER)
1832 RTLogWriteUser(pLogger->achScratch, pLogger->offScratch);
1833
1834 if (pLogger->fDestFlags & RTLOGDEST_DEBUGGER)
1835 RTLogWriteDebugger(pLogger->achScratch, pLogger->offScratch);
1836
1837# ifdef IN_RING3
1838 if (pLogger->fDestFlags & RTLOGDEST_FILE)
1839 RTFileWrite(pLogger->File, pLogger->achScratch, pLogger->offScratch, NULL);
1840# endif
1841
1842 if (pLogger->fDestFlags & RTLOGDEST_STDOUT)
1843 RTLogWriteStdOut(pLogger->achScratch, pLogger->offScratch);
1844
1845 if (pLogger->fDestFlags & RTLOGDEST_STDERR)
1846 RTLogWriteStdErr(pLogger->achScratch, pLogger->offScratch);
1847
1848# if (defined(IN_RING0) || defined(IN_GC)) && !defined(LOG_NO_COM)
1849 if (pLogger->fDestFlags & RTLOGDEST_COM)
1850 RTLogWriteCom(pLogger->achScratch, pLogger->offScratch);
1851# endif
1852#endif /* !IN_GC */
1853
1854 if (pLogger->pfnFlush)
1855 pLogger->pfnFlush(pLogger);
1856
1857 /* empty the buffer. */
1858 pLogger->offScratch = 0;
1859}
1860
1861
1862/**
1863 * Callback for RTLogFormatV which writes to the com port.
1864 * See PFNLOGOUTPUT() for details.
1865 */
1866static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars)
1867{
1868 PRTLOGGER pLogger = (PRTLOGGER)pv;
1869 if (cbChars)
1870 {
1871 size_t cbRet = 0;
1872 for (;;)
1873 {
1874#if defined(DEBUG) && defined(IN_RING3)
1875 /* sanity */
1876 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
1877 {
1878 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
1879 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
1880 AssertBreakpoint(); AssertBreakpoint();
1881 }
1882#endif
1883
1884 /* how much */
1885 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
1886 if (cb > cbChars)
1887 cb = cbChars;
1888
1889 /* copy */
1890 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
1891
1892 /* advance */
1893 pLogger->offScratch += cb;
1894 cbRet += cb;
1895 cbChars -= cb;
1896
1897 /* done? */
1898 if (cbChars <= 0)
1899 return cbRet;
1900
1901 pachChars += cb;
1902
1903 /* flush */
1904 rtlogFlush(pLogger);
1905 }
1906
1907 /* won't ever get here! */
1908 }
1909 else
1910 {
1911 /*
1912 * Termination call.
1913 * There's always space for a terminator, and it's not counted.
1914 */
1915 pLogger->achScratch[pLogger->offScratch] = '\0';
1916 return 0;
1917 }
1918}
1919
1920
1921
1922/**
1923 * Callback for RTLogFormatV which writes to the logger instance.
1924 * This version supports prefixes.
1925 *
1926 * See PFNLOGOUTPUT() for details.
1927 */
1928static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars)
1929{
1930 PRTLOGOUTPUTPREFIXEDARGS pArgs = (PRTLOGOUTPUTPREFIXEDARGS)pv;
1931 PRTLOGGER pLogger = pArgs->pLogger;
1932 if (cbChars)
1933 {
1934 size_t cbRet = 0;
1935 for (;;)
1936 {
1937 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
1938 char *psz;
1939 const char *pszNewLine;
1940
1941 /*
1942 * Pending prefix?
1943 */
1944 if (pLogger->fPendingPrefix)
1945 {
1946 pLogger->fPendingPrefix = false;
1947
1948#if defined(DEBUG) && defined(IN_RING3)
1949 /* sanity */
1950 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
1951 {
1952 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
1953 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
1954 AssertBreakpoint(); AssertBreakpoint();
1955 }
1956#endif
1957
1958 /*
1959 * Flush the buffer if there isn't enough room for the maximum prefix config.
1960 * Max is 224, add a couple of extra bytes.
1961 */
1962 if (cb < 224 + 16)
1963 {
1964 rtlogFlush(pLogger);
1965 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
1966 }
1967
1968 /*
1969 * Write the prefixes.
1970 * psz is pointing to the current position.
1971 */
1972 psz = &pLogger->achScratch[pLogger->offScratch];
1973 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TS)
1974 {
1975#if defined(IN_RING3) || defined(IN_GC)
1976 uint64_t u64 = RTTimeNanoTS();
1977#else
1978 uint64_t u64 = ~0;
1979#endif
1980 int iBase = 16;
1981 unsigned int fFlags = RTSTR_F_ZEROPAD;
1982 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
1983 {
1984 iBase = 10;
1985 fFlags = 0;
1986 }
1987 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
1988 {
1989 static volatile uint64_t s_u64LastTs;
1990 uint64_t u64DiffTs = u64 - s_u64LastTs;
1991 s_u64LastTs = u64;
1992 /* We could have been preempted just before reading of s_u64LastTs by
1993 * another thread which wrote s_u64LastTs. In that case the difference
1994 * is negative which we simply ignore. */
1995 u64 = (int64_t)u64DiffTs < 0 ? 0 : u64DiffTs;
1996 }
1997 /* 1E15 nanoseconds = 11 days */
1998 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags); /* +17 */
1999 *psz++ = ' ';
2000 }
2001 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TSC)
2002 {
2003 uint64_t u64 = ASMReadTSC();
2004 int iBase = 16;
2005 unsigned int fFlags = RTSTR_F_ZEROPAD;
2006 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
2007 {
2008 iBase = 10;
2009 fFlags = 0;
2010 }
2011 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
2012 {
2013 static volatile uint64_t s_u64LastTsc;
2014 uint64_t u64DiffTsc = u64 - s_u64LastTsc;
2015 s_u64LastTsc = u64;
2016 /* We could have been preempted just before reading of s_u64LastTsc by
2017 * another thread which wrote s_u64LastTsc. In that case the difference
2018 * is negative which we simply ignore. */
2019 u64 = u64DiffTsc < 0 ? 0 : u64DiffTsc;
2020 }
2021 /* 1E15 ticks at 4GHz = 69 hours */
2022 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags); /* +17 */
2023 *psz++ = ' ';
2024 }
2025 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_MS_PROG)
2026 {
2027#if defined(IN_RING3) || defined(IN_GC)
2028 uint64_t u64 = RTTimeProgramMilliTS();
2029#else
2030 uint64_t u64 = 0;
2031#endif
2032 /* 1E8 milliseconds = 27 hours */
2033 psz += RTStrFormatNumber(psz, u64, 10, 9, 0, RTSTR_F_ZEROPAD);
2034 *psz++ = ' ';
2035 }
2036 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME)
2037 {
2038#ifdef IN_RING3
2039 RTTIMESPEC TimeSpec;
2040 RTTIME Time;
2041 RTTimeExplode(&Time, RTTimeNow(&TimeSpec));
2042 psz += RTStrFormatNumber(psz, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD);
2043 *psz++ = ':';
2044 psz += RTStrFormatNumber(psz, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD);
2045 *psz++ = ':';
2046 psz += RTStrFormatNumber(psz, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD);
2047 *psz++ = '.';
2048 psz += RTStrFormatNumber(psz, Time.u32Nanosecond / 1000000, 10, 3, 0, RTSTR_F_ZEROPAD);
2049 *psz++ = ' '; /* +17 (3+1+3+1+3+1+4+1) */
2050#else
2051 memset(psz, ' ', 13);
2052 psz += 13;
2053#endif
2054 }
2055 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME_PROG)
2056 {
2057#ifdef IN_RING3
2058 uint64_t u64 = RTTimeProgramMilliTS();
2059 psz += RTStrFormatNumber(psz, (uint32_t)(u64 / (60 * 60 * 1000)), 10, 2, 0, RTSTR_F_ZEROPAD);
2060 *psz++ = ':';
2061 uint32_t u32 = (uint32_t)(u64 % (60 * 60 * 1000));
2062 psz += RTStrFormatNumber(psz, u32 / (60 * 1000), 10, 2, 0, RTSTR_F_ZEROPAD);
2063 *psz++ = ':';
2064 u32 %= 60 * 1000;
2065 psz += RTStrFormatNumber(psz, u32 / 1000, 10, 2, 0, RTSTR_F_ZEROPAD);
2066 *psz++ = '.';
2067 psz += RTStrFormatNumber(psz, u32 % 1000, 10, 3, 0, RTSTR_F_ZEROPAD);
2068 *psz++ = ' '; /* +20 (9+1+2+1+2+1+3+1) */
2069#else
2070 memset(psz, ' ', 13);
2071 psz += 13;
2072#endif
2073 }
2074# if 0
2075 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_DATETIME)
2076 {
2077 char szDate[32];
2078 RTTIMESPEC Time;
2079 RTTimeSpecToString(RTTimeNow(&Time), szDate, sizeof(szDate));
2080 size_t cch = strlen(szDate);
2081 memcpy(psz, szDate, cch);
2082 psz += cch;
2083 *psz++ = ' '; /* +32 */
2084 }
2085# endif
2086 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_PID)
2087 {
2088#ifndef IN_GC
2089 RTPROCESS Process = RTProcSelf();
2090#else
2091 RTPROCESS Process = NIL_RTPROCESS;
2092#endif
2093 psz += RTStrFormatNumber(psz, Process, 16, sizeof(RTPROCESS) * 2, 0, RTSTR_F_ZEROPAD);
2094 *psz++ = ' '; /* +9 */
2095 }
2096 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TID)
2097 {
2098#ifndef IN_GC
2099 RTNATIVETHREAD Thread = RTThreadNativeSelf();
2100#else
2101 RTNATIVETHREAD Thread = NIL_RTNATIVETHREAD;
2102#endif
2103 psz += RTStrFormatNumber(psz, Thread, 16, sizeof(RTNATIVETHREAD) * 2, 0, RTSTR_F_ZEROPAD);
2104 *psz++ = ' '; /* +17 */
2105 }
2106 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_THREAD)
2107 {
2108#ifdef IN_RING3
2109 const char *pszName = RTThreadSelfName();
2110#elif defined IN_GC
2111 const char *pszName = "EMT-GC";
2112#else
2113 const char *pszName = "EMT-R0";
2114#endif
2115 size_t cch = 0;
2116 if (pszName)
2117 {
2118 cch = strlen(pszName);
2119 cch = RT_MIN(cch, 16);
2120 memcpy(psz, pszName, cch);
2121 psz += cch;
2122 }
2123 do
2124 *psz++ = ' ';
2125 while (cch++ < 8); /* +17 */
2126 }
2127 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_CPUID)
2128 {
2129#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
2130 const uint8_t idCpu = ASMGetApicId();
2131#else
2132 const RTCPUID idCpu = RTMpCpuId();
2133#endif
2134 psz += RTStrFormatNumber(psz, idCpu, 16, sizeof(idCpu) * 2, 0, RTSTR_F_ZEROPAD);
2135 *psz++ = ' '; /* +17 */
2136 }
2137 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_LOCK_COUNTS)
2138 {
2139#ifdef IN_RING3 /** @todo implement these counters in ring-0 too? */
2140 RTTHREAD Thread = RTThreadSelf();
2141 if (Thread != NIL_RTTHREAD)
2142 {
2143 uint32_t cReadLocks = RTThreadGetReadLockCount(Thread);
2144 uint32_t cWriteLocks = RTThreadGetWriteLockCount(Thread) - g_cLoggerLockCount;
2145 cReadLocks = RT_MIN(0xfff, cReadLocks);
2146 cWriteLocks = RT_MIN(0xfff, cWriteLocks);
2147 psz += RTStrFormatNumber(psz, cReadLocks, 16, 1, 0, RTSTR_F_ZEROPAD);
2148 *psz++ = '/';
2149 psz += RTStrFormatNumber(psz, cWriteLocks, 16, 1, 0, RTSTR_F_ZEROPAD);
2150 }
2151 else
2152#endif
2153 {
2154 *psz++ = '?';
2155 *psz++ = '/';
2156 *psz++ = '?';
2157 }
2158 *psz++ = ' '; /* +8 */
2159 }
2160 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG_NO)
2161 {
2162 psz += RTStrFormatNumber(psz, pArgs->fFlags, 16, 8, 0, RTSTR_F_ZEROPAD);
2163 *psz++ = ' '; /* +9 */
2164 }
2165 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG)
2166 {
2167#ifdef IN_RING3
2168 const char *pszGroup = pArgs->iGroup != ~0U ? pLogger->papszGroups[pArgs->iGroup] : NULL;
2169#else
2170 const char *pszGroup = NULL;
2171#endif
2172 size_t cch = 0;
2173 if (pszGroup)
2174 {
2175 cch = strlen(pszGroup);
2176 cch = RT_MIN(cch, 16);
2177 memcpy(psz, pszGroup, cch);
2178 psz += cch;
2179 }
2180 do
2181 *psz++ = ' ';
2182 while (cch++ < 8); /* +17 */
2183 }
2184 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP_NO)
2185 {
2186 if (pArgs->iGroup != ~0U)
2187 {
2188 psz += RTStrFormatNumber(psz, pArgs->iGroup, 16, 3, 0, RTSTR_F_ZEROPAD);
2189 *psz++ = ' ';
2190 }
2191 else
2192 {
2193 memcpy(psz, "-1 ", sizeof("-1 ") - 1);
2194 psz += sizeof("-1 ") - 1;
2195 } /* +9 */
2196 }
2197 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP)
2198 {
2199 const unsigned fGrp = pLogger->afGroups[pArgs->iGroup != ~0U ? pArgs->iGroup : 0];
2200 const char *pszGroup;
2201 size_t cch;
2202 switch (pArgs->fFlags & fGrp)
2203 {
2204 case 0: pszGroup = "--------"; cch = sizeof("--------") - 1; break;
2205 case RTLOGGRPFLAGS_ENABLED: pszGroup = "enabled" ; cch = sizeof("enabled" ) - 1; break;
2206 case RTLOGGRPFLAGS_LEVEL_1: pszGroup = "level 1" ; cch = sizeof("level 1" ) - 1; break;
2207 case RTLOGGRPFLAGS_LEVEL_2: pszGroup = "level 2" ; cch = sizeof("level 2" ) - 1; break;
2208 case RTLOGGRPFLAGS_LEVEL_3: pszGroup = "level 3" ; cch = sizeof("level 3" ) - 1; break;
2209 case RTLOGGRPFLAGS_LEVEL_4: pszGroup = "level 4" ; cch = sizeof("level 4" ) - 1; break;
2210 case RTLOGGRPFLAGS_LEVEL_5: pszGroup = "level 5" ; cch = sizeof("level 5" ) - 1; break;
2211 case RTLOGGRPFLAGS_LEVEL_6: pszGroup = "level 6" ; cch = sizeof("level 6" ) - 1; break;
2212 case RTLOGGRPFLAGS_FLOW: pszGroup = "flow" ; cch = sizeof("flow" ) - 1; break;
2213
2214 /* personal groups */
2215 case RTLOGGRPFLAGS_LELIK: pszGroup = "lelik" ; cch = sizeof("lelik" ) - 1; break;
2216 case RTLOGGRPFLAGS_MICHAEL: pszGroup = "Michael" ; cch = sizeof("Michael" ) - 1; break;
2217 case RTLOGGRPFLAGS_DMIK: pszGroup = "dmik" ; cch = sizeof("dmik" ) - 1; break;
2218 case RTLOGGRPFLAGS_SUNLOVER: pszGroup = "sunlover"; cch = sizeof("sunlover") - 1; break;
2219 case RTLOGGRPFLAGS_ACHIM: pszGroup = "Achim" ; cch = sizeof("Achim" ) - 1; break;
2220 case RTLOGGRPFLAGS_SANDER: pszGroup = "Sander" ; cch = sizeof("Sander" ) - 1; break;
2221 case RTLOGGRPFLAGS_KLAUS: pszGroup = "Klaus" ; cch = sizeof("Klaus" ) - 1; break;
2222 case RTLOGGRPFLAGS_FRANK: pszGroup = "Frank" ; cch = sizeof("Frank" ) - 1; break;
2223 case RTLOGGRPFLAGS_BIRD: pszGroup = "bird" ; cch = sizeof("bird" ) - 1; break;
2224 case RTLOGGRPFLAGS_NONAME: pszGroup = "noname" ; cch = sizeof("noname" ) - 1; break;
2225 default: pszGroup = "????????"; cch = sizeof("????????") - 1; break;
2226 }
2227 if (pszGroup)
2228 {
2229 cch = RT_MIN(cch, 16);
2230 memcpy(psz, pszGroup, cch);
2231 psz += cch;
2232 }
2233 do
2234 *psz++ = ' ';
2235 while (cch++ < 8); /* +17 */
2236 }
2237
2238 /*
2239 * Done, figure what we've used and advance the buffer and free size.
2240 */
2241 cb = psz - &pLogger->achScratch[pLogger->offScratch];
2242 Assert(cb <= 198);
2243 pLogger->offScratch += cb;
2244 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2245 }
2246 else if (cb <= 0)
2247 {
2248 rtlogFlush(pLogger);
2249 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2250 }
2251
2252#if defined(DEBUG) && defined(IN_RING3)
2253 /* sanity */
2254 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
2255 {
2256 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
2257 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
2258 AssertBreakpoint(); AssertBreakpoint();
2259 }
2260#endif
2261
2262 /* how much */
2263 if (cb > cbChars)
2264 cb = cbChars;
2265
2266 /* have newline? */
2267 pszNewLine = (const char *)memchr(pachChars, '\n', cb);
2268 if (pszNewLine)
2269 {
2270 if (pLogger->fFlags & RTLOGFLAGS_USECRLF)
2271 cb = pszNewLine - pachChars;
2272 else
2273 {
2274 cb = pszNewLine - pachChars + 1;
2275 pLogger->fPendingPrefix = true;
2276 }
2277 }
2278
2279 /* copy */
2280 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
2281
2282 /* advance */
2283 pLogger->offScratch += cb;
2284 cbRet += cb;
2285 cbChars -= cb;
2286
2287 if ( pszNewLine
2288 && (pLogger->fFlags & RTLOGFLAGS_USECRLF)
2289 && pLogger->offScratch + 2 < sizeof(pLogger->achScratch))
2290 {
2291 memcpy(&pLogger->achScratch[pLogger->offScratch], "\r\n", 2);
2292 pLogger->offScratch += 2;
2293 cbRet++;
2294 cbChars--;
2295 cb++;
2296 pLogger->fPendingPrefix = true;
2297 }
2298
2299 /* done? */
2300 if (cbChars <= 0)
2301 return cbRet;
2302 pachChars += cb;
2303 }
2304
2305 /* won't ever get here! */
2306 }
2307 else
2308 {
2309 /*
2310 * Termination call.
2311 * There's always space for a terminator, and it's not counted.
2312 */
2313 pLogger->achScratch[pLogger->offScratch] = '\0';
2314 return 0;
2315 }
2316}
2317
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette