VirtualBox

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

最後變更 在這個檔案從11442是 11391,由 vboxsync 提交於 17 年 前

Moved annoying log messages to either level4 or private logging. Added private logging macro LogAleksey.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id
檔案大小: 77.8 KB
 
1/* $Id: log.cpp 11391 2008-08-13 14:48:53Z 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.alldomusa.eu.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(RTLOGGERRC) g_Logger;
97/** Default relese logger instance. */
98extern "C" DECLIMPORT(RTLOGGERRC) 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. NULL is fine.
536 */
537RTDECL(int) RTLogDestroy(PRTLOGGER pLogger)
538{
539 int rc;
540 RTUINT iGroup;
541 RTSEMFASTMUTEX MutexSem;
542
543 /*
544 * Validate input.
545 */
546 if (!pLogger)
547 return VINF_SUCCESS;
548 AssertReturn(VALID_PTR(pLogger), VERR_INVALID_POINTER);
549 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
550
551 /*
552 * Acquire logger instance sem and disable all logging. (paranoia)
553 */
554 rc = rtlogLock(pLogger);
555 if (RT_FAILURE(rc))
556 return rc;
557
558 pLogger->fFlags |= RTLOGFLAGS_DISABLED;
559 iGroup = pLogger->cGroups;
560 while (iGroup-- > 0)
561 pLogger->afGroups[iGroup] = 0;
562
563 /*
564 * Flush it.
565 */
566 RTLogFlush(pLogger);
567
568 /*
569 * Close output stuffs.
570 */
571#ifdef IN_RING3
572 if (pLogger->File != NIL_RTFILE)
573 {
574 int rc2 = RTFileClose(pLogger->File);
575 AssertRC(rc2);
576 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
577 rc = rc2;
578 pLogger->File = NIL_RTFILE;
579 }
580#endif
581
582 /*
583 * Free the mutex and the instance memory.
584 */
585 MutexSem = pLogger->MutexSem;
586 pLogger->MutexSem = NIL_RTSEMFASTMUTEX;
587 if (MutexSem != NIL_RTSEMFASTMUTEX)
588 {
589 RTSemFastMutexRelease(MutexSem);
590 int rc2 = RTSemFastMutexDestroy(MutexSem);
591 AssertRC(rc2);
592 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
593 rc = rc2;
594 }
595
596 RTMemFree(pLogger);
597
598 return rc;
599}
600
601
602/**
603 * Create a logger instance clone for RC usage.
604 *
605 * @returns iprt status code.
606 *
607 * @param pLogger The logger instance to be cloned.
608 * @param pLoggerGC Where to create the GC logger instance.
609 * @param cbLoggerGC Amount of memory allocated to for the GC logger instance clone.
610 * @param pfnLoggerGCPtr Pointer to logger wrapper function for this instance (GC Ptr).
611 * @param pfnFlushGCPtr Pointer to flush function (GC Ptr).
612 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
613 */
614RTDECL(int) RTLogCloneRC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerGC, size_t cbLoggerGC,
615 RTRCPTR pfnLoggerGCPtr, RTRCPTR pfnFlushGCPtr, RTUINT fFlags)
616{
617 /*
618 * Validate input.
619 */
620 if ( !pLoggerGC
621 || !pfnFlushGCPtr
622 || !pfnLoggerGCPtr)
623 {
624 AssertMsgFailed(("Invalid parameters!\n"));
625 return VERR_INVALID_PARAMETER;
626 }
627 if (cbLoggerGC < sizeof(*pLoggerGC))
628 {
629 AssertMsgFailed(("%d min=%d\n", cbLoggerGC, sizeof(*pLoggerGC)));
630 return VERR_INVALID_PARAMETER;
631 }
632
633 /*
634 * Initialize GC instance.
635 */
636 pLoggerGC->offScratch = 0;
637 pLoggerGC->fPendingPrefix = false;
638 pLoggerGC->pfnLogger = pfnLoggerGCPtr;
639 pLoggerGC->pfnFlush = pfnFlushGCPtr;
640 pLoggerGC->u32Magic = RTLOGGERRC_MAGIC;
641 pLoggerGC->fFlags = fFlags | RTLOGFLAGS_DISABLED;
642 pLoggerGC->cGroups = 1;
643 pLoggerGC->afGroups[0] = 0;
644
645 /*
646 * Resolve defaults.
647 */
648 if (!pLogger)
649 {
650 pLogger = RTLogDefaultInstance();
651 if (!pLogger)
652 return VINF_SUCCESS;
653 }
654
655 /*
656 * Check if there's enough space for the groups.
657 */
658 if (cbLoggerGC < (size_t)RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]))
659 {
660 AssertMsgFailed(("%d req=%d cGroups=%d\n", cbLoggerGC, RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]), pLogger->cGroups));
661 return VERR_INVALID_PARAMETER;
662 }
663 memcpy(&pLoggerGC->afGroups[0], &pLogger->afGroups[0], pLogger->cGroups * sizeof(pLoggerGC->afGroups[0]));
664 pLoggerGC->cGroups = pLogger->cGroups;
665
666 /*
667 * Copy bits from the HC instance.
668 */
669 pLoggerGC->fPendingPrefix = pLogger->fPendingPrefix;
670 pLoggerGC->fFlags |= pLogger->fFlags;
671
672 /*
673 * Check if we can remove the disabled flag.
674 */
675 if ( pLogger->fDestFlags
676 && !((pLogger->fFlags | fFlags) & RTLOGFLAGS_DISABLED))
677 pLoggerGC->fFlags &= ~RTLOGFLAGS_DISABLED;
678
679 return VINF_SUCCESS;
680}
681
682
683/**
684 * Flushes a GC logger instance to a HC logger.
685 *
686 *
687 * @returns iprt status code.
688 * @param pLogger The HC logger instance to flush pLoggerGC to.
689 * If NULL the default logger is used.
690 * @param pLoggerGC The GC logger instance to flush.
691 */
692RTDECL(void) RTLogFlushGC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerGC)
693{
694 /*
695 * Resolve defaults.
696 */
697 if (!pLogger)
698 {
699 pLogger = RTLogDefaultInstance();
700 if (!pLogger)
701 {
702 pLoggerGC->offScratch = 0;
703 return;
704 }
705 }
706
707 /*
708 * Any thing to flush?
709 */
710 if ( pLogger->offScratch
711 || pLoggerGC->offScratch)
712 {
713 /*
714 * Acquire logger instance sem.
715 */
716 int rc = rtlogLock(pLogger);
717 if (RT_FAILURE(rc))
718 return;
719
720 /*
721 * Write whatever the GC instance contains to the HC one, and then
722 * flush the HC instance.
723 */
724 if (pLoggerGC->offScratch)
725 {
726 rtLogOutput(pLogger, pLoggerGC->achScratch, pLoggerGC->offScratch);
727 rtLogOutput(pLogger, NULL, 0);
728 pLoggerGC->offScratch = 0;
729 }
730
731 /*
732 * Release the semaphore.
733 */
734 rtlogUnlock(pLogger);
735 }
736}
737
738
739#ifdef IN_RING3
740/**
741 * Create a logger instance for singled threaded ring-0 usage.
742 *
743 * @returns iprt status code.
744 *
745 * @param pLogger Where to create the logger instance.
746 * @param cbLogger The amount of memory available for the logger instance.
747 * @param pfnLogger Pointer to logger wrapper function for the clone.
748 * @param pfnFlush Pointer to flush function for the clone.
749 * @param fFlags Logger instance flags for the clone, a combination of the RTLOGFLAGS_* values.
750 * @param fDestFlags The destination flags.
751 */
752RTDECL(int) RTLogCreateForR0(PRTLOGGER pLogger, size_t cbLogger, PFNRTLOGGER pfnLogger, PFNRTLOGFLUSH pfnFlush, RTUINT fFlags, RTUINT fDestFlags)
753{
754 /*
755 * Validate input.
756 */
757 AssertPtrReturn(pLogger, VERR_INVALID_PARAMETER);
758 AssertReturn(cbLogger >= sizeof(*pLogger), VERR_INVALID_PARAMETER);
759 AssertReturn(pfnLogger, VERR_INVALID_PARAMETER);
760 AssertReturn(pfnFlush, VERR_INVALID_PARAMETER);
761
762 /*
763 * Initialize the ring-0 instance.
764 */
765 pLogger->offScratch = 0;
766 pLogger->fPendingPrefix = false;
767 pLogger->pfnLogger = pfnLogger;
768 pLogger->pfnFlush = pfnFlush;
769 pLogger->MutexSem = NIL_RTSEMFASTMUTEX; /* Not serialized. */
770 pLogger->u32Magic = RTLOGGER_MAGIC;
771 pLogger->fFlags = fFlags;
772 pLogger->fDestFlags = fDestFlags & ~RTLOGDEST_FILE;
773 pLogger->File = NIL_RTFILE;
774 pLogger->pszFilename = NULL;
775 pLogger->papszGroups = NULL;
776 pLogger->cMaxGroups = (cbLogger - RT_OFFSETOF(RTLOGGER, afGroups[0])) / sizeof(pLogger->afGroups[0]);
777 pLogger->cGroups = 1;
778 pLogger->afGroups[0] = 0;
779 return VINF_SUCCESS;
780}
781#endif /* IN_RING3 */
782
783
784/**
785 * Copies the group settings and flags from logger instance to another.
786 *
787 * @returns IPRT status code.
788 * @param pDstLogger The destination logger instance.
789 * @param pSrcLogger The source logger instance. If NULL the default one is used.
790 * @param fFlagsOr OR mask for the flags.
791 * @param fFlagsAnd AND mask for the flags.
792 */
793RTDECL(int) RTLogCopyGroupsAndFlags(PRTLOGGER pDstLogger, PCRTLOGGER pSrcLogger, unsigned fFlagsOr, unsigned fFlagsAnd)
794{
795 int rc;
796 unsigned cGroups;
797
798 /*
799 * Validate input.
800 */
801 AssertPtrReturn(pDstLogger, VERR_INVALID_PARAMETER);
802 AssertPtrNullReturn(pSrcLogger, VERR_INVALID_PARAMETER);
803
804 /*
805 * Resolve defaults.
806 */
807 if (!pSrcLogger)
808 {
809 pSrcLogger = RTLogDefaultInstance();
810 if (!pSrcLogger)
811 {
812 pDstLogger->fFlags |= RTLOGFLAGS_DISABLED;
813 pDstLogger->cGroups = 1;
814 pDstLogger->afGroups[0] = 0;
815 return VINF_SUCCESS;
816 }
817 }
818
819 /*
820 * Copy flags and group settings.
821 */
822 pDstLogger->fFlags = (pSrcLogger->fFlags & fFlagsAnd) | fFlagsOr;
823
824 rc = VINF_SUCCESS;
825 cGroups = pSrcLogger->cGroups;
826 if (cGroups < pDstLogger->cMaxGroups)
827 {
828 AssertMsgFailed(("cMaxGroups=%zd cGroups=%zd (min size %d)\n", pDstLogger->cMaxGroups,
829 pSrcLogger->cGroups, RT_OFFSETOF(RTLOGGER, afGroups[pSrcLogger->cGroups])));
830 rc = VERR_INVALID_PARAMETER;
831 cGroups = pDstLogger->cMaxGroups;
832 }
833 memcpy(&pDstLogger->afGroups[0], &pSrcLogger->afGroups[0], cGroups * sizeof(pDstLogger->afGroups[0]));
834 pDstLogger->cGroups = cGroups;
835
836 return rc;
837}
838
839
840/**
841 * Flushes the buffer in one logger instance onto another logger.
842 *
843 * @returns iprt status code.
844 *
845 * @param pSrcLogger The logger instance to flush.
846 * @param pDstLogger The logger instance to flush onto.
847 * If NULL the default logger will be used.
848 */
849RTDECL(void) RTLogFlushToLogger(PRTLOGGER pSrcLogger, PRTLOGGER pDstLogger)
850{
851 /*
852 * Resolve defaults.
853 */
854 if (!pDstLogger)
855 {
856 pDstLogger = RTLogDefaultInstance();
857 if (!pDstLogger)
858 {
859 /* flushing to "/dev/null". */
860 if (pSrcLogger->offScratch)
861 {
862 int rc = rtlogLock(pSrcLogger);
863 if (RT_SUCCESS(rc))
864 {
865 pSrcLogger->offScratch = 0;
866 rtlogLock(pSrcLogger);
867 }
868 }
869 return;
870 }
871 }
872
873 /*
874 * Any thing to flush?
875 */
876 if ( pSrcLogger->offScratch
877 || pDstLogger->offScratch)
878 {
879 /*
880 * Acquire logger semaphores.
881 */
882 int rc = rtlogLock(pDstLogger);
883 if (RT_FAILURE(rc))
884 return;
885 rc = rtlogLock(pSrcLogger);
886 if (RT_SUCCESS(rc))
887 {
888 /*
889 * Write whatever the GC instance contains to the HC one, and then
890 * flush the HC instance.
891 */
892 if (pSrcLogger->offScratch)
893 {
894 rtLogOutput(pDstLogger, pSrcLogger->achScratch, pSrcLogger->offScratch);
895 rtLogOutput(pDstLogger, NULL, 0);
896 pSrcLogger->offScratch = 0;
897 }
898
899 /*
900 * Release the semaphores.
901 */
902 rtlogUnlock(pSrcLogger);
903 }
904 rtlogUnlock(pDstLogger);
905 }
906}
907
908
909/**
910 * Matches a group name with a pattern mask in an case insensitive manner (ASCII).
911 *
912 * @returns true if matching and *ppachMask set to the end of the pattern.
913 * @returns false if no match.
914 * @param pszGrp The group name.
915 * @param ppachMask Pointer to the pointer to the mask. Only wildcard supported is '*'.
916 * @param cchMask The length of the mask, including modifiers. The modifiers is why
917 * we update *ppachMask on match.
918 */
919static bool rtlogIsGroupMatching(const char *pszGrp, const char **ppachMask, unsigned cchMask)
920{
921 const char *pachMask;
922
923 if (!pszGrp || !*pszGrp)
924 return false;
925 pachMask = *ppachMask;
926 for (;;)
927 {
928 if (RT_C_TO_LOWER(*pszGrp) != RT_C_TO_LOWER(*pachMask))
929 {
930 const char *pszTmp;
931
932 /*
933 * Check for wildcard and do a minimal match if found.
934 */
935 if (*pachMask != '*')
936 return false;
937
938 /* eat '*'s. */
939 do pachMask++;
940 while (--cchMask && *pachMask == '*');
941
942 /* is there more to match? */
943 if ( !cchMask
944 || *pachMask == '.'
945 || *pachMask == '=')
946 break; /* we're good */
947
948 /* do extremely minimal matching (fixme) */
949 pszTmp = strchr(pszGrp, RT_C_TO_LOWER(*pachMask));
950 if (!pszTmp)
951 pszTmp = strchr(pszGrp, RT_C_TO_UPPER(*pachMask));
952 if (!pszTmp)
953 return false;
954 pszGrp = pszTmp;
955 continue;
956 }
957
958 /* done? */
959 if (!*++pszGrp)
960 {
961 /* trailing wildcard is ok. */
962 do
963 {
964 pachMask++;
965 cchMask--;
966 } while (cchMask && *pachMask == '*');
967 if ( !cchMask
968 || *pachMask == '.'
969 || *pachMask == '=')
970 break; /* we're good */
971 return false;
972 }
973
974 if (!--cchMask)
975 return false;
976 pachMask++;
977 }
978
979 /* match */
980 *ppachMask = pachMask;
981 return true;
982}
983
984
985/**
986 * Updates the group settings for the logger instance using the specified
987 * specification string.
988 *
989 * @returns iprt status code.
990 * Failures can safely be ignored.
991 * @param pLogger Logger instance.
992 * @param pszVar Value to parse.
993 */
994RTDECL(int) RTLogGroupSettings(PRTLOGGER pLogger, const char *pszVar)
995{
996 /*
997 * Resolve defaults.
998 */
999 if (!pLogger)
1000 {
1001 pLogger = RTLogDefaultInstance();
1002 if (!pLogger)
1003 return VINF_SUCCESS;
1004 }
1005
1006 /*
1007 * Iterate the string.
1008 */
1009 while (*pszVar)
1010 {
1011 /*
1012 * Skip prefixes (blanks, ;, + and -).
1013 */
1014 bool fEnabled = true;
1015 char ch;
1016 const char *pszStart;
1017 unsigned i;
1018 size_t cch;
1019
1020 while ((ch = *pszVar) == '+' || ch == '-' || ch == ' ' || ch == '\t' || ch == '\n' || ch == ';')
1021 {
1022 if (ch == '+' || ch == '-' || ';')
1023 fEnabled = ch != '-';
1024 pszVar++;
1025 }
1026 if (!*pszVar)
1027 break;
1028
1029 /*
1030 * Find end.
1031 */
1032 pszStart = pszVar;
1033 while ((ch = *pszVar) != '\0' && ch != '+' && ch != '-' && ch != ' ' && ch != '\t')
1034 pszVar++;
1035
1036 /*
1037 * Find the group (ascii case insensitive search).
1038 * Special group 'all'.
1039 */
1040 cch = pszVar - pszStart;
1041 if ( cch >= 3
1042 && (pszStart[0] == 'a' || pszStart[0] == 'A')
1043 && (pszStart[1] == 'l' || pszStart[1] == 'L')
1044 && (pszStart[2] == 'l' || pszStart[2] == 'L')
1045 && (cch == 3 || pszStart[3] == '.' || pszStart[3] == '='))
1046 {
1047 /*
1048 * All.
1049 */
1050 unsigned fFlags = cch == 3
1051 ? RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1
1052 : rtlogGroupFlags(&pszStart[3]);
1053 for (i = 0; i < pLogger->cGroups; i++)
1054 {
1055 if (fEnabled)
1056 pLogger->afGroups[i] |= fFlags;
1057 else
1058 pLogger->afGroups[i] &= ~fFlags;
1059 }
1060 }
1061 else
1062 {
1063 /*
1064 * Specific group(s).
1065 */
1066 for (i = 0; i < pLogger->cGroups; i++)
1067 {
1068 const char *psz2 = (const char*)pszStart;
1069 if (rtlogIsGroupMatching(pLogger->papszGroups[i], &psz2, cch))
1070 {
1071 unsigned fFlags = RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1;
1072 if (*psz2 == '.' || *psz2 == '=')
1073 fFlags = rtlogGroupFlags(psz2);
1074 if (fEnabled)
1075 pLogger->afGroups[i] |= fFlags;
1076 else
1077 pLogger->afGroups[i] &= ~fFlags;
1078 }
1079 } /* for each group */
1080 }
1081
1082 } /* parse specification */
1083
1084 return VINF_SUCCESS;
1085}
1086
1087
1088/**
1089 * Interprets the group flags suffix.
1090 *
1091 * @returns Flags specified. (0 is possible!)
1092 * @param psz Start of Suffix. (Either dot or equal sign.)
1093 */
1094static unsigned rtlogGroupFlags(const char *psz)
1095{
1096 unsigned fFlags = 0;
1097
1098 /*
1099 * Litteral flags.
1100 */
1101 while (*psz == '.')
1102 {
1103 static struct
1104 {
1105 const char *pszFlag; /* lowercase!! */
1106 unsigned fFlag;
1107 } aFlags[] =
1108 {
1109 { "eo", RTLOGGRPFLAGS_ENABLED },
1110 { "enabledonly",RTLOGGRPFLAGS_ENABLED },
1111 { "e", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },
1112 { "enabled", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },
1113 { "l1", RTLOGGRPFLAGS_LEVEL_1 },
1114 { "level1", RTLOGGRPFLAGS_LEVEL_1 },
1115 { "l", RTLOGGRPFLAGS_LEVEL_2 },
1116 { "l2", RTLOGGRPFLAGS_LEVEL_2 },
1117 { "level2", RTLOGGRPFLAGS_LEVEL_2 },
1118 { "l3", RTLOGGRPFLAGS_LEVEL_3 },
1119 { "level3", RTLOGGRPFLAGS_LEVEL_3 },
1120 { "l4", RTLOGGRPFLAGS_LEVEL_4 },
1121 { "level4", RTLOGGRPFLAGS_LEVEL_4 },
1122 { "l5", RTLOGGRPFLAGS_LEVEL_5 },
1123 { "level5", RTLOGGRPFLAGS_LEVEL_5 },
1124 { "l6", RTLOGGRPFLAGS_LEVEL_6 },
1125 { "level6", RTLOGGRPFLAGS_LEVEL_6 },
1126 { "f", RTLOGGRPFLAGS_FLOW },
1127 { "flow", RTLOGGRPFLAGS_FLOW },
1128
1129 { "lelik", RTLOGGRPFLAGS_LELIK },
1130 { "michael", RTLOGGRPFLAGS_MICHAEL },
1131 { "dmik", RTLOGGRPFLAGS_DMIK },
1132 { "sunlover", RTLOGGRPFLAGS_SUNLOVER },
1133 { "achim", RTLOGGRPFLAGS_ACHIM },
1134 { "achimha", RTLOGGRPFLAGS_ACHIM },
1135 { "s", RTLOGGRPFLAGS_SANDER },
1136 { "sander", RTLOGGRPFLAGS_SANDER },
1137 { "sandervl", RTLOGGRPFLAGS_SANDER },
1138 { "klaus", RTLOGGRPFLAGS_KLAUS },
1139 { "frank", RTLOGGRPFLAGS_FRANK },
1140 { "b", RTLOGGRPFLAGS_BIRD },
1141 { "bird", RTLOGGRPFLAGS_BIRD },
1142 { "aleksey", RTLOGGRPFLAGS_ALEKSEY },
1143 { "n", RTLOGGRPFLAGS_NONAME },
1144 { "noname", RTLOGGRPFLAGS_NONAME }
1145 };
1146 unsigned i;
1147 bool fFound = false;
1148 psz++;
1149 for (i = 0; i < ELEMENTS(aFlags) && !fFound; i++)
1150 {
1151 const char *psz1 = aFlags[i].pszFlag;
1152 const char *psz2 = psz;
1153 while (*psz1 == RT_C_TO_LOWER(*psz2))
1154 {
1155 psz1++;
1156 psz2++;
1157 if (!*psz1)
1158 {
1159 if ( (*psz2 >= 'a' && *psz2 <= 'z')
1160 || (*psz2 >= 'A' && *psz2 <= 'Z')
1161 || (*psz2 >= '0' && *psz2 <= '9') )
1162 break;
1163 fFlags |= aFlags[i].fFlag;
1164 fFound = true;
1165 psz = psz2;
1166 break;
1167 }
1168 } /* strincmp */
1169 } /* for each flags */
1170 }
1171
1172 /*
1173 * Flag value.
1174 */
1175 if (*psz == '=')
1176 {
1177 psz++;
1178 if (*psz == '~')
1179 fFlags = ~RTStrToInt32(psz + 1);
1180 else
1181 fFlags = RTStrToInt32(psz);
1182 }
1183
1184 return fFlags;
1185}
1186
1187#endif /* !IN_GC */
1188
1189
1190/**
1191 * Updates the flags for the logger instance using the specified
1192 * specification string.
1193 *
1194 * @returns iprt status code.
1195 * Failures can safely be ignored.
1196 * @param pLogger Logger instance (NULL for default logger).
1197 * @param pszVar Value to parse.
1198 */
1199RTDECL(int) RTLogFlags(PRTLOGGER pLogger, const char *pszVar)
1200{
1201 int rc = VINF_SUCCESS;
1202
1203 /*
1204 * Resolve defaults.
1205 */
1206 if (!pLogger)
1207 {
1208 pLogger = RTLogDefaultInstance();
1209 if (!pLogger)
1210 return VINF_SUCCESS;
1211 }
1212
1213 /*
1214 * Iterate the string.
1215 */
1216 while (*pszVar)
1217 {
1218 /* parse instruction. */
1219 static struct
1220 {
1221 const char *pszInstr;
1222 size_t cchInstr;
1223 unsigned fFlag;
1224 bool fInverted;
1225 } const aDest[] =
1226 {
1227 { "disabled", sizeof("disabled" ) - 1, RTLOGFLAGS_DISABLED, false },
1228 { "enabled", sizeof("enabled" ) - 1, RTLOGFLAGS_DISABLED, true },
1229 { "buffered", sizeof("buffered" ) - 1, RTLOGFLAGS_BUFFERED, false },
1230 { "unbuffered", sizeof("unbuffered" ) - 1, RTLOGFLAGS_BUFFERED, true },
1231 { "usecrlf", sizeof("usecrlf" ) - 1, RTLOGFLAGS_USECRLF, true },
1232 { "uself", sizeof("uself" ) - 1, RTLOGFLAGS_USECRLF, false },
1233 { "rel", sizeof("rel" ) - 1, RTLOGFLAGS_REL_TS, false },
1234 { "abs", sizeof("abs" ) - 1, RTLOGFLAGS_REL_TS, true },
1235 { "dec", sizeof("dec" ) - 1, RTLOGFLAGS_DECIMAL_TS, false },
1236 { "hex", sizeof("hex" ) - 1, RTLOGFLAGS_DECIMAL_TS, true },
1237 { "lockcnts", sizeof("lockcnts" ) - 1, RTLOGFLAGS_PREFIX_LOCK_COUNTS, false },
1238 { "cpuid", sizeof("cpuid" ) - 1, RTLOGFLAGS_PREFIX_CPUID, false },
1239 { "pid", sizeof("pid" ) - 1, RTLOGFLAGS_PREFIX_PID, false },
1240 { "flagno", sizeof("flagno" ) - 1, RTLOGFLAGS_PREFIX_FLAG_NO, false },
1241 { "flag", sizeof("flag" ) - 1, RTLOGFLAGS_PREFIX_FLAG, false },
1242 { "groupno", sizeof("groupno" ) - 1, RTLOGFLAGS_PREFIX_GROUP_NO, false },
1243 { "group", sizeof("group" ) - 1, RTLOGFLAGS_PREFIX_GROUP, false },
1244 { "tid", sizeof("tid" ) - 1, RTLOGFLAGS_PREFIX_TID, false },
1245 { "thread", sizeof("thread" ) - 1, RTLOGFLAGS_PREFIX_THREAD, false },
1246 { "timeprog", sizeof("timeprog" ) - 1, RTLOGFLAGS_PREFIX_TIME_PROG, false },
1247 { "time", sizeof("time" ) - 1, RTLOGFLAGS_PREFIX_TIME, false },
1248 { "msprog", sizeof("msprog" ) - 1, RTLOGFLAGS_PREFIX_MS_PROG, false },
1249 { "tsc", sizeof("tsc" ) - 1, RTLOGFLAGS_PREFIX_TSC, false }, /* before ts! */
1250 { "ts", sizeof("ts" ) - 1, RTLOGFLAGS_PREFIX_TS, false },
1251 };
1252
1253 /* check no prefix. */
1254 bool fNo = false;
1255 char ch;
1256 unsigned i;
1257
1258 /* skip blanks. */
1259 while (RT_C_IS_SPACE(*pszVar))
1260 pszVar++;
1261 if (!*pszVar)
1262 return rc;
1263
1264 while ((ch = *pszVar) != '\0')
1265 {
1266 if (ch == 'n' && pszVar[1] == 'o')
1267 {
1268 pszVar += 2;
1269 fNo = !fNo;
1270 }
1271 else if (ch == '+')
1272 {
1273 pszVar++;
1274 fNo = true;
1275 }
1276 else if (ch == '-' || ch == '!' || ch == '~')
1277 {
1278 pszVar++;
1279 fNo = !fNo;
1280 }
1281 else
1282 break;
1283 }
1284
1285 /* instruction. */
1286 for (i = 0; i < ELEMENTS(aDest); i++)
1287 {
1288 if (!strncmp(pszVar, aDest[i].pszInstr, aDest[i].cchInstr))
1289 {
1290 if (fNo == aDest[i].fInverted)
1291 pLogger->fFlags |= aDest[i].fFlag;
1292 else
1293 pLogger->fFlags &= ~aDest[i].fFlag;
1294 pszVar += aDest[i].cchInstr;
1295 break;
1296 }
1297 }
1298
1299 /* unknown instruction? */
1300 if (i >= ELEMENTS(aDest))
1301 {
1302 AssertMsgFailed(("Invalid flags! unknown instruction %.20s\n", pszVar));
1303 pszVar++;
1304 }
1305
1306 /* skip blanks and delimiters. */
1307 while (RT_C_IS_SPACE(*pszVar) || *pszVar == ';')
1308 pszVar++;
1309 } /* while more environment variable value left */
1310
1311 return rc;
1312}
1313
1314
1315/**
1316 * Flushes the specified logger.
1317 *
1318 * @param pLogger The logger instance to flush.
1319 * If NULL the default instance is used. The default instance
1320 * will not be initialized by this call.
1321 */
1322RTDECL(void) RTLogFlush(PRTLOGGER pLogger)
1323{
1324 /*
1325 * Resolve defaults.
1326 */
1327 if (!pLogger)
1328 {
1329#ifdef IN_GC
1330 pLogger = &g_Logger;
1331#else
1332 pLogger = g_pLogger;
1333#endif
1334 if (!pLogger)
1335 return;
1336 }
1337
1338 /*
1339 * Any thing to flush?
1340 */
1341 if (pLogger->offScratch)
1342 {
1343#ifndef IN_GC
1344 /*
1345 * Acquire logger instance sem.
1346 */
1347 int rc = rtlogLock(pLogger);
1348 if (RT_FAILURE(rc))
1349 return;
1350#endif
1351 /*
1352 * Call worker.
1353 */
1354 rtlogFlush(pLogger);
1355
1356#ifndef IN_GC
1357 /*
1358 * Release the semaphore.
1359 */
1360 rtlogUnlock(pLogger);
1361#endif
1362 }
1363}
1364
1365
1366/**
1367 * Gets the default logger instance.
1368 *
1369 * @returns Pointer to default logger instance.
1370 * @returns NULL if no default logger instance available.
1371 */
1372RTDECL(PRTLOGGER) RTLogDefaultInstance(void)
1373{
1374#ifdef IN_GC
1375 return &g_Logger;
1376
1377#else /* !IN_GC */
1378# ifdef IN_RING0
1379 /*
1380 * Check per thread loggers first.
1381 */
1382 if (g_cPerThreadLoggers)
1383 {
1384 const RTNATIVETHREAD Self = RTThreadNativeSelf();
1385 int32_t i = ELEMENTS(g_aPerThreadLoggers);
1386 while (i-- > 0)
1387 if (g_aPerThreadLoggers[i].NativeThread == Self)
1388 return g_aPerThreadLoggers[i].pLogger;
1389 }
1390# endif /* IN_RING0 */
1391
1392 /*
1393 * If no per thread logger, use the default one.
1394 */
1395 if (!g_pLogger)
1396 g_pLogger = RTLogDefaultInit();
1397 return g_pLogger;
1398#endif /* !IN_GC */
1399}
1400
1401
1402#ifndef IN_GC
1403/**
1404 * Sets the default logger instance.
1405 *
1406 * @returns iprt status code.
1407 * @param pLogger The new default logger instance.
1408 */
1409RTDECL(PRTLOGGER) RTLogSetDefaultInstance(PRTLOGGER pLogger)
1410{
1411 return (PRTLOGGER)ASMAtomicXchgPtr((void * volatile *)&g_pLogger, pLogger);
1412}
1413#endif /* !IN_GC */
1414
1415
1416#ifdef IN_RING0
1417/**
1418 * Changes the default logger instance for the current thread.
1419 *
1420 * @returns IPRT status code.
1421 * @param pLogger The logger instance. Pass NULL for deregistration.
1422 * @param uKey Associated key for cleanup purposes. If pLogger is NULL,
1423 * all instances with this key will be deregistered. So in
1424 * order to only deregister the instance associated with the
1425 * current thread use 0.
1426 */
1427RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey)
1428{
1429 int rc;
1430 RTNATIVETHREAD Self = RTThreadNativeSelf();
1431 if (pLogger)
1432 {
1433 int32_t i;
1434 unsigned j;
1435
1436 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
1437
1438 /*
1439 * Iterate the table to see if there is already an entry for this thread.
1440 */
1441 i = ELEMENTS(g_aPerThreadLoggers);
1442 while (i-- > 0)
1443 if (g_aPerThreadLoggers[i].NativeThread == Self)
1444 {
1445 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
1446 g_aPerThreadLoggers[i].pLogger = pLogger;
1447 return VINF_SUCCESS;
1448 }
1449
1450 /*
1451 * Allocate a new table entry.
1452 */
1453 i = ASMAtomicIncS32(&g_cPerThreadLoggers);
1454 if (i > (int32_t)ELEMENTS(g_aPerThreadLoggers))
1455 {
1456 ASMAtomicDecS32(&g_cPerThreadLoggers);
1457 return VERR_BUFFER_OVERFLOW; /* horrible error code! */
1458 }
1459
1460 for (j = 0; j < 10; j++)
1461 {
1462 i = ELEMENTS(g_aPerThreadLoggers);
1463 while (i-- > 0)
1464 {
1465 AssertCompile(sizeof(RTNATIVETHREAD) == sizeof(void*));
1466 if ( g_aPerThreadLoggers[i].NativeThread == NIL_RTNATIVETHREAD
1467 && ASMAtomicCmpXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)Self, (void *)NIL_RTNATIVETHREAD))
1468 {
1469 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
1470 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, pLogger);
1471 return VINF_SUCCESS;
1472 }
1473 }
1474 }
1475
1476 ASMAtomicDecS32(&g_cPerThreadLoggers);
1477 rc = VERR_INTERNAL_ERROR;
1478 }
1479 else
1480 {
1481 /*
1482 * Search the array for the current thread.
1483 */
1484 int32_t i = ELEMENTS(g_aPerThreadLoggers);
1485 while (i-- > 0)
1486 if ( g_aPerThreadLoggers[i].NativeThread == Self
1487 || g_aPerThreadLoggers[i].uKey == uKey)
1488 {
1489 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, NULL);
1490 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, NULL);
1491 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)NIL_RTNATIVETHREAD);
1492 ASMAtomicDecS32(&g_cPerThreadLoggers);
1493 }
1494
1495 rc = VINF_SUCCESS;
1496 }
1497 return rc;
1498}
1499#endif
1500
1501
1502/**
1503 * Gets the default release logger instance.
1504 *
1505 * @returns Pointer to default release logger instance.
1506 * @returns NULL if no default release logger instance available.
1507 */
1508RTDECL(PRTLOGGER) RTLogRelDefaultInstance(void)
1509{
1510#ifdef IN_GC
1511 return &g_RelLogger;
1512#else /* !IN_GC */
1513 return g_pRelLogger;
1514#endif /* !IN_GC */
1515}
1516
1517
1518#ifndef IN_GC
1519/**
1520 * Sets the default logger instance.
1521 *
1522 * @returns iprt status code.
1523 * @param pLogger The new default release logger instance.
1524 */
1525RTDECL(PRTLOGGER) RTLogRelSetDefaultInstance(PRTLOGGER pLogger)
1526{
1527 return (PRTLOGGER)ASMAtomicXchgPtr((void * volatile *)&g_pRelLogger, pLogger);
1528}
1529#endif /* !IN_GC */
1530
1531
1532/**
1533 * Write to a logger instance.
1534 *
1535 * @param pLogger Pointer to logger instance.
1536 * @param pvCallerRet Ignored.
1537 * @param pszFormat Format string.
1538 * @param ... Format arguments.
1539 */
1540RTDECL(void) RTLogLogger(PRTLOGGER pLogger, void *pvCallerRet, const char *pszFormat, ...)
1541{
1542 va_list args;
1543 va_start(args, pszFormat);
1544#if defined(RT_OS_DARWIN) && defined(RT_ARCH_X86) && defined(IN_RING3)
1545 /* manually align the stack before doing the call.
1546 * We boldly assume that there is a stack frame here! */
1547 __asm__ __volatile__("andl $-32, %%esp\t\n" ::: "%esp");
1548 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
1549#else
1550 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
1551#endif
1552 va_end(args);
1553}
1554
1555
1556/**
1557 * Write to a logger instance.
1558 *
1559 * @param pLogger Pointer to logger instance.
1560 * @param pszFormat Format string.
1561 * @param args Format arguments.
1562 */
1563RTDECL(void) RTLogLoggerV(PRTLOGGER pLogger, const char *pszFormat, va_list args)
1564{
1565 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
1566}
1567
1568
1569/**
1570 * Write to a logger instance.
1571 *
1572 * This function will check whether the instance, group and flags makes up a
1573 * logging kind which is currently enabled before writing anything to the log.
1574 *
1575 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.
1576 * @param fFlags The logging flags.
1577 * @param iGroup The group.
1578 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1579 * only for internal usage!
1580 * @param pszFormat Format string.
1581 * @param ... Format arguments.
1582 * @remark This is a worker function of LogIt.
1583 */
1584RTDECL(void) RTLogLoggerEx(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
1585{
1586 va_list args;
1587 va_start(args, pszFormat);
1588 RTLogLoggerExV(pLogger, fFlags, iGroup, pszFormat, args);
1589 va_end(args);
1590}
1591
1592
1593/**
1594 * Write to a logger instance.
1595 *
1596 * This function will check whether the instance, group and flags makes up a
1597 * logging kind which is currently enabled before writing anything to the log.
1598 *
1599 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.
1600 * @param fFlags The logging flags.
1601 * @param iGroup The group.
1602 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1603 * only for internal usage!
1604 * @param pszFormat Format string.
1605 * @param args Format arguments.
1606 */
1607RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
1608{
1609 /*
1610 * A NULL logger means default instance.
1611 */
1612 if (!pLogger)
1613 {
1614 pLogger = RTLogDefaultInstance();
1615 if (!pLogger)
1616 return;
1617 }
1618 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);
1619}
1620
1621
1622/**
1623 * Write to a logger instance, defaulting to the release one.
1624 *
1625 * This function will check whether the instance, group and flags makes up a
1626 * logging kind which is currently enabled before writing anything to the log.
1627 *
1628 * @param pLogger Pointer to logger instance.
1629 * @param fFlags The logging flags.
1630 * @param iGroup The group.
1631 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1632 * only for internal usage!
1633 * @param pszFormat Format string.
1634 * @param ... Format arguments.
1635 * @remark This is a worker function for LogRelIt.
1636 */
1637RTDECL(void) RTLogRelLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
1638{
1639 va_list args;
1640 va_start(args, pszFormat);
1641 RTLogRelLoggerV(pLogger, fFlags, iGroup, pszFormat, args);
1642 va_end(args);
1643}
1644
1645
1646/**
1647 * Write to a logger instance, defaulting to the release one.
1648 *
1649 * This function will check whether the instance, group and flags makes up a
1650 * logging kind which is currently enabled before writing anything to the log.
1651 *
1652 * @param pLogger Pointer to logger instance. If NULL the default release instance is attempted.
1653 * @param fFlags The logging flags.
1654 * @param iGroup The group.
1655 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1656 * only for internal usage!
1657 * @param pszFormat Format string.
1658 * @param args Format arguments.
1659 */
1660RTDECL(void) RTLogRelLoggerV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
1661{
1662 /*
1663 * A NULL logger means default instance.
1664 */
1665 if (!pLogger)
1666 {
1667 pLogger = RTLogRelDefaultInstance();
1668 if (!pLogger)
1669 return;
1670 }
1671 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);
1672}
1673
1674
1675/**
1676 * Worker for the RTLog[Rel]Logger*() functions.
1677 *
1678 * @param pLogger Pointer to logger instance.
1679 * @param fFlags The logging flags.
1680 * @param iGroup The group.
1681 * The value ~0U is reserved for compatability with RTLogLogger[V] and is
1682 * only for internal usage!
1683 * @param pszFormat Format string.
1684 * @param args Format arguments.
1685 */
1686static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
1687{
1688 int rc;
1689
1690 /*
1691 * Validate and correct iGroup.
1692 */
1693 if (iGroup != ~0U && iGroup >= pLogger->cGroups)
1694 iGroup = 0;
1695
1696 /*
1697 * If no output, then just skip it.
1698 */
1699 if ( (pLogger->fFlags & RTLOGFLAGS_DISABLED)
1700#ifndef IN_GC
1701 || !pLogger->fDestFlags
1702#endif
1703 || !pszFormat || !*pszFormat)
1704 return;
1705 if ( iGroup != ~0U
1706 && (pLogger->afGroups[iGroup] & (fFlags | RTLOGGRPFLAGS_ENABLED)) != (fFlags | RTLOGGRPFLAGS_ENABLED))
1707 return;
1708
1709 /*
1710 * Acquire logger instance sem.
1711 */
1712 rc = rtlogLock(pLogger);
1713 if (RT_FAILURE(rc))
1714 return;
1715
1716 /*
1717 * Format the message and perhaps flush it.
1718 */
1719 if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF))
1720 {
1721 RTLOGOUTPUTPREFIXEDARGS OutputArgs;
1722 OutputArgs.pLogger = pLogger;
1723 OutputArgs.iGroup = iGroup;
1724 OutputArgs.fFlags = fFlags;
1725 RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);
1726 }
1727 else
1728 RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);
1729 if ( !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)
1730 && pLogger->offScratch)
1731 rtlogFlush(pLogger);
1732
1733 /*
1734 * Release the semaphore.
1735 */
1736 rtlogUnlock(pLogger);
1737}
1738
1739
1740/**
1741 * printf like function for writing to the default log.
1742 *
1743 * @param pszFormat Printf like format string.
1744 * @param ... Optional arguments as specified in pszFormat.
1745 *
1746 * @remark The API doesn't support formatting of floating point numbers at the moment.
1747 */
1748RTDECL(void) RTLogPrintf(const char *pszFormat, ...)
1749{
1750 va_list args;
1751 va_start(args, pszFormat);
1752 RTLogPrintfV(pszFormat, args);
1753 va_end(args);
1754}
1755
1756
1757/**
1758 * vprintf like function for writing to the default log.
1759 *
1760 * @param pszFormat Printf like format string.
1761 * @param args Optional arguments as specified in pszFormat.
1762 *
1763 * @remark The API doesn't support formatting of floating point numbers at the moment.
1764 */
1765RTDECL(void) RTLogPrintfV(const char *pszFormat, va_list args)
1766{
1767 RTLogLoggerV(NULL, pszFormat, args);
1768}
1769
1770
1771/**
1772 * printf like function for writing to the default release log.
1773 *
1774 * @param pszFormat Printf like format string.
1775 * @param ... Optional arguments as specified in pszFormat.
1776 *
1777 * @remark The API doesn't support formatting of floating point numbers at the moment.
1778 */
1779RTDECL(void) RTLogRelPrintf(const char *pszFormat, ...)
1780{
1781 va_list args;
1782 va_start(args, pszFormat);
1783 RTLogRelPrintfV(pszFormat, args);
1784 va_end(args);
1785}
1786
1787
1788/**
1789 * vprintf like function for writing to the default release log.
1790 *
1791 * @param pszFormat Printf like format string.
1792 * @param args Optional arguments as specified in pszFormat.
1793 *
1794 * @remark The API doesn't support formatting of floating point numbers at the moment.
1795 */
1796RTDECL(void) RTLogRelPrintfV(const char *pszFormat, va_list args)
1797{
1798 RTLogRelLoggerV(NULL, 0, ~0U, pszFormat, args);
1799}
1800
1801
1802/**
1803 * Writes the buffer to the given log device without checking for buffered
1804 * data or anything.
1805 * Used by the RTLogFlush() function.
1806 *
1807 * @param pLogger The logger instance to write to. NULL is not allowed!
1808 */
1809static void rtlogFlush(PRTLOGGER pLogger)
1810{
1811#ifndef IN_GC
1812 if (pLogger->fDestFlags & RTLOGDEST_USER)
1813 RTLogWriteUser(pLogger->achScratch, pLogger->offScratch);
1814
1815 if (pLogger->fDestFlags & RTLOGDEST_DEBUGGER)
1816 RTLogWriteDebugger(pLogger->achScratch, pLogger->offScratch);
1817
1818# ifdef IN_RING3
1819 if (pLogger->fDestFlags & RTLOGDEST_FILE)
1820 RTFileWrite(pLogger->File, pLogger->achScratch, pLogger->offScratch, NULL);
1821# endif
1822
1823 if (pLogger->fDestFlags & RTLOGDEST_STDOUT)
1824 RTLogWriteStdOut(pLogger->achScratch, pLogger->offScratch);
1825
1826 if (pLogger->fDestFlags & RTLOGDEST_STDERR)
1827 RTLogWriteStdErr(pLogger->achScratch, pLogger->offScratch);
1828
1829# if (defined(IN_RING0) || defined(IN_GC)) && !defined(LOG_NO_COM)
1830 if (pLogger->fDestFlags & RTLOGDEST_COM)
1831 RTLogWriteCom(pLogger->achScratch, pLogger->offScratch);
1832# endif
1833#endif /* !IN_GC */
1834
1835 if (pLogger->pfnFlush)
1836 pLogger->pfnFlush(pLogger);
1837
1838 /* empty the buffer. */
1839 pLogger->offScratch = 0;
1840}
1841
1842
1843/**
1844 * Callback for RTLogFormatV which writes to the com port.
1845 * See PFNLOGOUTPUT() for details.
1846 */
1847static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars)
1848{
1849 PRTLOGGER pLogger = (PRTLOGGER)pv;
1850 if (cbChars)
1851 {
1852 size_t cbRet = 0;
1853 for (;;)
1854 {
1855#if defined(DEBUG) && defined(IN_RING3)
1856 /* sanity */
1857 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
1858 {
1859 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
1860 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
1861 AssertBreakpoint(); AssertBreakpoint();
1862 }
1863#endif
1864
1865 /* how much */
1866 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
1867 if (cb > cbChars)
1868 cb = cbChars;
1869
1870 /* copy */
1871 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
1872
1873 /* advance */
1874 pLogger->offScratch += cb;
1875 cbRet += cb;
1876 cbChars -= cb;
1877
1878 /* done? */
1879 if (cbChars <= 0)
1880 return cbRet;
1881
1882 pachChars += cb;
1883
1884 /* flush */
1885 rtlogFlush(pLogger);
1886 }
1887
1888 /* won't ever get here! */
1889 }
1890 else
1891 {
1892 /*
1893 * Termination call.
1894 * There's always space for a terminator, and it's not counted.
1895 */
1896 pLogger->achScratch[pLogger->offScratch] = '\0';
1897 return 0;
1898 }
1899}
1900
1901
1902
1903/**
1904 * Callback for RTLogFormatV which writes to the logger instance.
1905 * This version supports prefixes.
1906 *
1907 * See PFNLOGOUTPUT() for details.
1908 */
1909static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars)
1910{
1911 PRTLOGOUTPUTPREFIXEDARGS pArgs = (PRTLOGOUTPUTPREFIXEDARGS)pv;
1912 PRTLOGGER pLogger = pArgs->pLogger;
1913 if (cbChars)
1914 {
1915 size_t cbRet = 0;
1916 for (;;)
1917 {
1918 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
1919 char *psz;
1920 const char *pszNewLine;
1921
1922 /*
1923 * Pending prefix?
1924 */
1925 if (pLogger->fPendingPrefix)
1926 {
1927 pLogger->fPendingPrefix = false;
1928
1929#if defined(DEBUG) && defined(IN_RING3)
1930 /* sanity */
1931 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
1932 {
1933 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
1934 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
1935 AssertBreakpoint(); AssertBreakpoint();
1936 }
1937#endif
1938
1939 /*
1940 * Flush the buffer if there isn't enough room for the maximum prefix config.
1941 * Max is 224, add a couple of extra bytes.
1942 */
1943 if (cb < 224 + 16)
1944 {
1945 rtlogFlush(pLogger);
1946 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
1947 }
1948
1949 /*
1950 * Write the prefixes.
1951 * psz is pointing to the current position.
1952 */
1953 psz = &pLogger->achScratch[pLogger->offScratch];
1954 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TS)
1955 {
1956#if defined(IN_RING3) || defined(IN_GC)
1957 uint64_t u64 = RTTimeNanoTS();
1958#else
1959 uint64_t u64 = ~0;
1960#endif
1961 int iBase = 16;
1962 unsigned int fFlags = RTSTR_F_ZEROPAD;
1963 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
1964 {
1965 iBase = 10;
1966 fFlags = 0;
1967 }
1968 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
1969 {
1970 static volatile uint64_t s_u64LastTs;
1971 uint64_t u64DiffTs = u64 - s_u64LastTs;
1972 s_u64LastTs = u64;
1973 /* We could have been preempted just before reading of s_u64LastTs by
1974 * another thread which wrote s_u64LastTs. In that case the difference
1975 * is negative which we simply ignore. */
1976 u64 = (int64_t)u64DiffTs < 0 ? 0 : u64DiffTs;
1977 }
1978 /* 1E15 nanoseconds = 11 days */
1979 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags); /* +17 */
1980 *psz++ = ' ';
1981 }
1982 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TSC)
1983 {
1984 uint64_t u64 = ASMReadTSC();
1985 int iBase = 16;
1986 unsigned int fFlags = RTSTR_F_ZEROPAD;
1987 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
1988 {
1989 iBase = 10;
1990 fFlags = 0;
1991 }
1992 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
1993 {
1994 static volatile uint64_t s_u64LastTsc;
1995 uint64_t u64DiffTsc = u64 - s_u64LastTsc;
1996 s_u64LastTsc = u64;
1997 /* We could have been preempted just before reading of s_u64LastTsc by
1998 * another thread which wrote s_u64LastTsc. In that case the difference
1999 * is negative which we simply ignore. */
2000 u64 = u64DiffTsc < 0 ? 0 : u64DiffTsc;
2001 }
2002 /* 1E15 ticks at 4GHz = 69 hours */
2003 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags); /* +17 */
2004 *psz++ = ' ';
2005 }
2006 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_MS_PROG)
2007 {
2008#if defined(IN_RING3) || defined(IN_GC)
2009 uint64_t u64 = RTTimeProgramMilliTS();
2010#else
2011 uint64_t u64 = 0;
2012#endif
2013 /* 1E8 milliseconds = 27 hours */
2014 psz += RTStrFormatNumber(psz, u64, 10, 9, 0, RTSTR_F_ZEROPAD);
2015 *psz++ = ' ';
2016 }
2017 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME)
2018 {
2019#ifdef IN_RING3
2020 RTTIMESPEC TimeSpec;
2021 RTTIME Time;
2022 RTTimeExplode(&Time, RTTimeNow(&TimeSpec));
2023 psz += RTStrFormatNumber(psz, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD);
2024 *psz++ = ':';
2025 psz += RTStrFormatNumber(psz, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD);
2026 *psz++ = ':';
2027 psz += RTStrFormatNumber(psz, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD);
2028 *psz++ = '.';
2029 psz += RTStrFormatNumber(psz, Time.u32Nanosecond / 1000000, 10, 3, 0, RTSTR_F_ZEROPAD);
2030 *psz++ = ' '; /* +17 (3+1+3+1+3+1+4+1) */
2031#else
2032 memset(psz, ' ', 13);
2033 psz += 13;
2034#endif
2035 }
2036 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME_PROG)
2037 {
2038#ifdef IN_RING3
2039 uint64_t u64 = RTTimeProgramMilliTS();
2040 psz += RTStrFormatNumber(psz, (uint32_t)(u64 / (60 * 60 * 1000)), 10, 2, 0, RTSTR_F_ZEROPAD);
2041 *psz++ = ':';
2042 uint32_t u32 = (uint32_t)(u64 % (60 * 60 * 1000));
2043 psz += RTStrFormatNumber(psz, u32 / (60 * 1000), 10, 2, 0, RTSTR_F_ZEROPAD);
2044 *psz++ = ':';
2045 u32 %= 60 * 1000;
2046 psz += RTStrFormatNumber(psz, u32 / 1000, 10, 2, 0, RTSTR_F_ZEROPAD);
2047 *psz++ = '.';
2048 psz += RTStrFormatNumber(psz, u32 % 1000, 10, 3, 0, RTSTR_F_ZEROPAD);
2049 *psz++ = ' '; /* +20 (9+1+2+1+2+1+3+1) */
2050#else
2051 memset(psz, ' ', 13);
2052 psz += 13;
2053#endif
2054 }
2055# if 0
2056 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_DATETIME)
2057 {
2058 char szDate[32];
2059 RTTIMESPEC Time;
2060 RTTimeSpecToString(RTTimeNow(&Time), szDate, sizeof(szDate));
2061 size_t cch = strlen(szDate);
2062 memcpy(psz, szDate, cch);
2063 psz += cch;
2064 *psz++ = ' '; /* +32 */
2065 }
2066# endif
2067 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_PID)
2068 {
2069#ifndef IN_GC
2070 RTPROCESS Process = RTProcSelf();
2071#else
2072 RTPROCESS Process = NIL_RTPROCESS;
2073#endif
2074 psz += RTStrFormatNumber(psz, Process, 16, sizeof(RTPROCESS) * 2, 0, RTSTR_F_ZEROPAD);
2075 *psz++ = ' '; /* +9 */
2076 }
2077 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TID)
2078 {
2079#ifndef IN_GC
2080 RTNATIVETHREAD Thread = RTThreadNativeSelf();
2081#else
2082 RTNATIVETHREAD Thread = NIL_RTNATIVETHREAD;
2083#endif
2084 psz += RTStrFormatNumber(psz, Thread, 16, sizeof(RTNATIVETHREAD) * 2, 0, RTSTR_F_ZEROPAD);
2085 *psz++ = ' '; /* +17 */
2086 }
2087 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_THREAD)
2088 {
2089#ifdef IN_RING3
2090 const char *pszName = RTThreadSelfName();
2091#elif defined IN_GC
2092 const char *pszName = "EMT-GC";
2093#else
2094 const char *pszName = "EMT-R0";
2095#endif
2096 size_t cch = 0;
2097 if (pszName)
2098 {
2099 cch = strlen(pszName);
2100 cch = RT_MIN(cch, 16);
2101 memcpy(psz, pszName, cch);
2102 psz += cch;
2103 }
2104 do
2105 *psz++ = ' ';
2106 while (cch++ < 8); /* +17 */
2107 }
2108 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_CPUID)
2109 {
2110#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
2111 const uint8_t idCpu = ASMGetApicId();
2112#else
2113 const RTCPUID idCpu = RTMpCpuId();
2114#endif
2115 psz += RTStrFormatNumber(psz, idCpu, 16, sizeof(idCpu) * 2, 0, RTSTR_F_ZEROPAD);
2116 *psz++ = ' '; /* +17 */
2117 }
2118 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_LOCK_COUNTS)
2119 {
2120#ifdef IN_RING3 /** @todo implement these counters in ring-0 too? */
2121 RTTHREAD Thread = RTThreadSelf();
2122 if (Thread != NIL_RTTHREAD)
2123 {
2124 uint32_t cReadLocks = RTThreadGetReadLockCount(Thread);
2125 uint32_t cWriteLocks = RTThreadGetWriteLockCount(Thread) - g_cLoggerLockCount;
2126 cReadLocks = RT_MIN(0xfff, cReadLocks);
2127 cWriteLocks = RT_MIN(0xfff, cWriteLocks);
2128 psz += RTStrFormatNumber(psz, cReadLocks, 16, 1, 0, RTSTR_F_ZEROPAD);
2129 *psz++ = '/';
2130 psz += RTStrFormatNumber(psz, cWriteLocks, 16, 1, 0, RTSTR_F_ZEROPAD);
2131 }
2132 else
2133#endif
2134 {
2135 *psz++ = '?';
2136 *psz++ = '/';
2137 *psz++ = '?';
2138 }
2139 *psz++ = ' '; /* +8 */
2140 }
2141 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG_NO)
2142 {
2143 psz += RTStrFormatNumber(psz, pArgs->fFlags, 16, 8, 0, RTSTR_F_ZEROPAD);
2144 *psz++ = ' '; /* +9 */
2145 }
2146 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG)
2147 {
2148#ifdef IN_RING3
2149 const char *pszGroup = pArgs->iGroup != ~0U ? pLogger->papszGroups[pArgs->iGroup] : NULL;
2150#else
2151 const char *pszGroup = NULL;
2152#endif
2153 size_t cch = 0;
2154 if (pszGroup)
2155 {
2156 cch = strlen(pszGroup);
2157 cch = RT_MIN(cch, 16);
2158 memcpy(psz, pszGroup, cch);
2159 psz += cch;
2160 }
2161 do
2162 *psz++ = ' ';
2163 while (cch++ < 8); /* +17 */
2164 }
2165 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP_NO)
2166 {
2167 if (pArgs->iGroup != ~0U)
2168 {
2169 psz += RTStrFormatNumber(psz, pArgs->iGroup, 16, 3, 0, RTSTR_F_ZEROPAD);
2170 *psz++ = ' ';
2171 }
2172 else
2173 {
2174 memcpy(psz, "-1 ", sizeof("-1 ") - 1);
2175 psz += sizeof("-1 ") - 1;
2176 } /* +9 */
2177 }
2178 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP)
2179 {
2180 const unsigned fGrp = pLogger->afGroups[pArgs->iGroup != ~0U ? pArgs->iGroup : 0];
2181 const char *pszGroup;
2182 size_t cch;
2183 switch (pArgs->fFlags & fGrp)
2184 {
2185 case 0: pszGroup = "--------"; cch = sizeof("--------") - 1; break;
2186 case RTLOGGRPFLAGS_ENABLED: pszGroup = "enabled" ; cch = sizeof("enabled" ) - 1; break;
2187 case RTLOGGRPFLAGS_LEVEL_1: pszGroup = "level 1" ; cch = sizeof("level 1" ) - 1; break;
2188 case RTLOGGRPFLAGS_LEVEL_2: pszGroup = "level 2" ; cch = sizeof("level 2" ) - 1; break;
2189 case RTLOGGRPFLAGS_LEVEL_3: pszGroup = "level 3" ; cch = sizeof("level 3" ) - 1; break;
2190 case RTLOGGRPFLAGS_LEVEL_4: pszGroup = "level 4" ; cch = sizeof("level 4" ) - 1; break;
2191 case RTLOGGRPFLAGS_LEVEL_5: pszGroup = "level 5" ; cch = sizeof("level 5" ) - 1; break;
2192 case RTLOGGRPFLAGS_LEVEL_6: pszGroup = "level 6" ; cch = sizeof("level 6" ) - 1; break;
2193 case RTLOGGRPFLAGS_FLOW: pszGroup = "flow" ; cch = sizeof("flow" ) - 1; break;
2194
2195 /* personal groups */
2196 case RTLOGGRPFLAGS_LELIK: pszGroup = "lelik" ; cch = sizeof("lelik" ) - 1; break;
2197 case RTLOGGRPFLAGS_MICHAEL: pszGroup = "Michael" ; cch = sizeof("Michael" ) - 1; break;
2198 case RTLOGGRPFLAGS_DMIK: pszGroup = "dmik" ; cch = sizeof("dmik" ) - 1; break;
2199 case RTLOGGRPFLAGS_SUNLOVER: pszGroup = "sunlover"; cch = sizeof("sunlover") - 1; break;
2200 case RTLOGGRPFLAGS_ACHIM: pszGroup = "Achim" ; cch = sizeof("Achim" ) - 1; break;
2201 case RTLOGGRPFLAGS_SANDER: pszGroup = "Sander" ; cch = sizeof("Sander" ) - 1; break;
2202 case RTLOGGRPFLAGS_KLAUS: pszGroup = "Klaus" ; cch = sizeof("Klaus" ) - 1; break;
2203 case RTLOGGRPFLAGS_FRANK: pszGroup = "Frank" ; cch = sizeof("Frank" ) - 1; break;
2204 case RTLOGGRPFLAGS_BIRD: pszGroup = "bird" ; cch = sizeof("bird" ) - 1; break;
2205 case RTLOGGRPFLAGS_NONAME: pszGroup = "noname" ; cch = sizeof("noname" ) - 1; break;
2206 default: pszGroup = "????????"; cch = sizeof("????????") - 1; break;
2207 }
2208 if (pszGroup)
2209 {
2210 cch = RT_MIN(cch, 16);
2211 memcpy(psz, pszGroup, cch);
2212 psz += cch;
2213 }
2214 do
2215 *psz++ = ' ';
2216 while (cch++ < 8); /* +17 */
2217 }
2218
2219 /*
2220 * Done, figure what we've used and advance the buffer and free size.
2221 */
2222 cb = psz - &pLogger->achScratch[pLogger->offScratch];
2223 Assert(cb <= 198);
2224 pLogger->offScratch += cb;
2225 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2226 }
2227 else if (cb <= 0)
2228 {
2229 rtlogFlush(pLogger);
2230 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2231 }
2232
2233#if defined(DEBUG) && defined(IN_RING3)
2234 /* sanity */
2235 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
2236 {
2237 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
2238 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
2239 AssertBreakpoint(); AssertBreakpoint();
2240 }
2241#endif
2242
2243 /* how much */
2244 if (cb > cbChars)
2245 cb = cbChars;
2246
2247 /* have newline? */
2248 pszNewLine = (const char *)memchr(pachChars, '\n', cb);
2249 if (pszNewLine)
2250 {
2251 if (pLogger->fFlags & RTLOGFLAGS_USECRLF)
2252 cb = pszNewLine - pachChars;
2253 else
2254 {
2255 cb = pszNewLine - pachChars + 1;
2256 pLogger->fPendingPrefix = true;
2257 }
2258 }
2259
2260 /* copy */
2261 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
2262
2263 /* advance */
2264 pLogger->offScratch += cb;
2265 cbRet += cb;
2266 cbChars -= cb;
2267
2268 if ( pszNewLine
2269 && (pLogger->fFlags & RTLOGFLAGS_USECRLF)
2270 && pLogger->offScratch + 2 < sizeof(pLogger->achScratch))
2271 {
2272 memcpy(&pLogger->achScratch[pLogger->offScratch], "\r\n", 2);
2273 pLogger->offScratch += 2;
2274 cbRet++;
2275 cbChars--;
2276 cb++;
2277 pLogger->fPendingPrefix = true;
2278 }
2279
2280 /* done? */
2281 if (cbChars <= 0)
2282 return cbRet;
2283 pachChars += cb;
2284 }
2285
2286 /* won't ever get here! */
2287 }
2288 else
2289 {
2290 /*
2291 * Termination call.
2292 * There's always space for a terminator, and it's not counted.
2293 */
2294 pLogger->achScratch[pLogger->offScratch] = '\0';
2295 return 0;
2296 }
2297}
2298
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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