VirtualBox

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

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

log.cpp: Fixed assertion if RTLogDestroy is called during linux module init (preemption is disabled). Also fixed fatal log lock recursion if it needed flushing during destruction.

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