VirtualBox

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

Last change on this file since 977 was 631, checked in by vboxsync, 18 years ago

Select the right output method when RTLOGFLAGS_USECRLF set is and there are no prefixes.

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