VirtualBox

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

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

iprt: fixed RTMemExec leak in RTLogDestroy.

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