VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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