VirtualBox

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

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

Runtime: Added support for colon as the 'instruction=value' separator in the _DEST log subsystem's environment variable (needed for OS/2 where the assignment char itself cannot be part of the environment variable's value).

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