1
/* $Id: log.cpp 4071 2007-08-07 17:07:59Z vboxsync $ */
3
* Runtime VBox - Logger.
7
* Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
13
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14
* distribution. VirtualBox OSE is distributed in the hope that it will
15
* be useful, but WITHOUT ANY WARRANTY of any kind.
19
/*******************************************************************************
21
*******************************************************************************/
24
# include <iprt/alloc.h>
25
# include <iprt/thread.h>
26
# include <iprt/semaphore.h>
29
# include <iprt/process.h>
30
# include <iprt/file.h>
31
# include <iprt/path.h>
33
#include <iprt/time.h>
35
#include <iprt/assert.h>
37
#include <iprt/param.h>
39
#include <iprt/stdarg.h>
40
#include <iprt/string.h>
42
# include <iprt/ctype.h>
43
# include <iprt/alloca.h>
46
# define isspace(ch) ( (ch) == ' ' || (ch) == '\t' )
51
/*******************************************************************************
52
* Defined Constants And Macros *
53
*******************************************************************************/
54
/** Ascii to lower macro. */
55
#define CHLOWER(ch) (((unsigned char)(ch) < (unsigned char)'A') || ((unsigned char)(ch) > (unsigned char)'Z') ? (ch) : (ch) + ('a' - 'A'))
58
/*******************************************************************************
59
* Structures and Typedefs *
60
*******************************************************************************/
62
* Arguments passed to the output function.
64
typedef struct RTLOGOUTPUTPREFIXEDARGS
66
/** The logger instance. */
68
/** The flags. (used for prefixing.) */
70
/** The group. (used for prefixing.) */
72
} RTLOGOUTPUTPREFIXEDARGS, *PRTLOGOUTPUTPREFIXEDARGS;
75
/*******************************************************************************
76
* Internal Functions *
77
*******************************************************************************/
79
static unsigned rtlogGroupFlags(const char *psz);
81
static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args);
82
static void rtlogFlush(PRTLOGGER pLogger);
83
static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars);
84
static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars);
87
/*******************************************************************************
89
*******************************************************************************/
91
/** Default logger instance. */
92
extern "C" DECLIMPORT(RTLOGGERGC) g_Logger;
93
/** Default relese logger instance. */
94
extern "C" DECLIMPORT(RTLOGGERGC) g_RelLogger;
96
/** Default logger instance. */
97
static PRTLOGGER g_pLogger;
98
/** Default release logger instance. */
99
static PRTLOGGER g_pRelLogger;
102
/** Number of per-thread loggers. */
103
static int32_t volatile g_cPerThreadLoggers;
104
/** Per-thread loggers.
105
* This is just a quick TLS hack suitable for debug logging only.
106
* If we run out of entries, just unload and reload the driver. */
107
static struct RTLOGGERPERTHREAD
110
RTNATIVETHREAD volatile NativeThread;
111
/** The (process / session) key. */
112
uintptr_t volatile uKey;
113
/** The logger instance.*/
114
PRTLOGGER volatile pLogger;
115
} g_aPerThreadLoggers[8] =
116
{ { NIL_RTNATIVETHREAD, 0, 0},
117
{ NIL_RTNATIVETHREAD, 0, 0},
118
{ NIL_RTNATIVETHREAD, 0, 0},
119
{ NIL_RTNATIVETHREAD, 0, 0},
120
{ NIL_RTNATIVETHREAD, 0, 0},
121
{ NIL_RTNATIVETHREAD, 0, 0},
122
{ NIL_RTNATIVETHREAD, 0, 0},
123
{ NIL_RTNATIVETHREAD, 0, 0}
125
#endif /* IN_RING0 */
129
* Locks the logger instance.
131
* @returns See RTSemFastMutexRequest().
132
* @param pLogger The logger instance.
134
DECLINLINE(int) rtlogLock(PRTLOGGER pLogger)
137
if (pLogger->MutexSem != NIL_RTSEMFASTMUTEX)
139
int rc = RTSemFastMutexRequest(pLogger->MutexSem);
140
AssertRCReturn(rc, rc);
148
* Unlocks the logger instance.
149
* @param pLogger The logger instance.
151
DECLINLINE(void) rtlogUnlock(PRTLOGGER pLogger)
154
if (pLogger->MutexSem != NIL_RTSEMFASTMUTEX)
155
RTSemFastMutexRelease(pLogger->MutexSem);
163
* Create a logger instance, comprehensive version.
165
* @returns iprt status code.
167
* @param ppLogger Where to store the logger instance.
168
* @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
169
* @param pszGroupSettings The initial group settings.
170
* @param pszEnvVarBase Base name for the environment variables for this instance.
171
* @param cGroups Number of groups in the array.
172
* @param papszGroups Pointer to array of groups. This must stick around for the life of the
174
* @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
175
* @param pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.
176
* @param cchErrorMsg The size of the error message buffer.
177
* @param pszFilenameFmt Log filename format string. Standard RTStrFormat().
178
* @param ... Format arguments.
180
RTDECL(int) RTLogCreateExV(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,
181
const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
182
RTUINT fDestFlags, char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, va_list args)
187
if ( (cGroups && !papszGroups)
188
|| !VALID_PTR(ppLogger)
191
AssertMsgFailed(("Invalid parameters!\n"));
192
return VERR_INVALID_PARAMETER;
198
* Allocate a logger instance.
201
size_t cb = RT_OFFSETOF(RTLOGGER, afGroups[cGroups + 1]) + RTPATH_MAX;
202
PRTLOGGER pLogger = (PRTLOGGER)RTMemAllocZ(cb);
205
pLogger->u32Magic = RTLOGGER_MAGIC;
206
pLogger->papszGroups = papszGroups;
207
pLogger->cMaxGroups = cGroups;
208
pLogger->cGroups = cGroups;
209
pLogger->pszFilename = (char *)&pLogger->afGroups[cGroups + 1];
210
pLogger->File = NIL_RTFILE;
211
pLogger->fFlags = fFlags;
212
pLogger->fDestFlags = fDestFlags;
213
pLogger->fPendingPrefix = true;
214
if (pszGroupSettings)
215
RTLogGroupSettings(pLogger, pszGroupSettings);
220
uint8_t *pu8Code = (uint8_t *)RTMemExecAlloc(64);
223
pLogger->pfnLogger = *(PFNRTLOGGER*)&pu8Code;
225
/* this wrapper will not be used on AMD64, we will be requiring C99 compilers there. */
228
*pu8Code++ = 0x68; /* push imm32 */
229
*(void **)pu8Code = pLogger;
230
pu8Code += sizeof(void *);
231
*pu8Code++ = 0xe8; /* call rel32 */
232
*(uint32_t *)pu8Code = (uintptr_t)RTLogLogger - ((uintptr_t)pu8Code + sizeof(uint32_t));
233
pu8Code += sizeof(uint32_t);
234
*pu8Code++ = 0x8d; /* lea esp, [esp + 4] */
238
*pu8Code++ = 0xc3; /* ret near */
240
AssertMsg((uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger <= 64,
241
("Wrapper assembly is too big! %d bytes\n", (uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger));
244
#ifdef IN_RING3 /* files and env.vars. are only accessible when in R3 at the present time. */
246
* Format the filename.
250
RTStrPrintfV(pLogger->pszFilename, RTPATH_MAX, pszFilenameFmt, args);
251
pLogger->fDestFlags |= RTLOGDEST_FILE;
255
* Parse the environment variables.
259
/* make temp copy of environment variable base. */
260
size_t cchEnvVarBase = strlen(pszEnvVarBase);
261
char *pszEnvVar = (char *)alloca(cchEnvVarBase + 16);
262
memcpy(pszEnvVar, pszEnvVarBase, cchEnvVarBase);
267
strcpy(pszEnvVar + cchEnvVarBase, "_DEST");
268
const char *pszVar = getenv(pszEnvVar);
274
while (isspace(*pszVar) || *pszVar == '\n' || *pszVar == '\r')
279
/* parse instruction. */
282
const char *pszInstr;
286
{ "file", RTLOGDEST_FILE }, /* Must be 1st! */
287
{ "dir", RTLOGDEST_FILE }, /* Must be 2nd! */
288
{ "stdout", RTLOGDEST_STDOUT },
289
{ "stderr", RTLOGDEST_STDERR },
290
{ "debugger", RTLOGDEST_DEBUGGER },
291
{ "com", RTLOGDEST_COM },
292
{ "user", RTLOGDEST_USER },
295
/* check no prefix. */
297
if (pszVar[0] == 'n' && pszVar[1] == 'o')
305
for (i = 0; i < ELEMENTS(aDest); i++)
307
size_t cchInstr = strlen(aDest[i].pszInstr);
308
if (!strncmp(pszVar, aDest[i].pszInstr, cchInstr))
311
pLogger->fDestFlags |= aDest[i].fFlag;
313
pLogger->fDestFlags &= ~aDest[i].fFlag;
316
/* check for value. */
317
while (isspace(*pszVar) || *pszVar == '\n' || *pszVar == '\r')
319
if (*pszVar == '=' || *pszVar == ':')
322
const char *pszEnd = strchr(pszVar, ';');
324
pszEnd = strchr(pszVar, '\0');
327
size_t cch = pszEnd - pszVar;
328
if (i == 0 /* file */ && !fNo)
330
memcpy(pLogger->pszFilename, pszVar, cch);
331
pLogger->pszFilename[cch] = '\0';
334
else if (i == 1 /* dir */ && !fNo)
336
char szTmp[RTPATH_MAX];
337
const char *pszFile = RTPathFilename(pLogger->pszFilename);
339
strcpy(szTmp, pszFile);
341
pszFile = ""; /* you've screwed up, sir. */
343
memcpy(pLogger->pszFilename, pszVar, cch);
344
pLogger->pszFilename[cch] = '\0';
345
RTPathStripTrailingSlash(pLogger->pszFilename);
347
cch = strlen(pLogger->pszFilename);
348
pLogger->pszFilename[cch++] = '/';
349
strcpy(&pLogger->pszFilename[cch], szTmp);
352
AssertMsgFailed(("Invalid %s_DEST! %s%s doesn't take a value!\n", pszEnvVarBase, fNo ? "no" : "", aDest[i].pszInstr));
353
pszVar = pszEnd + (*pszEnd != '\0');
358
/* unknown instruction? */
359
if (i >= ELEMENTS(aDest))
361
AssertMsgFailed(("Invalid %s_DEST! unknown instruction %.20s\n", pszEnvVarBase, pszVar));
365
/* skip blanks and delimiters. */
366
while (isspace(*pszVar) || *pszVar == '\n' || *pszVar == '\r' || *pszVar == ';')
368
} /* while more environment variable value left */
374
strcpy(pszEnvVar + cchEnvVarBase, "_FLAGS");
375
pszVar = getenv(pszEnvVar);
377
RTLogFlags(pLogger, pszVar);
380
* The group settings.
382
pszEnvVar[cchEnvVarBase] = '\0';
383
pszVar = getenv(pszEnvVar);
385
RTLogGroupSettings(pLogger, pszVar);
387
#endif /* IN_RING3 */
390
* Open the destination(s).
394
if (pLogger->fDestFlags & RTLOGDEST_FILE)
396
rc = RTFileOpen(&pLogger->File, pLogger->pszFilename,
397
RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE);
398
if (RT_FAILURE(rc) && pszErrorMsg)
399
RTStrPrintf(pszErrorMsg, cchErrorMsg, "could not open file '%s'", pLogger->pszFilename);
401
#endif /* IN_RING3 */
408
rc = RTSemFastMutexCreate(&pLogger->MutexSem);
414
else if (pszErrorMsg)
415
RTStrPrintf(pszErrorMsg, cchErrorMsg, "failed to create sempahore");
418
RTFileClose(pLogger->File);
420
RTMemExecFree(*(void **)&pLogger->pfnLogger);
433
* Create a logger instance.
435
* @returns iprt status code.
437
* @param ppLogger Where to store the logger instance.
438
* @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
439
* @param pszGroupSettings The initial group settings.
440
* @param pszEnvVarBase Base name for the environment variables for this instance.
441
* @param cGroups Number of groups in the array.
442
* @param papszGroups Pointer to array of groups. This must stick around for the life of the
444
* @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
445
* @param pszFilenameFmt Log filename format string. Standard RTStrFormat().
446
* @param ... Format arguments.
448
RTDECL(int) RTLogCreate(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,
449
const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
450
RTUINT fDestFlags, const char *pszFilenameFmt, ...)
455
va_start(args, pszFilenameFmt);
456
rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, NULL, 0, pszFilenameFmt, args);
462
* Create a logger instance.
464
* @returns iprt status code.
466
* @param ppLogger Where to store the logger instance.
467
* @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
468
* @param pszGroupSettings The initial group settings.
469
* @param pszEnvVarBase Base name for the environment variables for this instance.
470
* @param cGroups Number of groups in the array.
471
* @param papszGroups Pointer to array of groups. This must stick around for the life of the
473
* @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
474
* @param pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.
475
* @param cchErrorMsg The size of the error message buffer.
476
* @param pszFilenameFmt Log filename format string. Standard RTStrFormat().
477
* @param ... Format arguments.
479
RTDECL(int) RTLogCreateEx(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,
480
const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
481
RTUINT fDestFlags, char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, ...)
486
va_start(args, pszFilenameFmt);
487
rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, pszErrorMsg, cchErrorMsg, pszFilenameFmt, args);
493
* Destroys a logger instance.
495
* The instance is flushed and all output destinations closed (where applicable).
497
* @returns iprt status code.
498
* @param pLogger The logger instance which close destroyed.
500
RTDECL(int) RTLogDestroy(PRTLOGGER pLogger)
505
AssertReturn(VALID_PTR(pLogger), VERR_INVALID_POINTER);
506
AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
509
* Acquire logger instance sem and disable all logging. (paranoia)
511
int rc = rtlogLock(pLogger);
515
pLogger->fFlags |= RTLOGFLAGS_DISABLED;
516
RTUINT iGroup = pLogger->cGroups;
518
pLogger->afGroups[iGroup] = 0;
526
* Close output stuffs.
529
if (pLogger->File != NIL_RTFILE)
531
int rc2 = RTFileClose(pLogger->File);
533
if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
535
pLogger->File = NIL_RTFILE;
540
* Free the mutex and the instance memory.
542
RTSEMFASTMUTEX MutexSem = pLogger->MutexSem;
543
pLogger->MutexSem = NIL_RTSEMFASTMUTEX;
544
if (MutexSem != NIL_RTSEMFASTMUTEX)
546
int rc2 = RTSemFastMutexDestroy(MutexSem);
548
if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
559
* Create a logger instance clone for GC usage.
561
* @returns iprt status code.
563
* @param pLogger The logger instance to be cloned.
564
* @param pLoggerGC Where to create the GC logger instance.
565
* @param cbLoggerGC Amount of memory allocated to for the GC logger instance clone.
566
* @param pfnLoggerGCPtr Pointer to logger wrapper function for this instance (GC Ptr).
567
* @param pfnFlushGCPtr Pointer to flush function (GC Ptr).
568
* @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
570
RTDECL(int) RTLogCloneGC(PRTLOGGER pLogger, PRTLOGGERGC pLoggerGC, size_t cbLoggerGC,
571
RTGCPTR pfnLoggerGCPtr, RTGCPTR pfnFlushGCPtr, RTUINT fFlags)
580
AssertMsgFailed(("Invalid parameters!\n"));
581
return VERR_INVALID_PARAMETER;
583
if (cbLoggerGC < sizeof(*pLoggerGC))
585
AssertMsgFailed(("%d min=%d\n", cbLoggerGC, sizeof(*pLoggerGC)));
586
return VERR_INVALID_PARAMETER;
590
* Initialize GC instance.
592
pLoggerGC->offScratch = 0;
593
pLoggerGC->fPendingPrefix = false;
594
pLoggerGC->pfnLogger = pfnLoggerGCPtr;
595
pLoggerGC->pfnFlush = pfnFlushGCPtr;
596
pLoggerGC->u32Magic = RTLOGGERGC_MAGIC;
597
pLoggerGC->fFlags = fFlags | RTLOGFLAGS_DISABLED;
598
pLoggerGC->cGroups = 1;
599
pLoggerGC->afGroups[0] = 0;
606
pLogger = RTLogDefaultInstance();
612
* Check if there's enough space for the groups.
614
if (cbLoggerGC < (size_t)RT_OFFSETOF(RTLOGGERGC, afGroups[pLogger->cGroups]))
616
AssertMsgFailed(("%d req=%d cGroups=%d\n", cbLoggerGC, RT_OFFSETOF(RTLOGGERGC, afGroups[pLogger->cGroups]), pLogger->cGroups));
617
return VERR_INVALID_PARAMETER;
619
memcpy(&pLoggerGC->afGroups[0], &pLogger->afGroups[0], pLogger->cGroups * sizeof(pLoggerGC->afGroups[0]));
620
pLoggerGC->cGroups = pLogger->cGroups;
623
* Copy bits from the HC instance.
625
pLoggerGC->fPendingPrefix = pLogger->fPendingPrefix;
626
pLoggerGC->fFlags |= pLogger->fFlags;
629
* Check if we can remove the disabled flag.
631
if ( pLogger->fDestFlags
632
&& !((pLogger->fFlags | fFlags) & RTLOGFLAGS_DISABLED))
633
pLoggerGC->fFlags &= ~RTLOGFLAGS_DISABLED;
640
* Flushes a GC logger instance to a HC logger.
643
* @returns iprt status code.
644
* @param pLogger The HC logger instance to flush pLoggerGC to.
645
* If NULL the default logger is used.
646
* @param pLoggerGC The GC logger instance to flush.
648
RTDECL(void) RTLogFlushGC(PRTLOGGER pLogger, PRTLOGGERGC pLoggerGC)
655
pLogger = RTLogDefaultInstance();
658
pLoggerGC->offScratch = 0;
664
* Any thing to flush?
666
if ( pLogger->offScratch
667
|| pLoggerGC->offScratch)
670
* Acquire logger instance sem.
672
int rc = rtlogLock(pLogger);
677
* Write whatever the GC instance contains to the HC one, and then
678
* flush the HC instance.
680
if (pLoggerGC->offScratch)
682
rtLogOutput(pLogger, pLoggerGC->achScratch, pLoggerGC->offScratch);
683
rtLogOutput(pLogger, NULL, 0);
684
pLoggerGC->offScratch = 0;
688
* Release the semaphore.
690
rtlogUnlock(pLogger);
697
* Create a logger instance for singled threaded ring-0 usage.
699
* @returns iprt status code.
701
* @param pLogger Where to create the logger instance.
702
* @param cbLogger The amount of memory available for the logger instance.
703
* @param pfnLogger Pointer to logger wrapper function for the clone.
704
* @param pfnFlush Pointer to flush function for the clone.
705
* @param fFlags Logger instance flags for the clone, a combination of the RTLOGFLAGS_* values.
706
* @param fDestFlags The destination flags.
708
RTDECL(int) RTLogCreateForR0(PRTLOGGER pLogger, size_t cbLogger, PFNRTLOGGER pfnLogger, PFNRTLOGFLUSH pfnFlush, RTUINT fFlags, RTUINT fDestFlags)
713
AssertPtrReturn(pLogger, VERR_INVALID_PARAMETER);
714
AssertReturn(cbLogger >= sizeof(*pLogger), VERR_INVALID_PARAMETER);
715
AssertReturn(pfnLogger, VERR_INVALID_PARAMETER);
716
AssertReturn(pfnFlush, VERR_INVALID_PARAMETER);
719
* Initialize the ring-0 instance.
721
pLogger->offScratch = 0;
722
pLogger->fPendingPrefix = false;
723
pLogger->pfnLogger = pfnLogger;
724
pLogger->pfnFlush = pfnFlush;
725
pLogger->MutexSem = NIL_RTSEMFASTMUTEX; /* Not serialized. */
726
pLogger->u32Magic = RTLOGGER_MAGIC;
727
pLogger->fFlags = fFlags;
728
pLogger->fDestFlags = fDestFlags & ~RTLOGDEST_FILE;
729
pLogger->File = NIL_RTFILE;
730
pLogger->pszFilename = NULL;
731
pLogger->papszGroups = NULL;
732
pLogger->cMaxGroups = (cbLogger - RT_OFFSETOF(RTLOGGER, afGroups[0])) / sizeof(pLogger->afGroups[0]);
733
pLogger->cGroups = 1;
734
pLogger->afGroups[0] = 0;
737
#endif /* IN_RING3 */
741
* Copies the group settings and flags from logger instance to another.
743
* @returns IPRT status code.
744
* @param pDstLogger The destination logger instance.
745
* @param pSrcLogger The source logger instance. If NULL the default one is used.
746
* @param fFlagsOr OR mask for the flags.
747
* @param fFlagsAnd AND mask for the flags.
749
RTDECL(int) RTLogCopyGroupsAndFlags(PRTLOGGER pDstLogger, PCRTLOGGER pSrcLogger, unsigned fFlagsOr, unsigned fFlagsAnd)
754
AssertPtrReturn(pDstLogger, VERR_INVALID_PARAMETER);
755
AssertPtrNullReturn(pSrcLogger, VERR_INVALID_PARAMETER);
762
pSrcLogger = RTLogDefaultInstance();
765
pDstLogger->fFlags |= RTLOGFLAGS_DISABLED;
766
pDstLogger->cGroups = 1;
767
pDstLogger->afGroups[0] = 0;
773
* Copy flags and group settings.
775
pDstLogger->fFlags = (pSrcLogger->fFlags & fFlagsAnd) | fFlagsOr;
777
int rc = VINF_SUCCESS;
778
unsigned cGroups = pSrcLogger->cGroups;
779
if (cGroups < pDstLogger->cMaxGroups)
781
AssertMsgFailed(("cMaxGroups=%zd cGroups=%zd (min size %d)\n", pDstLogger->cMaxGroups,
782
pSrcLogger->cGroups, RT_OFFSETOF(RTLOGGER, afGroups[pSrcLogger->cGroups])));
783
rc = VERR_INVALID_PARAMETER;
784
cGroups = pDstLogger->cMaxGroups;
786
memcpy(&pDstLogger->afGroups[0], &pSrcLogger->afGroups[0], cGroups * sizeof(pDstLogger->afGroups[0]));
787
pDstLogger->cGroups = cGroups;
794
* Flushes the buffer in one logger instance onto another logger.
796
* @returns iprt status code.
798
* @param pSrcLogger The logger instance to flush.
799
* @param pDstLogger The logger instance to flush onto.
800
* If NULL the default logger will be used.
802
RTDECL(void) RTLogFlushToLogger(PRTLOGGER pSrcLogger, PRTLOGGER pDstLogger)
809
pDstLogger = RTLogDefaultInstance();
812
/* flushing to "/dev/null". */
813
if (pSrcLogger->offScratch)
815
int rc = rtlogLock(pSrcLogger);
818
pSrcLogger->offScratch = 0;
819
rtlogLock(pSrcLogger);
827
* Any thing to flush?
829
if ( pSrcLogger->offScratch
830
|| pDstLogger->offScratch)
833
* Acquire logger semaphores.
835
int rc = rtlogLock(pDstLogger);
838
rc = rtlogLock(pSrcLogger);
842
* Write whatever the GC instance contains to the HC one, and then
843
* flush the HC instance.
845
if (pSrcLogger->offScratch)
847
rtLogOutput(pDstLogger, pSrcLogger->achScratch, pSrcLogger->offScratch);
848
rtLogOutput(pDstLogger, NULL, 0);
849
pSrcLogger->offScratch = 0;
853
* Release the semaphores.
855
rtlogUnlock(pSrcLogger);
857
rtlogUnlock(pDstLogger);
863
* Matches a group name with a pattern mask in an case insensitive manner (ASCII).
865
* @returns true if matching and *ppachMask set to the end of the pattern.
866
* @returns false if no match.
867
* @param pszGrp The group name.
868
* @param ppachMask Pointer to the pointer to the mask. Only wildcard supported is '*'.
869
* @param cchMask The length of the mask, including modifiers. The modifiers is why
870
* we update *ppachMask on match.
872
static bool rtlogIsGroupMatching(const char *pszGrp, const char **ppachMask, unsigned cchMask)
874
if (!pszGrp || !*pszGrp)
876
const char *pachMask = *ppachMask;
879
if (CHLOWER(*pszGrp) != CHLOWER(*pachMask))
882
* Check for wildcard and do a minimal match if found.
884
if (*pachMask != '*')
889
while (--cchMask && *pachMask == '*');
891
/* is there more to match? */
895
break; /* we're good */
897
/* do extremely minimal matching (fixme) */
898
pszGrp = strchr(pszGrp, *pachMask);
907
/* trailing wildcard is ok. */
912
} while (cchMask && *pachMask == '*');
916
break; /* we're good */
926
*ppachMask = pachMask;
932
* Updates the group settings for the logger instance using the specified
933
* specification string.
935
* @returns iprt status code.
936
* Failures can safely be ignored.
937
* @param pLogger Logger instance.
938
* @param pszVar Value to parse.
940
RTDECL(int) RTLogGroupSettings(PRTLOGGER pLogger, const char *pszVar)
947
pLogger = RTLogDefaultInstance();
953
* Iterate the string.
958
* Skip prefixes (blanks, ;, + and -).
960
bool fEnabled = true;
962
while ((ch = *pszVar) == '+' || ch == '-' || ch == ' ' || ch == '\t' || ch == '\n' || ch == ';')
964
if (ch == '+' || ch == '-' || ';')
965
fEnabled = ch != '-';
974
const char *pszStart = pszVar;
975
while ((ch = *pszVar) != '\0' && ch != '+' && ch != '-' && ch != ' ' && ch != '\t')
979
* Find the group (ascii case insensitive search).
980
* Special group 'all'.
983
size_t cch = pszVar - pszStart;
985
&& (pszStart[0] == 'a' || pszStart[0] == 'A')
986
&& (pszStart[1] == 'l' || pszStart[1] == 'L')
987
&& (pszStart[2] == 'l' || pszStart[2] == 'L')
988
&& (cch == 3 || pszStart[3] == '.' || pszStart[3] == '='))
993
unsigned fFlags = cch == 3
994
? RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1
995
: rtlogGroupFlags(&pszStart[3]);
996
for (i = 0; i < pLogger->cGroups; i++)
999
pLogger->afGroups[i] |= fFlags;
1001
pLogger->afGroups[i] &= ~fFlags;
1007
* Specific group(s).
1010
for (i = 0, fFound = false; i < pLogger->cGroups && !fFound; i++)
1012
const char *psz2 = (const char*)pszStart;
1013
if (rtlogIsGroupMatching(pLogger->papszGroups[i], &psz2, cch))
1015
unsigned fFlags = RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1;
1016
if (*psz2 == '.' || *psz2 == '=')
1017
fFlags = rtlogGroupFlags(psz2);
1019
pLogger->afGroups[i] |= fFlags;
1021
pLogger->afGroups[i] &= ~fFlags;
1023
} /* for each group */
1026
} /* parse specification */
1028
return VINF_SUCCESS;
1033
* Interprets the group flags suffix.
1035
* @returns Flags specified. (0 is possible!)
1036
* @param psz Start of Suffix. (Either dot or equal sign.)
1038
static unsigned rtlogGroupFlags(const char *psz)
1040
unsigned fFlags = 0;
1049
const char *pszFlag; /* lowercase!! */
1053
{ "eo", RTLOGGRPFLAGS_ENABLED },
1054
{ "enabledonly",RTLOGGRPFLAGS_ENABLED },
1055
{ "e", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },
1056
{ "enabled", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },
1057
{ "l1", RTLOGGRPFLAGS_LEVEL_1 },
1058
{ "level1", RTLOGGRPFLAGS_LEVEL_1 },
1059
{ "l", RTLOGGRPFLAGS_LEVEL_2 },
1060
{ "l2", RTLOGGRPFLAGS_LEVEL_2 },
1061
{ "level2", RTLOGGRPFLAGS_LEVEL_2 },
1062
{ "l3", RTLOGGRPFLAGS_LEVEL_3 },
1063
{ "level3", RTLOGGRPFLAGS_LEVEL_3 },
1064
{ "l4", RTLOGGRPFLAGS_LEVEL_4 },
1065
{ "level4", RTLOGGRPFLAGS_LEVEL_4 },
1066
{ "l5", RTLOGGRPFLAGS_LEVEL_5 },
1067
{ "level5", RTLOGGRPFLAGS_LEVEL_5 },
1068
{ "l6", RTLOGGRPFLAGS_LEVEL_6 },
1069
{ "level6", RTLOGGRPFLAGS_LEVEL_6 },
1070
{ "f", RTLOGGRPFLAGS_FLOW },
1071
{ "flow", RTLOGGRPFLAGS_FLOW },
1073
{ "lelik", RTLOGGRPFLAGS_LELIK },
1074
{ "michael", RTLOGGRPFLAGS_MICHAEL },
1075
{ "dmik", RTLOGGRPFLAGS_DMIK },
1076
{ "sunlover", RTLOGGRPFLAGS_SUNLOVER },
1077
{ "achim", RTLOGGRPFLAGS_ACHIM },
1078
{ "achimha", RTLOGGRPFLAGS_ACHIM },
1079
{ "s", RTLOGGRPFLAGS_SANDER },
1080
{ "sander", RTLOGGRPFLAGS_SANDER },
1081
{ "sandervl", RTLOGGRPFLAGS_SANDER },
1082
{ "klaus", RTLOGGRPFLAGS_KLAUS },
1083
{ "frank", RTLOGGRPFLAGS_FRANK },
1084
{ "b", RTLOGGRPFLAGS_BIRD },
1085
{ "bird", RTLOGGRPFLAGS_BIRD },
1086
{ "n", RTLOGGRPFLAGS_NONAME },
1087
{ "noname", RTLOGGRPFLAGS_NONAME }
1091
bool fFound = false;
1092
for (i = 0; i < ELEMENTS(aFlags) && !fFound; i++)
1094
const char *psz1 = aFlags[i].pszFlag;
1095
const char *psz2 = psz;
1096
while (*psz1 == CHLOWER(*psz2))
1102
if ( (*psz2 >= 'a' && *psz2 <= 'z')
1103
|| (*psz2 >= 'A' && *psz2 <= 'Z')
1104
|| (*psz2 >= '0' && *psz2 <= '9') )
1106
fFlags |= aFlags[i].fFlag;
1112
} /* for each flags */
1122
fFlags = ~RTStrToInt32(psz + 1);
1124
fFlags = RTStrToInt32(psz);
1134
* Updates the flags for the logger instance using the specified
1135
* specification string.
1137
* @returns iprt status code.
1138
* Failures can safely be ignored.
1139
* @param pLogger Logger instance (NULL for default logger).
1140
* @param pszVar Value to parse.
1142
RTDECL(int) RTLogFlags(PRTLOGGER pLogger, const char *pszVar)
1144
int rc = VINF_SUCCESS;
1151
pLogger = RTLogDefaultInstance();
1153
return VINF_SUCCESS;
1157
* Iterate the string.
1162
while (isspace(*pszVar) || *pszVar == '\n' || *pszVar == '\r')
1167
/* parse instruction. */
1170
const char *pszInstr;
1176
{ "disabled", sizeof("disabled" ) - 1, RTLOGFLAGS_DISABLED, false },
1177
{ "enabled", sizeof("enabled" ) - 1, RTLOGFLAGS_DISABLED, true },
1178
{ "buffered", sizeof("buffered" ) - 1, RTLOGFLAGS_BUFFERED, false },
1179
{ "unbuffered", sizeof("unbuffered" ) - 1, RTLOGFLAGS_BUFFERED, true },
1180
{ "usecrlf", sizeof("usecrlf" ) - 1, RTLOGFLAGS_USECRLF, true },
1181
{ "uself", sizeof("uself" ) - 1, RTLOGFLAGS_USECRLF, false },
1182
{ "rel", sizeof("rel" ) - 1, RTLOGFLAGS_REL_TS, false },
1183
{ "abs", sizeof("abs" ) - 1, RTLOGFLAGS_REL_TS, true },
1184
{ "dec", sizeof("dec" ) - 1, RTLOGFLAGS_DECIMAL_TS, false },
1185
{ "hex", sizeof("hex" ) - 1, RTLOGFLAGS_DECIMAL_TS, true },
1186
{ "flagno", sizeof("flagno" ) - 1, RTLOGFLAGS_PREFIX_FLAG_NO, false },
1187
{ "flag", sizeof("flag" ) - 1, RTLOGFLAGS_PREFIX_FLAG, false },
1188
{ "groupno", sizeof("groupno" ) - 1, RTLOGFLAGS_PREFIX_GROUP_NO, false },
1189
{ "group", sizeof("group" ) - 1, RTLOGFLAGS_PREFIX_GROUP, false },
1190
{ "tid", sizeof("tid" ) - 1, RTLOGFLAGS_PREFIX_TID, false },
1191
{ "thread", sizeof("thread" ) - 1, RTLOGFLAGS_PREFIX_THREAD, false },
1192
{ "timeprog", sizeof("timeprog" ) - 1, RTLOGFLAGS_PREFIX_TIME_PROG, false },
1193
{ "time", sizeof("time" ) - 1, RTLOGFLAGS_PREFIX_TIME, false },
1194
{ "msprog", sizeof("msprog" ) - 1, RTLOGFLAGS_PREFIX_MS_PROG, false },
1195
{ "tsc", sizeof("tsc" ) - 1, RTLOGFLAGS_PREFIX_TSC, false }, /* before ts! */
1196
{ "ts", sizeof("ts" ) - 1, RTLOGFLAGS_PREFIX_TS, false },
1199
/* check no prefix. */
1202
while ((ch = *pszVar) != '\0')
1204
if (ch == 'n' && pszVar[1] == 'o')
1214
else if (ch == '-' || ch == '!' || ch == '~')
1225
for (i = 0; i < ELEMENTS(aDest); i++)
1227
if (!strncmp(pszVar, aDest[i].pszInstr, aDest[i].cchInstr))
1229
if (fNo == aDest[i].fInverted)
1230
pLogger->fFlags |= aDest[i].fFlag;
1232
pLogger->fFlags &= ~aDest[i].fFlag;
1233
pszVar += aDest[i].cchInstr;
1238
/* unknown instruction? */
1239
if (i >= ELEMENTS(aDest))
1241
AssertMsgFailed(("Invalid flags! unknown instruction %.20s\n", pszVar));
1245
/* skip blanks and delimiters. */
1246
while (isspace(*pszVar) || *pszVar == '\n' || *pszVar == '\r' || *pszVar == ';')
1248
} /* while more environment variable value left */
1255
* Flushes the specified logger.
1257
* @param pLogger The logger instance to flush.
1258
* If NULL the default instance is used. The default instance
1259
* will not be initialized by this call.
1261
RTDECL(void) RTLogFlush(PRTLOGGER pLogger)
1269
pLogger = &g_Logger;
1271
pLogger = g_pLogger;
1278
* Any thing to flush?
1280
if (pLogger->offScratch)
1284
* Acquire logger instance sem.
1286
int rc = rtlogLock(pLogger);
1293
rtlogFlush(pLogger);
1297
* Release the semaphore.
1299
rtlogUnlock(pLogger);
1306
* Gets the default logger instance.
1308
* @returns Pointer to default logger instance.
1309
* @returns NULL if no default logger instance available.
1311
RTDECL(PRTLOGGER) RTLogDefaultInstance(void)
1319
* Check per thread loggers first.
1321
if (g_cPerThreadLoggers)
1323
const RTNATIVETHREAD Self = RTThreadNativeSelf();
1324
int32_t i = ELEMENTS(g_aPerThreadLoggers);
1326
if (g_aPerThreadLoggers[i].NativeThread == Self)
1327
return g_aPerThreadLoggers[i].pLogger;
1329
# endif /* IN_RING0 */
1332
* If no per thread logger, use the default one.
1335
g_pLogger = RTLogDefaultInit();
1343
* Changes the default logger instance for the current thread.
1345
* @returns IPRT status code.
1346
* @param pLogger The logger instance. Pass NULL for deregistration.
1347
* @param uKey Associated key for cleanup purposes. If pLogger is NULL,
1348
* all instances with this key will be deregistered. So in
1349
* order to only deregister the instance associated with the
1350
* current thread use 0.
1352
RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey)
1355
RTNATIVETHREAD Self = RTThreadNativeSelf();
1358
AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
1361
* Iterate the table to see if there is already an entry for this thread.
1363
int32_t i = ELEMENTS(g_aPerThreadLoggers);
1365
if (g_aPerThreadLoggers[i].NativeThread == Self)
1367
ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
1368
g_aPerThreadLoggers[i].pLogger = pLogger;
1369
return VINF_SUCCESS;
1373
* Allocate a new table entry.
1375
i = ASMAtomicIncS32(&g_cPerThreadLoggers);
1376
if (i > (int32_t)ELEMENTS(g_aPerThreadLoggers))
1378
ASMAtomicDecS32(&g_cPerThreadLoggers);
1379
return VERR_BUFFER_OVERFLOW; /* horrible error code! */
1382
for (unsigned j = 0; j < 10; j++)
1384
i = ELEMENTS(g_aPerThreadLoggers);
1387
AssertCompile(sizeof(RTNATIVETHREAD) == sizeof(void*));
1388
if ( g_aPerThreadLoggers[i].NativeThread == NIL_RTNATIVETHREAD
1389
&& ASMAtomicCmpXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)Self, (void *)NIL_RTNATIVETHREAD))
1391
ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
1392
ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, pLogger);
1393
return VINF_SUCCESS;
1398
ASMAtomicDecS32(&g_cPerThreadLoggers);
1399
rc = VERR_INTERNAL_ERROR;
1404
* Search the array for the current thread.
1406
int32_t i = ELEMENTS(g_aPerThreadLoggers);
1408
if ( g_aPerThreadLoggers[i].NativeThread == Self
1409
|| g_aPerThreadLoggers[i].uKey == uKey)
1411
ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, NULL);
1412
ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, NULL);
1413
ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)NIL_RTNATIVETHREAD);
1414
ASMAtomicDecS32(&g_cPerThreadLoggers);
1425
* Gets the default release logger instance.
1427
* @returns Pointer to default release logger instance.
1428
* @returns NULL if no default release logger instance available.
1430
RTDECL(PRTLOGGER) RTLogRelDefaultInstance(void)
1433
return &g_RelLogger;
1435
return g_pRelLogger;
1442
* Sets the default logger instance.
1444
* @returns iprt status code.
1445
* @param pLogger The new default release logger instance.
1447
RTDECL(PRTLOGGER) RTLogRelSetDefaultInstance(PRTLOGGER pLogger)
1449
return (PRTLOGGER)ASMAtomicXchgPtr((void * volatile *)&g_pRelLogger, pLogger);
1455
* Write to a logger instance.
1457
* @param pLogger Pointer to logger instance.
1458
* @param pvCallerRet Ignored.
1459
* @param pszFormat Format string.
1460
* @param ... Format arguments.
1462
RTDECL(void) RTLogLogger(PRTLOGGER pLogger, void *pvCallerRet, const char *pszFormat, ...)
1465
va_start(args, pszFormat);
1466
#if defined(RT_OS_DARWIN) && defined(RT_ARCH_X86) && defined(IN_RING3)
1467
/* manually align the stack before doing the call.
1468
* We boldly assume that there is a stack frame here! */
1469
__asm__ __volatile__("andl $-32, %%esp\t\n" ::: "%esp");
1470
RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
1472
RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
1479
* Write to a logger instance.
1481
* @param pLogger Pointer to logger instance.
1482
* @param pszFormat Format string.
1483
* @param args Format arguments.
1485
RTDECL(void) RTLogLoggerV(PRTLOGGER pLogger, const char *pszFormat, va_list args)
1487
RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
1492
* Write to a logger instance.
1494
* This function will check whether the instance, group and flags makes up a
1495
* logging kind which is currently enabled before writing anything to the log.
1497
* @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.
1498
* @param fFlags The logging flags.
1499
* @param iGroup The group.
1500
* The value ~0U is reserved for compatability with RTLogLogger[V] and is
1501
* only for internal usage!
1502
* @param pszFormat Format string.
1503
* @param ... Format arguments.
1504
* @remark This is a worker function of LogIt.
1506
RTDECL(void) RTLogLoggerEx(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
1509
va_start(args, pszFormat);
1510
RTLogLoggerExV(pLogger, fFlags, iGroup, pszFormat, args);
1516
* Write to a logger instance.
1518
* This function will check whether the instance, group and flags makes up a
1519
* logging kind which is currently enabled before writing anything to the log.
1521
* @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.
1522
* @param fFlags The logging flags.
1523
* @param iGroup The group.
1524
* The value ~0U is reserved for compatability with RTLogLogger[V] and is
1525
* only for internal usage!
1526
* @param pszFormat Format string.
1527
* @param args Format arguments.
1529
RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
1532
* A NULL logger means default instance.
1536
pLogger = RTLogDefaultInstance();
1540
rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);
1545
* Write to a logger instance, defaulting to the release one.
1547
* This function will check whether the instance, group and flags makes up a
1548
* logging kind which is currently enabled before writing anything to the log.
1550
* @param pLogger Pointer to logger instance.
1551
* @param fFlags The logging flags.
1552
* @param iGroup The group.
1553
* The value ~0U is reserved for compatability with RTLogLogger[V] and is
1554
* only for internal usage!
1555
* @param pszFormat Format string.
1556
* @param ... Format arguments.
1557
* @remark This is a worker function for LogRelIt.
1559
RTDECL(void) RTLogRelLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
1562
va_start(args, pszFormat);
1563
RTLogRelLoggerV(pLogger, fFlags, iGroup, pszFormat, args);
1569
* Write to a logger instance, defaulting to the release one.
1571
* This function will check whether the instance, group and flags makes up a
1572
* logging kind which is currently enabled before writing anything to the log.
1574
* @param pLogger Pointer to logger instance. If NULL the default release instance is attempted.
1575
* @param fFlags The logging flags.
1576
* @param iGroup The group.
1577
* The value ~0U is reserved for compatability with RTLogLogger[V] and is
1578
* only for internal usage!
1579
* @param pszFormat Format string.
1580
* @param args Format arguments.
1582
RTDECL(void) RTLogRelLoggerV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
1585
* A NULL logger means default instance.
1589
pLogger = RTLogRelDefaultInstance();
1593
rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);
1598
* Worker for the RTLog[Rel]Logger*() functions.
1600
* @param pLogger Pointer to logger instance.
1601
* @param fFlags The logging flags.
1602
* @param iGroup The group.
1603
* The value ~0U is reserved for compatability with RTLogLogger[V] and is
1604
* only for internal usage!
1605
* @param pszFormat Format string.
1606
* @param args Format arguments.
1608
static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
1611
* Validate and correct iGroup.
1613
if (iGroup != ~0U && iGroup >= pLogger->cGroups)
1617
* If no output, then just skip it.
1619
if ( (pLogger->fFlags & RTLOGFLAGS_DISABLED)
1621
|| !pLogger->fDestFlags
1623
|| !pszFormat || !*pszFormat)
1626
&& (pLogger->afGroups[iGroup] & (fFlags | RTLOGGRPFLAGS_ENABLED)) != (fFlags | RTLOGGRPFLAGS_ENABLED))
1630
* Acquire logger instance sem.
1632
int rc = rtlogLock(pLogger);
1637
* Format the message and perhaps flush it.
1639
if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF))
1641
RTLOGOUTPUTPREFIXEDARGS OutputArgs;
1642
OutputArgs.pLogger = pLogger;
1643
OutputArgs.iGroup = iGroup;
1644
OutputArgs.fFlags = fFlags;
1645
RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);
1648
RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);
1649
if ( !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)
1650
&& pLogger->offScratch)
1651
rtlogFlush(pLogger);
1654
* Release the semaphore.
1656
rtlogUnlock(pLogger);
1661
* printf like function for writing to the default log.
1663
* @param pszFormat Printf like format string.
1664
* @param ... Optional arguments as specified in pszFormat.
1666
* @remark The API doesn't support formatting of floating point numbers at the moment.
1668
RTDECL(void) RTLogPrintf(const char *pszFormat, ...)
1671
va_start(args, pszFormat);
1672
RTLogPrintfV(pszFormat, args);
1678
* vprintf like function for writing to the default log.
1680
* @param pszFormat Printf like format string.
1681
* @param args Optional arguments as specified in pszFormat.
1683
* @remark The API doesn't support formatting of floating point numbers at the moment.
1685
RTDECL(void) RTLogPrintfV(const char *pszFormat, va_list args)
1687
RTLogLoggerV(NULL, pszFormat, args);
1692
* printf like function for writing to the default release log.
1694
* @param pszFormat Printf like format string.
1695
* @param ... Optional arguments as specified in pszFormat.
1697
* @remark The API doesn't support formatting of floating point numbers at the moment.
1699
RTDECL(void) RTLogRelPrintf(const char *pszFormat, ...)
1702
va_start(args, pszFormat);
1703
RTLogRelPrintfV(pszFormat, args);
1709
* vprintf like function for writing to the default release log.
1711
* @param pszFormat Printf like format string.
1712
* @param args Optional arguments as specified in pszFormat.
1714
* @remark The API doesn't support formatting of floating point numbers at the moment.
1716
RTDECL(void) RTLogRelPrintfV(const char *pszFormat, va_list args)
1718
RTLogRelLoggerV(NULL, 0, ~0U, pszFormat, args);
1723
* Writes the buffer to the given log device without checking for buffered
1725
* Used by the RTLogFlush() function.
1727
* @param pLogger The logger instance to write to. NULL is not allowed!
1729
static void rtlogFlush(PRTLOGGER pLogger)
1732
if (pLogger->fDestFlags & RTLOGDEST_USER)
1733
RTLogWriteUser(pLogger->achScratch, pLogger->offScratch);
1735
if (pLogger->fDestFlags & RTLOGDEST_DEBUGGER)
1736
RTLogWriteDebugger(pLogger->achScratch, pLogger->offScratch);
1739
if (pLogger->fDestFlags & RTLOGDEST_FILE)
1740
RTFileWrite(pLogger->File, pLogger->achScratch, pLogger->offScratch, NULL);
1743
if (pLogger->fDestFlags & RTLOGDEST_STDOUT)
1744
RTLogWriteStdOut(pLogger->achScratch, pLogger->offScratch);
1746
if (pLogger->fDestFlags & RTLOGDEST_STDERR)
1747
RTLogWriteStdErr(pLogger->achScratch, pLogger->offScratch);
1749
# if (defined(IN_RING0) || defined(IN_GC)) && !defined(LOG_NO_COM)
1750
if (pLogger->fDestFlags & RTLOGDEST_COM)
1751
RTLogWriteCom(pLogger->achScratch, pLogger->offScratch);
1755
if (pLogger->pfnFlush)
1756
pLogger->pfnFlush(pLogger);
1758
/* empty the buffer. */
1759
pLogger->offScratch = 0;
1764
* Callback for RTLogFormatV which writes to the com port.
1765
* See PFNLOGOUTPUT() for details.
1767
static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars)
1769
PRTLOGGER pLogger = (PRTLOGGER)pv;
1775
#if defined(DEBUG) && defined(IN_RING3)
1777
if (pLogger->offScratch >= sizeof(pLogger->achScratch))
1779
fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
1780
pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
1781
AssertBreakpoint(); AssertBreakpoint();
1786
size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
1791
memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
1794
pLogger->offScratch += cb;
1805
rtlogFlush(pLogger);
1808
/* won't ever get here! */
1814
* There's always space for a terminator, and it's not counted.
1816
pLogger->achScratch[pLogger->offScratch] = '\0';
1824
* Callback for RTLogFormatV which writes to the logger instance.
1825
* This version supports prefixes.
1827
* See PFNLOGOUTPUT() for details.
1829
static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars)
1831
PRTLOGOUTPUTPREFIXEDARGS pArgs = (PRTLOGOUTPUTPREFIXEDARGS)pv;
1832
PRTLOGGER pLogger = pArgs->pLogger;
1838
size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
1843
if (pLogger->fPendingPrefix)
1845
pLogger->fPendingPrefix = false;
1847
#if defined(DEBUG) && defined(IN_RING3)
1849
if (pLogger->offScratch >= sizeof(pLogger->achScratch))
1851
fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
1852
pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
1853
AssertBreakpoint(); AssertBreakpoint();
1858
* Flush the buffer if there isn't enough room for the maximum prefix config.
1859
* Max is 124, add a couple of extra bytes.
1861
if (cb < 128 + 18 + 22)
1863
rtlogFlush(pLogger);
1864
cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
1868
* Write the prefixes.
1869
* psz is pointing to the current position.
1871
char *psz = &pLogger->achScratch[pLogger->offScratch];
1872
if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TS)
1874
#if defined(IN_RING3) || defined(IN_GC)
1875
uint64_t u64 = RTTimeNanoTS();
1880
unsigned int fFlags = RTSTR_F_ZEROPAD;
1881
if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
1886
if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
1888
static uint64_t s_u64LastTs;
1889
uint64_t u64DiffTs = u64 - s_u64LastTs;
1891
/* We could have been preempted just before reading of s_u64LastTs by
1892
* another thread which wrote s_u64LastTs. In that case the difference
1893
* is negative which we simply ignore. */
1894
u64 = (int64_t)u64DiffTs < 0 ? 0 : u64DiffTs;
1896
/* 1E15 nanoseconds = 11 days */
1897
psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags);
1900
if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TSC)
1902
uint64_t u64 = ASMReadTSC();
1904
unsigned int fFlags = RTSTR_F_ZEROPAD;
1905
if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
1910
if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
1912
static uint64_t s_u64LastTsc;
1913
uint64_t u64DiffTsc = u64 - s_u64LastTsc;
1915
/* We could have been preempted just before reading of s_u64LastTsc by
1916
* another thread which wrote s_u64LastTsc. In that case the difference
1917
* is negative which we simply ignore. */
1918
u64 = u64DiffTsc < 0 ? 0 : u64DiffTsc;
1920
/* 1E15 ticks at 4GHz = 69 hours */
1921
psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags);
1924
if (pLogger->fFlags & RTLOGFLAGS_PREFIX_MS_PROG)
1926
#if defined(IN_RING3) || defined(IN_GC)
1927
uint64_t u64 = RTTimeProgramMilliTS();
1931
/* 1E8 milliseconds = 27 hours */
1932
psz += RTStrFormatNumber(psz, u64, 10, 9, 0, RTSTR_F_ZEROPAD);
1935
if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME)
1938
RTTIMESPEC TimeSpec;
1940
RTTimeExplode(&Time, RTTimeNow(&TimeSpec));
1941
psz += RTStrFormatNumber(psz, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD);
1943
psz += RTStrFormatNumber(psz, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD);
1945
psz += RTStrFormatNumber(psz, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD);
1947
psz += RTStrFormatNumber(psz, Time.u32Nanosecond / 1000000, 10, 3, 0, RTSTR_F_ZEROPAD);
1950
memset(psz, ' ', 13);
1954
if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME_PROG)
1957
uint64_t u64 = RTTimeProgramMilliTS();
1958
psz += RTStrFormatNumber(psz, (uint32_t)(u64 / (60 * 60 * 1000)), 10, 2, 0, RTSTR_F_ZEROPAD);
1960
uint32_t u32 = (uint32_t)(u64 % (60 * 60 * 1000));
1961
psz += RTStrFormatNumber(psz, u32 / (60 * 1000), 10, 2, 0, RTSTR_F_ZEROPAD);
1964
psz += RTStrFormatNumber(psz, u32 / 1000, 10, 2, 0, RTSTR_F_ZEROPAD);
1966
psz += RTStrFormatNumber(psz, u32 % 1000, 10, 3, 0, RTSTR_F_ZEROPAD);
1969
memset(psz, ' ', 13);
1974
if (pLogger->fFlags & RTLOGFLAGS_PREFIX_DATETIME)
1978
RTTimeSpecToString(RTTimeNow(&Time), szDate, sizeof(szDate));
1979
size_t cch = strlen(szDate);
1980
memcpy(psz, szDate, cch);
1985
if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TID)
1988
RTNATIVETHREAD Thread = RTThreadNativeSelf();
1990
RTNATIVETHREAD Thread = NIL_RTNATIVETHREAD;
1992
psz += RTStrFormatNumber(psz, Thread, 16, sizeof(RTNATIVETHREAD) * 2, 0, RTSTR_F_ZEROPAD);
1995
if (pLogger->fFlags & RTLOGFLAGS_PREFIX_THREAD)
1998
const char *pszName = RTThreadSelfName();
2000
const char *pszName = "EMT-GC";
2002
const char *pszName = "EMT-R0";
2007
cch = strlen(pszName);
2008
cch = RT_MIN(cch, 16);
2009
memcpy(psz, pszName, cch);
2016
if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG_NO)
2018
psz += RTStrFormatNumber(psz, pArgs->fFlags, 16, 8, 0, RTSTR_F_ZEROPAD);
2021
if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG)
2024
const char *pszGroup = pArgs->iGroup != ~0U ? pLogger->papszGroups[pArgs->iGroup] : NULL;
2026
const char *pszGroup = NULL;
2031
cch = strlen(pszGroup);
2032
cch = RT_MIN(cch, 16);
2033
memcpy(psz, pszGroup, cch);
2040
if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP_NO)
2042
if (pArgs->iGroup != ~0U)
2044
psz += RTStrFormatNumber(psz, pArgs->iGroup, 16, 3, 0, RTSTR_F_ZEROPAD);
2049
memcpy(psz, "-1 ", sizeof("-1 ") - 1);
2050
psz += sizeof("-1 ") - 1;
2053
if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP)
2055
const unsigned fGrp = pLogger->afGroups[pArgs->iGroup != ~0U ? pArgs->iGroup : 0];
2056
const char *pszGroup;
2058
switch (pArgs->fFlags & fGrp)
2060
case 0: pszGroup = "--------"; cch = sizeof("--------") - 1; break;
2061
case RTLOGGRPFLAGS_ENABLED: pszGroup = "enabled" ; cch = sizeof("enabled" ) - 1; break;
2062
case RTLOGGRPFLAGS_LEVEL_1: pszGroup = "level 1" ; cch = sizeof("level 1" ) - 1; break;
2063
case RTLOGGRPFLAGS_LEVEL_2: pszGroup = "level 2" ; cch = sizeof("level 2" ) - 1; break;
2064
case RTLOGGRPFLAGS_LEVEL_3: pszGroup = "level 3" ; cch = sizeof("level 3" ) - 1; break;
2065
case RTLOGGRPFLAGS_LEVEL_4: pszGroup = "level 4" ; cch = sizeof("level 4" ) - 1; break;
2066
case RTLOGGRPFLAGS_LEVEL_5: pszGroup = "level 5" ; cch = sizeof("level 5" ) - 1; break;
2067
case RTLOGGRPFLAGS_LEVEL_6: pszGroup = "level 6" ; cch = sizeof("level 6" ) - 1; break;
2068
case RTLOGGRPFLAGS_FLOW: pszGroup = "flow" ; cch = sizeof("flow" ) - 1; break;
2070
/* personal groups */
2071
case RTLOGGRPFLAGS_LELIK: pszGroup = "lelik" ; cch = sizeof("lelik" ) - 1; break;
2072
case RTLOGGRPFLAGS_MICHAEL: pszGroup = "Michael" ; cch = sizeof("Michael" ) - 1; break;
2073
case RTLOGGRPFLAGS_DMIK: pszGroup = "dmik" ; cch = sizeof("dmik" ) - 1; break;
2074
case RTLOGGRPFLAGS_SUNLOVER: pszGroup = "sunlover"; cch = sizeof("sunlover") - 1; break;
2075
case RTLOGGRPFLAGS_ACHIM: pszGroup = "Achim" ; cch = sizeof("Achim" ) - 1; break;
2076
case RTLOGGRPFLAGS_SANDER: pszGroup = "Sander" ; cch = sizeof("Sander" ) - 1; break;
2077
case RTLOGGRPFLAGS_KLAUS: pszGroup = "Klaus" ; cch = sizeof("Klaus" ) - 1; break;
2078
case RTLOGGRPFLAGS_FRANK: pszGroup = "Frank" ; cch = sizeof("Frank" ) - 1; break;
2079
case RTLOGGRPFLAGS_BIRD: pszGroup = "bird" ; cch = sizeof("bird" ) - 1; break;
2080
case RTLOGGRPFLAGS_NONAME: pszGroup = "noname" ; cch = sizeof("noname" ) - 1; break;
2081
default: pszGroup = "????????"; cch = sizeof("????????") - 1; break;
2085
cch = RT_MIN(cch, 16);
2086
memcpy(psz, pszGroup, cch);
2095
* Done, figure what we've used and advance the buffer and free size.
2097
cb = psz - &pLogger->achScratch[pLogger->offScratch];
2099
pLogger->offScratch += cb;
2100
cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2104
rtlogFlush(pLogger);
2105
cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2108
#if defined(DEBUG) && defined(IN_RING3)
2110
if (pLogger->offScratch >= sizeof(pLogger->achScratch))
2112
fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
2113
pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
2114
AssertBreakpoint(); AssertBreakpoint();
2123
const char *pszNewLine = (const char *)memchr(pachChars, '\n', cb);
2126
if (pLogger->fFlags & RTLOGFLAGS_USECRLF)
2127
cb = pszNewLine - pachChars;
2130
cb = pszNewLine - pachChars + 1;
2131
pLogger->fPendingPrefix = true;
2136
memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
2139
pLogger->offScratch += cb;
2144
&& (pLogger->fFlags & RTLOGFLAGS_USECRLF)
2145
&& pLogger->offScratch + 2 < sizeof(pLogger->achScratch))
2147
memcpy(&pLogger->achScratch[pLogger->offScratch], "\r\n", 2);
2148
pLogger->offScratch += 2;
2152
pLogger->fPendingPrefix = true;
2161
/* won't ever get here! */
2167
* There's always space for a terminator, and it's not counted.
2169
pLogger->achScratch[pLogger->offScratch] = '\0';