VirtualBox

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

Last change on this file since 14423 was 14423, checked in by vboxsync, 16 years ago

Debug log mutex fix for linux in atomic context.

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