VirtualBox

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

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

fixed check

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

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