VirtualBox

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

Last change on this file since 21337 was 21337, checked in by vboxsync, 15 years ago

IPRT,HostDrv,AddDrv: Export public IPRT symbols for the linux kernel (pain).

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