VirtualBox

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

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

Fix log flags parsing. The CHLOWER function wasn't working for my gcc
(converted numbers incorrectly) and the end check e.g. caused the
user-provided flag "l3" to match "l".

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 68.3 KB
Line 
1/* $Id: log.cpp 148 2007-01-18 16:12:03Z 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 RTTHREAD volatile Thread;
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 { "rel", sizeof("rel" ) - 1, RTLOGFLAGS_REL_TS, false },
1112 { "abs", sizeof("rel" ) - 1, RTLOGFLAGS_REL_TS, true },
1113 { "dec", sizeof("dec" ) - 1, RTLOGFLAGS_DECIMAL_TS, false },
1114 { "hex", sizeof("hex" ) - 1, RTLOGFLAGS_DECIMAL_TS, true },
1115 { "flagno", sizeof("flagno" ) - 1, RTLOGFLAGS_PREFIX_FLAG_NO, false },
1116 { "flag", sizeof("flag" ) - 1, RTLOGFLAGS_PREFIX_FLAG, false },
1117 { "groupno", sizeof("groupno" ) - 1, RTLOGFLAGS_PREFIX_GROUP_NO, false },
1118 { "group", sizeof("group" ) - 1, RTLOGFLAGS_PREFIX_GROUP, false },
1119 { "tid", sizeof("tid" ) - 1, RTLOGFLAGS_PREFIX_TID, false },
1120 { "thread", sizeof("thread" ) - 1, RTLOGFLAGS_PREFIX_THREAD, false },
1121 { "timeprog", sizeof("timeprog" ) - 1, RTLOGFLAGS_PREFIX_TIME_PROG, false },
1122 { "time", sizeof("time" ) - 1, RTLOGFLAGS_PREFIX_TIME, false },
1123 { "msprog", sizeof("msprog" ) - 1, RTLOGFLAGS_PREFIX_MS_PROG, false },
1124 { "tsc", sizeof("tsc" ) - 1, RTLOGFLAGS_PREFIX_TSC, false }, /* before ts! */
1125 { "ts", sizeof("ts" ) - 1, RTLOGFLAGS_PREFIX_TS, false },
1126 };
1127
1128 /* check no prefix. */
1129 bool fNo = false;
1130 char ch;
1131 while ((ch = *pszVar) != '\0')
1132 {
1133 if (ch == 'n' && pszVar[1] == 'o')
1134 {
1135 pszVar += 2;
1136 fNo = !fNo;
1137 }
1138 else if (ch == '+')
1139 {
1140 pszVar++;
1141 fNo = true;
1142 }
1143 else if (ch == '-' || ch == '!' || ch == '~')
1144 {
1145 pszVar++;
1146 fNo = !fNo;
1147 }
1148 else
1149 break;
1150 }
1151
1152 /* instruction. */
1153 unsigned i;
1154 for (i = 0; i < ELEMENTS(aDest); i++)
1155 {
1156 if (!strncmp(pszVar, aDest[i].pszInstr, aDest[i].cchInstr))
1157 {
1158 if (fNo == aDest[i].fInverted)
1159 pLogger->fFlags |= aDest[i].fFlag;
1160 else
1161 pLogger->fFlags &= ~aDest[i].fFlag;
1162 pszVar += aDest[i].cchInstr;
1163 break;
1164 }
1165 }
1166
1167 /* unknown instruction? */
1168 if (i >= ELEMENTS(aDest))
1169 {
1170 AssertMsgFailed(("Invalid flags! unknown instruction %.20s\n", pszVar));
1171 pszVar++;
1172 }
1173
1174 /* skip blanks and delimiters. */
1175 while (isspace(*pszVar) || *pszVar == '\n' || *pszVar == '\r' || *pszVar == ';')
1176 pszVar++;
1177 } /* while more environment variable value left */
1178
1179 return rc;
1180}
1181
1182
1183/**
1184 * Flushes the specified logger.
1185 *
1186 * @param pLogger The logger instance to flush.
1187 * If NULL the default instance is used. The default instance
1188 * will not be initialized by this call.
1189 */
1190RTDECL(void) RTLogFlush(PRTLOGGER pLogger)
1191{
1192 /*
1193 * Resolve defaults.
1194 */
1195 if (!pLogger)
1196 {
1197#ifdef IN_GC
1198 pLogger = &g_Logger;
1199#else
1200 pLogger = g_pLogger;
1201#endif
1202 if (!pLogger)
1203 return;
1204 }
1205
1206 /*
1207 * Any thing to flush?
1208 */
1209 if (pLogger->offScratch)
1210 {
1211#ifndef IN_GC
1212 /*
1213 * Acquire logger instance sem.
1214 */
1215 int rc = rtlogLock(pLogger);
1216 if (RT_FAILURE(rc))
1217 return;
1218#endif
1219 /*
1220 * Call worker.
1221 */
1222 rtlogFlush(pLogger);
1223
1224#ifndef IN_GC
1225 /*
1226 * Release the semaphore.
1227 */
1228 rtlogUnlock(pLogger);
1229#endif
1230 }
1231}
1232
1233
1234/**
1235 * Gets the default logger instance.
1236 *
1237 * @returns Pointer to default logger instance.
1238 * @returns NULL if no default logger instance available.
1239 */
1240RTDECL(PRTLOGGER) RTLogDefaultInstance(void)
1241{
1242#ifdef IN_GC
1243 return &g_Logger;
1244
1245#else /* !IN_GC */
1246# ifdef IN_RING0
1247 /*
1248 * Check per thread loggers first.
1249 */
1250 if (g_cPerThreadLoggers)
1251 {
1252 const RTTHREAD Self = RTThreadSelf();
1253 int32_t i = ELEMENTS(g_aPerThreadLoggers);
1254 while (i-- > 0)
1255 if (g_aPerThreadLoggers[i].Thread == Self)
1256 return g_aPerThreadLoggers[i].pLogger;
1257 }
1258# endif /* IN_RING0 */
1259
1260 /*
1261 * If no per thread logger, use the default one.
1262 */
1263 if (!g_pLogger)
1264 g_pLogger = RTLogDefaultInit();
1265 return g_pLogger;
1266#endif /* !IN_GC */
1267}
1268
1269
1270#ifdef IN_RING0
1271/**
1272 * Changes the default logger instance for the current thread.
1273 *
1274 * @returns IPRT status code.
1275 * @param pLogger The logger instance. Pass NULL for deregistration.
1276 * @param uKey Associated key for cleanup purposes. If pLogger is NULL,
1277 * all instances with this key will be deregistered. So in
1278 * order to only deregister the instance associated with the
1279 * current thread use 0.
1280 */
1281RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey)
1282{
1283 int rc;
1284 RTTHREAD Self = RTThreadSelf();
1285 if (pLogger)
1286 {
1287 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
1288
1289 /*
1290 * Iterate the table to see if there is already an entry for this thread.
1291 */
1292 int32_t i = ELEMENTS(g_aPerThreadLoggers);
1293 while (i-- > 0)
1294 if (g_aPerThreadLoggers[i].Thread == Self)
1295 {
1296 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
1297 g_aPerThreadLoggers[i].pLogger = pLogger;
1298 return VINF_SUCCESS;
1299 }
1300
1301 /*
1302 * Allocate a new table entry.
1303 */
1304 i = ASMAtomicIncS32(&g_cPerThreadLoggers);
1305 if (i > (int32_t)ELEMENTS(g_aPerThreadLoggers))
1306 {
1307 ASMAtomicDecS32(&g_cPerThreadLoggers);
1308 return VERR_BUFFER_OVERFLOW; /* horrible error code! */
1309 }
1310
1311 for (unsigned j = 0; j < 10; j++)
1312 {
1313 i = ELEMENTS(g_aPerThreadLoggers);
1314 while (i-- > 0)
1315 {
1316 AssertCompile(sizeof(RTTHREAD) == sizeof(void*));
1317 if ( g_aPerThreadLoggers[i].Thread == NIL_RTTHREAD
1318 && ASMAtomicCmpXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].Thread, Self, NIL_RTTHREAD))
1319 {
1320 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
1321 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, pLogger);
1322 return VINF_SUCCESS;
1323 }
1324 }
1325 }
1326
1327 ASMAtomicDecS32(&g_cPerThreadLoggers);
1328 rc = VERR_INTERNAL_ERROR;
1329 }
1330 else
1331 {
1332 /*
1333 * Search the array for the current thread.
1334 */
1335 int32_t i = ELEMENTS(g_aPerThreadLoggers);
1336 while (i-- > 0)
1337 if ( g_aPerThreadLoggers[i].Thread == Self
1338 || g_aPerThreadLoggers[i].uKey == uKey)
1339 {
1340 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, NULL);
1341 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, NULL);
1342 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].Thread, NIL_RTTHREAD);
1343 ASMAtomicDecS32(&g_cPerThreadLoggers);
1344 }
1345
1346 rc = VINF_SUCCESS;
1347 }
1348 return rc;
1349}
1350#endif
1351
1352
1353/**
1354 * Gets the default release logger instance.
1355 *
1356 * @returns Pointer to default release logger instance.
1357 * @returns NULL if no default release logger instance available.
1358 */
1359RTDECL(PRTLOGGER) RTLogRelDefaultInstance(void)
1360{
1361#ifdef IN_GC
1362 return &g_RelLogger;
1363#else /* !IN_GC */
1364 return g_pRelLogger;
1365#endif /* !IN_GC */
1366}
1367
1368
1369#ifndef IN_GC
1370/**
1371 * Sets the default logger instance.
1372 *
1373 * @returns iprt status code.
1374 * @param pLogger The new default release logger instance.
1375 */
1376RTDECL(PRTLOGGER) RTLogRelSetDefaultInstance(PRTLOGGER pLogger)
1377{
1378 return (PRTLOGGER)ASMAtomicXchgPtr((void * volatile *)&g_pRelLogger, pLogger);
1379}
1380#endif /* !IN_GC */
1381
1382
1383/**
1384 * Write to a logger instance.
1385 *
1386 * @param pLogger Pointer to logger instance.
1387 * @param pvCallerRet Ignored.
1388 * @param pszFormat Format string.
1389 * @param ... Format arguments.
1390 */
1391RTDECL(void) RTLogLogger(PRTLOGGER pLogger, void *pvCallerRet, const char *pszFormat, ...)
1392{
1393 va_list args;
1394 va_start(args, pszFormat);
1395#if defined(__DARWIN__) && defined(__X86__) && defined(IN_RING3)
1396 /* manually align the stack before doing the call.
1397 * We boldly assume that there is a stack frame here! */
1398 __asm__ __volatile__("andl $-32, %%esp\t\n" ::: "%esp");
1399 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
1400#else
1401 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
1402#endif
1403 va_end(args);
1404}
1405
1406
1407/**
1408 * Write to a logger instance.
1409 *
1410 * @param pLogger Pointer to logger instance.
1411 * @param pszFormat Format string.
1412 * @param args Format arguments.
1413 */
1414RTDECL(void) RTLogLoggerV(PRTLOGGER pLogger, const char *pszFormat, va_list args)
1415{
1416 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
1417}
1418
1419
1420/**
1421 * Write to a logger instance.
1422 *
1423 * This function will check whether the instance, group and flags makes up a
1424 * logging kind which is currently enabled before writing anything to the log.
1425 *
1426 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.
1427 * @param fFlags The logging flags.
1428 * @param iGroup The group.
1429 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1430 * only for internal usage!
1431 * @param pszFormat Format string.
1432 * @param ... Format arguments.
1433 * @remark This is a worker function of LogIt.
1434 */
1435RTDECL(void) RTLogLoggerEx(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
1436{
1437 va_list args;
1438 va_start(args, pszFormat);
1439 RTLogLoggerExV(pLogger, fFlags, iGroup, pszFormat, args);
1440 va_end(args);
1441}
1442
1443
1444/**
1445 * Write to a logger instance.
1446 *
1447 * This function will check whether the instance, group and flags makes up a
1448 * logging kind which is currently enabled before writing anything to the log.
1449 *
1450 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.
1451 * @param fFlags The logging flags.
1452 * @param iGroup The group.
1453 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1454 * only for internal usage!
1455 * @param pszFormat Format string.
1456 * @param args Format arguments.
1457 */
1458RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
1459{
1460 /*
1461 * A NULL logger means default instance.
1462 */
1463 if (!pLogger)
1464 {
1465 pLogger = RTLogDefaultInstance();
1466 if (!pLogger)
1467 return;
1468 }
1469 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);
1470}
1471
1472
1473/**
1474 * Write to a logger instance, defaulting to the release one.
1475 *
1476 * This function will check whether the instance, group and flags makes up a
1477 * logging kind which is currently enabled before writing anything to the log.
1478 *
1479 * @param pLogger Pointer to logger instance.
1480 * @param fFlags The logging flags.
1481 * @param iGroup The group.
1482 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1483 * only for internal usage!
1484 * @param pszFormat Format string.
1485 * @param ... Format arguments.
1486 * @remark This is a worker function for LogRelIt.
1487 */
1488RTDECL(void) RTLogRelLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
1489{
1490 va_list args;
1491 va_start(args, pszFormat);
1492 RTLogRelLoggerV(pLogger, fFlags, iGroup, pszFormat, args);
1493 va_end(args);
1494}
1495
1496
1497/**
1498 * Write to a logger instance, defaulting to the release one.
1499 *
1500 * This function will check whether the instance, group and flags makes up a
1501 * logging kind which is currently enabled before writing anything to the log.
1502 *
1503 * @param pLogger Pointer to logger instance. If NULL the default release instance is attempted.
1504 * @param fFlags The logging flags.
1505 * @param iGroup The group.
1506 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1507 * only for internal usage!
1508 * @param pszFormat Format string.
1509 * @param args Format arguments.
1510 */
1511RTDECL(void) RTLogRelLoggerV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
1512{
1513 /*
1514 * A NULL logger means default instance.
1515 */
1516 if (!pLogger)
1517 {
1518 pLogger = RTLogRelDefaultInstance();
1519 if (!pLogger)
1520 return;
1521 }
1522 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);
1523}
1524
1525
1526/**
1527 * Worker for the RTLog[Rel]Logger*() functions.
1528 *
1529 * @param pLogger Pointer to logger instance.
1530 * @param fFlags The logging flags.
1531 * @param iGroup The group.
1532 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1533 * only for internal usage!
1534 * @param pszFormat Format string.
1535 * @param args Format arguments.
1536 */
1537static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
1538{
1539 /*
1540 * Validate and correct iGroup.
1541 */
1542 if (iGroup != ~0U && iGroup >= pLogger->cGroups)
1543 iGroup = 0;
1544
1545 /*
1546 * If no output, then just skip it.
1547 */
1548 if ( (pLogger->fFlags & RTLOGFLAGS_DISABLED)
1549#ifndef IN_GC
1550 || !pLogger->fDestFlags
1551#endif
1552 || !pszFormat || !*pszFormat)
1553 return;
1554 if ( iGroup != ~0U
1555 && (pLogger->afGroups[iGroup] & (fFlags | RTLOGGRPFLAGS_ENABLED)) != (fFlags | RTLOGGRPFLAGS_ENABLED))
1556 return;
1557
1558 /*
1559 * Acquire logger instance sem.
1560 */
1561 int rc = rtlogLock(pLogger);
1562 if (RT_FAILURE(rc))
1563 return;
1564
1565 /*
1566 * Format the message and perhaps flush it.
1567 */
1568 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_MASK)
1569 {
1570 RTLOGOUTPUTPREFIXEDARGS OutputArgs;
1571 OutputArgs.pLogger = pLogger;
1572 OutputArgs.iGroup = iGroup;
1573 OutputArgs.fFlags = fFlags;
1574 RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);
1575 }
1576 else
1577 RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);
1578 if ( !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)
1579 && pLogger->offScratch)
1580 rtlogFlush(pLogger);
1581
1582 /*
1583 * Release the semaphore.
1584 */
1585 rtlogUnlock(pLogger);
1586}
1587
1588
1589/**
1590 * printf like function for writing to the default log.
1591 *
1592 * @param pszFormat Printf like format string.
1593 * @param ... Optional arguments as specified in pszFormat.
1594 *
1595 * @remark The API doesn't support formatting of floating point numbers at the moment.
1596 */
1597RTDECL(void) RTLogPrintf(const char *pszFormat, ...)
1598{
1599 va_list args;
1600 va_start(args, pszFormat);
1601 RTLogPrintfV(pszFormat, args);
1602 va_end(args);
1603}
1604
1605
1606/**
1607 * vprintf like function for writing to the default log.
1608 *
1609 * @param pszFormat Printf like format string.
1610 * @param args Optional arguments as specified in pszFormat.
1611 *
1612 * @remark The API doesn't support formatting of floating point numbers at the moment.
1613 */
1614RTDECL(void) RTLogPrintfV(const char *pszFormat, va_list args)
1615{
1616 RTLogLoggerV(NULL, pszFormat, args);
1617}
1618
1619
1620/**
1621 * printf like function for writing to the default release log.
1622 *
1623 * @param pszFormat Printf like format string.
1624 * @param ... Optional arguments as specified in pszFormat.
1625 *
1626 * @remark The API doesn't support formatting of floating point numbers at the moment.
1627 */
1628RTDECL(void) RTLogRelPrintf(const char *pszFormat, ...)
1629{
1630 va_list args;
1631 va_start(args, pszFormat);
1632 RTLogRelPrintfV(pszFormat, args);
1633 va_end(args);
1634}
1635
1636
1637/**
1638 * vprintf like function for writing to the default release log.
1639 *
1640 * @param pszFormat Printf like format string.
1641 * @param args Optional arguments as specified in pszFormat.
1642 *
1643 * @remark The API doesn't support formatting of floating point numbers at the moment.
1644 */
1645RTDECL(void) RTLogRelPrintfV(const char *pszFormat, va_list args)
1646{
1647 RTLogRelLoggerV(NULL, 0, ~0U, pszFormat, args);
1648}
1649
1650
1651/**
1652 * Writes the buffer to the given log device without checking for buffered
1653 * data or anything.
1654 * Used by the RTLogFlush() function.
1655 *
1656 * @param pLogger The logger instance to write to. NULL is not allowed!
1657 */
1658static void rtlogFlush(PRTLOGGER pLogger)
1659{
1660#ifndef IN_GC
1661 if (pLogger->fDestFlags & RTLOGDEST_USER)
1662 RTLogWriteUser(pLogger->achScratch, pLogger->offScratch);
1663
1664 if (pLogger->fDestFlags & RTLOGDEST_DEBUGGER)
1665 RTLogWriteDebugger(pLogger->achScratch, pLogger->offScratch);
1666
1667# ifdef IN_RING3
1668 if (pLogger->fDestFlags & RTLOGDEST_FILE)
1669 RTFileWrite(pLogger->File, pLogger->achScratch, pLogger->offScratch, NULL);
1670# endif
1671
1672 if (pLogger->fDestFlags & RTLOGDEST_STDOUT)
1673 RTLogWriteStdOut(pLogger->achScratch, pLogger->offScratch);
1674
1675 if (pLogger->fDestFlags & RTLOGDEST_STDERR)
1676 RTLogWriteStdErr(pLogger->achScratch, pLogger->offScratch);
1677
1678# if (defined(IN_RING0) || defined(IN_GC)) && !defined(LOG_NO_COM)
1679 if (pLogger->fDestFlags & RTLOGDEST_COM)
1680 RTLogWriteCom(pLogger->achScratch, pLogger->offScratch);
1681# endif
1682#endif /* !IN_GC */
1683
1684 if (pLogger->pfnFlush)
1685 pLogger->pfnFlush(pLogger);
1686
1687 /* empty the buffer. */
1688 pLogger->offScratch = 0;
1689}
1690
1691
1692/**
1693 * Callback for RTLogFormatV which writes to the com port.
1694 * See PFNLOGOUTPUT() for details.
1695 */
1696static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars)
1697{
1698 PRTLOGGER pLogger = (PRTLOGGER)pv;
1699 if (cbChars)
1700 {
1701 size_t cbRet = 0;
1702 for (;;)
1703 {
1704#if defined(DEBUG) && defined(IN_RING3)
1705 /* sanity */
1706 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
1707 {
1708 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
1709 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
1710 AssertBreakpoint(); AssertBreakpoint();
1711 }
1712#endif
1713
1714 /* how much */
1715 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
1716 if (cb > cbChars)
1717 cb = cbChars;
1718
1719 /* copy */
1720 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
1721
1722 /* advance */
1723 pLogger->offScratch += cb;
1724 cbRet += cb;
1725 cbChars -= cb;
1726
1727 /* done? */
1728 if (cbChars <= 0)
1729 return cbRet;
1730
1731 pachChars += cb;
1732
1733 /* flush */
1734 rtlogFlush(pLogger);
1735 }
1736
1737 /* won't ever get here! */
1738 }
1739 else
1740 {
1741 /*
1742 * Termination call.
1743 * There's always space for a terminator, and it's not counted.
1744 */
1745 pLogger->achScratch[pLogger->offScratch] = '\0';
1746 return 0;
1747 }
1748}
1749
1750
1751
1752/**
1753 * Callback for RTLogFormatV which writes to the logger instance.
1754 * This version supports prefixes.
1755 *
1756 * See PFNLOGOUTPUT() for details.
1757 */
1758static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars)
1759{
1760 PRTLOGOUTPUTPREFIXEDARGS pArgs = (PRTLOGOUTPUTPREFIXEDARGS)pv;
1761 PRTLOGGER pLogger = pArgs->pLogger;
1762 if (cbChars)
1763 {
1764 size_t cbRet = 0;
1765 for (;;)
1766 {
1767 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
1768
1769 /*
1770 * Pending prefix?
1771 */
1772 if (pLogger->fPendingPrefix)
1773 {
1774 pLogger->fPendingPrefix = false;
1775
1776#if defined(DEBUG) && defined(IN_RING3)
1777 /* sanity */
1778 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
1779 {
1780 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
1781 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
1782 AssertBreakpoint(); AssertBreakpoint();
1783 }
1784#endif
1785
1786 /*
1787 * Flush the buffer if there isn't enough room for the maximum prefix config.
1788 * Max is 124, add a couple of extra bytes.
1789 */
1790 if (cb < 128 + 18 + 22)
1791 {
1792 rtlogFlush(pLogger);
1793 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
1794 }
1795
1796 /*
1797 * Write the prefixes.
1798 * psz is pointing to the current position.
1799 */
1800 char *psz = &pLogger->achScratch[pLogger->offScratch];
1801 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TS)
1802 {
1803#if defined(IN_RING3) || defined(IN_GC)
1804 uint64_t u64 = RTTimeNanoTS();
1805#else
1806 uint64_t u64 = ~0;
1807#endif
1808 int iBase = 16;
1809 unsigned int fFlags = RTSTR_F_ZEROPAD;
1810 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
1811 {
1812 iBase = 10;
1813 fFlags = 0;
1814 }
1815 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
1816 {
1817 static uint64_t s_u64LastTs;
1818 uint64_t u64DiffTs = u64 - s_u64LastTs;
1819 s_u64LastTs = u64;
1820 /* We could have been preempted just before reading of s_u64LastTs by
1821 * another thread which wrote s_u64LastTs. In that case the difference
1822 * is negative which we simply ignore. */
1823 u64 = (int64_t)u64DiffTs < 0 ? 0 : u64DiffTs;
1824 }
1825 /* 1E15 nanoseconds = 11 days */
1826 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags);
1827 *psz++ = ' ';
1828 }
1829 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TSC)
1830 {
1831 uint64_t u64 = ASMReadTSC();
1832 int iBase = 16;
1833 unsigned int fFlags = RTSTR_F_ZEROPAD;
1834 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
1835 {
1836 iBase = 10;
1837 fFlags = 0;
1838 }
1839 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
1840 {
1841 static uint64_t s_u64LastTsc;
1842 uint64_t u64DiffTsc = u64 - s_u64LastTsc;
1843 s_u64LastTsc = u64;
1844 /* We could have been preempted just before reading of s_u64LastTsc by
1845 * another thread which wrote s_u64LastTsc. In that case the difference
1846 * is negative which we simply ignore. */
1847 u64 = u64DiffTsc < 0 ? 0 : u64DiffTsc;
1848 }
1849 /* 1E15 ticks at 4GHz = 69 hours */
1850 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags);
1851 *psz++ = ' ';
1852 }
1853 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_MS_PROG)
1854 {
1855#ifdef IN_RING3
1856 uint64_t u64 = RTTimeProgramMilliTS();
1857#else
1858 uint64_t u64 = 0;
1859#endif
1860 /* 1E8 milliseconds = 27 hours */
1861 psz += RTStrFormatNumber(psz, u64, 10, 9, 0, RTSTR_F_ZEROPAD);
1862 *psz++ = ' ';
1863 }
1864 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME)
1865 {
1866#ifdef IN_RING3
1867 RTTIMESPEC TimeSpec;
1868 RTTIME Time;
1869 RTTimeExplode(&Time, RTTimeNow(&TimeSpec));
1870 psz += RTStrFormatNumber(psz, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD);
1871 *psz++ = ':';
1872 psz += RTStrFormatNumber(psz, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD);
1873 *psz++ = ':';
1874 psz += RTStrFormatNumber(psz, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD);
1875 *psz++ = '.';
1876 psz += RTStrFormatNumber(psz, Time.u32Nanosecond / 1000000, 10, 3, 0, RTSTR_F_ZEROPAD);
1877 *psz++ = ' ';
1878#else
1879 memset(psz, ' ', 13);
1880 psz += 13;
1881#endif
1882 }
1883 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME_PROG)
1884 {
1885#ifdef IN_RING3
1886 uint64_t u64 = RTTimeProgramMilliTS();
1887 psz += RTStrFormatNumber(psz, (uint32_t)(u64 / (60 * 60 * 1000)), 10, 2, 0, RTSTR_F_ZEROPAD);
1888 *psz++ = ':';
1889 uint32_t u32 = (uint32_t)(u64 % (60 * 60 * 1000));
1890 psz += RTStrFormatNumber(psz, u32 / (60 * 1000), 10, 2, 0, RTSTR_F_ZEROPAD);
1891 *psz++ = ':';
1892 u32 %= 60 * 1000;
1893 psz += RTStrFormatNumber(psz, u32 / 1000, 10, 2, 0, RTSTR_F_ZEROPAD);
1894 *psz++ = '.';
1895 psz += RTStrFormatNumber(psz, u32 % 1000, 10, 3, 0, RTSTR_F_ZEROPAD);
1896 *psz++ = ' ';
1897#else
1898 memset(psz, ' ', 13);
1899 psz += 13;
1900#endif
1901 }
1902# if 0
1903 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_DATETIME)
1904 {
1905 char szDate[32];
1906 RTTIMESPEC Time;
1907 RTTimeSpecToString(RTTimeNow(&Time), szDate, sizeof(szDate));
1908 size_t cch = strlen(szDate);
1909 memcpy(psz, szDate, cch);
1910 psz += cch;
1911 *psz++ = ' ';
1912 }
1913# endif
1914 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TID)
1915 {
1916#ifdef IN_RING3
1917 RTNATIVETHREAD Thread = RTThreadNativeSelf();
1918#else
1919 RTNATIVETHREAD Thread = NIL_RTNATIVETHREAD;
1920#endif
1921 psz += RTStrFormatNumber(psz, Thread, 16, sizeof(RTNATIVETHREAD) * 2, 0, RTSTR_F_ZEROPAD);
1922 *psz++ = ' ';
1923 }
1924 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_THREAD)
1925 {
1926#ifdef IN_RING3
1927 const char *pszName = RTThreadSelfName();
1928#elif defined IN_GC
1929 const char *pszName = "EMT-GC";
1930#else
1931 const char *pszName = "EMT-R0";
1932#endif
1933 size_t cch = 0;
1934 if (pszName)
1935 {
1936 cch = strlen(pszName);
1937 cch = RT_MIN(cch, 16);
1938 memcpy(psz, pszName, cch);
1939 psz += cch;
1940 }
1941 do
1942 *psz++ = ' ';
1943 while (cch++ < 8);
1944 }
1945 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG_NO)
1946 {
1947 psz += RTStrFormatNumber(psz, pArgs->fFlags, 16, 8, 0, RTSTR_F_ZEROPAD);
1948 *psz++ = ' ';
1949 }
1950 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG)
1951 {
1952#ifdef IN_RING3
1953 const char *pszGroup = pArgs->iGroup != ~0U ? pLogger->papszGroups[pArgs->iGroup] : NULL;
1954#else
1955 const char *pszGroup = NULL;
1956#endif
1957 size_t cch = 0;
1958 if (pszGroup)
1959 {
1960 cch = strlen(pszGroup);
1961 cch = RT_MIN(cch, 16);
1962 memcpy(psz, pszGroup, cch);
1963 psz += cch;
1964 }
1965 do
1966 *psz++ = ' ';
1967 while (cch++ < 8);
1968 }
1969 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP_NO)
1970 {
1971 if (pArgs->iGroup != ~0U)
1972 {
1973 psz += RTStrFormatNumber(psz, pArgs->iGroup, 16, 3, 0, RTSTR_F_ZEROPAD);
1974 *psz++ = ' ';
1975 }
1976 else
1977 {
1978 memcpy(psz, "-1 ", sizeof("-1 ") - 1);
1979 psz += sizeof("-1 ") - 1;
1980 }
1981 }
1982 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP)
1983 {
1984 const unsigned fGrp = pLogger->afGroups[pArgs->iGroup != ~0U ? pArgs->iGroup : 0];
1985 const char *pszGroup;
1986 size_t cch;
1987 switch (pArgs->fFlags & fGrp)
1988 {
1989 case 0: pszGroup = "--------"; cch = sizeof("--------") - 1; break;
1990 case RTLOGGRPFLAGS_ENABLED: pszGroup = "enabled" ; cch = sizeof("enabled" ) - 1; break;
1991 case RTLOGGRPFLAGS_LEVEL_1: pszGroup = "level 1" ; cch = sizeof("level 1" ) - 1; break;
1992 case RTLOGGRPFLAGS_LEVEL_2: pszGroup = "level 2" ; cch = sizeof("level 2" ) - 1; break;
1993 case RTLOGGRPFLAGS_LEVEL_3: pszGroup = "level 3" ; cch = sizeof("level 3" ) - 1; break;
1994 case RTLOGGRPFLAGS_LEVEL_4: pszGroup = "level 4" ; cch = sizeof("level 4" ) - 1; break;
1995 case RTLOGGRPFLAGS_LEVEL_5: pszGroup = "level 5" ; cch = sizeof("level 5" ) - 1; break;
1996 case RTLOGGRPFLAGS_LEVEL_6: pszGroup = "level 6" ; cch = sizeof("level 6" ) - 1; break;
1997 case RTLOGGRPFLAGS_FLOW: pszGroup = "flow" ; cch = sizeof("flow" ) - 1; break;
1998
1999 /* personal groups */
2000 case RTLOGGRPFLAGS_LELIK: pszGroup = "lelik" ; cch = sizeof("lelik" ) - 1; break;
2001 case RTLOGGRPFLAGS_MICHAEL: pszGroup = "Michael" ; cch = sizeof("Michael" ) - 1; break;
2002 case RTLOGGRPFLAGS_DMIK: pszGroup = "dmik" ; cch = sizeof("dmik" ) - 1; break;
2003 case RTLOGGRPFLAGS_SUNLOVER: pszGroup = "sunlover"; cch = sizeof("sunlover") - 1; break;
2004 case RTLOGGRPFLAGS_ACHIM: pszGroup = "Achim" ; cch = sizeof("Achim" ) - 1; break;
2005 case RTLOGGRPFLAGS_SANDER: pszGroup = "Sander" ; cch = sizeof("Sander" ) - 1; break;
2006 case RTLOGGRPFLAGS_KLAUS: pszGroup = "Klaus" ; cch = sizeof("Klaus" ) - 1; break;
2007 case RTLOGGRPFLAGS_FRANK: pszGroup = "Frank" ; cch = sizeof("Frank" ) - 1; break;
2008 case RTLOGGRPFLAGS_BIRD: pszGroup = "bird" ; cch = sizeof("bird" ) - 1; break;
2009 case RTLOGGRPFLAGS_NONAME: pszGroup = "noname" ; cch = sizeof("noname" ) - 1; break;
2010 default: pszGroup = "????????"; cch = sizeof("????????") - 1; break;
2011 }
2012 if (pszGroup)
2013 {
2014 cch = RT_MIN(cch, 16);
2015 memcpy(psz, pszGroup, cch);
2016 psz += cch;
2017 }
2018 do
2019 *psz++ = ' ';
2020 while (cch++ < 8);
2021 }
2022
2023 /*
2024 * Done, figure what we've used and advance the buffer and free size.
2025 */
2026 cb = psz - &pLogger->achScratch[pLogger->offScratch];
2027 Assert(cb <= 124);
2028 pLogger->offScratch += cb;
2029 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2030 }
2031 else if (cb <= 0)
2032 {
2033 rtlogFlush(pLogger);
2034 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2035 }
2036
2037#if defined(DEBUG) && defined(IN_RING3)
2038 /* sanity */
2039 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
2040 {
2041 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
2042 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
2043 AssertBreakpoint(); AssertBreakpoint();
2044 }
2045#endif
2046
2047 /* how much */
2048 if (cb > cbChars)
2049 cb = cbChars;
2050
2051 /* have newline? */
2052 const char *pszNewLine = (const char *)memchr(pachChars, '\n', cb);
2053 if (pszNewLine)
2054 {
2055 pLogger->fPendingPrefix = true;
2056 cb = pszNewLine - pachChars + 1;
2057 }
2058
2059 /* copy */
2060 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
2061
2062 /* advance */
2063 pLogger->offScratch += cb;
2064 cbRet += cb;
2065 cbChars -= cb;
2066
2067 /* done? */
2068 if (cbChars <= 0)
2069 return cbRet;
2070 pachChars += cb;
2071 }
2072
2073 /* won't ever get here! */
2074 }
2075 else
2076 {
2077 /*
2078 * Termination call.
2079 * There's always space for a terminator, and it's not counted.
2080 */
2081 pLogger->achScratch[pLogger->offScratch] = '\0';
2082 return 0;
2083 }
2084}
2085
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