VirtualBox

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

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

The Big Sun Rebranding Header Change

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