VirtualBox

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

Last change on this file since 7937 was 7713, checked in by vboxsync, 17 years ago

forgot the comment with the max output size.

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

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