VirtualBox

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

Last change on this file since 9185 was 8671, checked in by vboxsync, 17 years ago

?/? for r0/gc.

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