VirtualBox

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

Last change on this file since 7942 was 7942, checked in by vboxsync, 17 years ago

move variables around to be able to compile this as .c (debug kernel module)

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

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