VirtualBox

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

Last change on this file since 1532 was 1219, checked in by vboxsync, 18 years ago

R0 logging fixes

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