VirtualBox

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

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

The Giant CDDL Dual-License Header Change.

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

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