VirtualBox

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

Last change on this file since 39627 was 39480, checked in by vboxsync, 13 years ago

fix for r75104

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 111.0 KB
Line 
1/* $Id: log.cpp 39480 2011-11-30 17:53:58Z vboxsync $ */
2/** @file
3 * Runtime VBox - Logger.
4 */
5
6/*
7 * Copyright (C) 2006-2011 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include <iprt/log.h>
32#include "internal/iprt.h"
33
34#ifndef IN_RC
35# include <iprt/alloc.h>
36# include <iprt/process.h>
37# include <iprt/semaphore.h>
38# include <iprt/thread.h>
39# include <iprt/mp.h>
40#endif
41#ifdef IN_RING3
42# include <iprt/env.h>
43# include <iprt/file.h>
44# include <iprt/lockvalidator.h>
45# include <iprt/path.h>
46#endif
47#include <iprt/time.h>
48#include <iprt/asm.h>
49#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
50# include <iprt/asm-amd64-x86.h>
51#endif
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 * Internal logger data.
83 *
84 * @remarks Don't make casual changes to this structure.
85 */
86typedef struct RTLOGGERINTERNAL
87{
88 /** The structure revision (RTLOGGERINTERNAL_REV). */
89 uint32_t uRevision;
90 /** The size of the internal logger structure. */
91 uint32_t cbSelf;
92
93 /** Spinning mutex semaphore. Can be NIL. */
94 RTSEMSPINMUTEX hSpinMtx;
95 /** Pointer to the flush function. */
96 PFNRTLOGFLUSH pfnFlush;
97
98 /** Custom prefix callback. */
99 PFNRTLOGPREFIX pfnPrefix;
100 /** Prefix callback argument. */
101 void *pvPrefixUserArg;
102 /** This is set if a prefix is pending. */
103 bool fPendingPrefix;
104 /** Alignment padding. */
105 bool afPadding1[3];
106
107 /** The max number of groups that there is room for in afGroups and papszGroups.
108 * Used by RTLogCopyGroupAndFlags(). */
109 uint32_t cMaxGroups;
110 /** Pointer to the group name array.
111 * (The data is readonly and provided by the user.) */
112 const char * const *papszGroups;
113
114 /** The number of log entries per group. NULL if
115 * RTLOGFLAGS_RESTRICT_GROUPS is not specified. */
116 uint32_t *pacEntriesPerGroup;
117 /** The max number of entries per group. */
118 uint32_t cMaxEntriesPerGroup;
119 /** Padding. */
120 uint32_t u32Padding2;
121
122#ifdef IN_RING3 /* Note! Must be at the end! */
123 /** @name File logging bits for the logger.
124 * @{ */
125 /** Pointer to the function called when starting logging, and when
126 * ending or starting a new log file as part of history rotation.
127 * This can be NULL. */
128 PFNRTLOGPHASE pfnPhase;
129
130 /** Handle to log file (if open). */
131 RTFILE hFile;
132 /** Log file history settings: maximum amount of data to put in a file. */
133 uint64_t cbHistoryFileMax;
134 /** Log file history settings: current amount of data in a file. */
135 uint64_t cbHistoryFileWritten;
136 /** Log file history settings: maximum time to use a file (in seconds). */
137 uint32_t cSecsHistoryTimeSlot;
138 /** Log file history settings: in what time slot was the file created. */
139 uint32_t uHistoryTimeSlotStart;
140 /** Log file history settings: number of older files to keep.
141 * 0 means no history. */
142 uint32_t cHistory;
143 /** Pointer to filename. */
144 char szFilename[RTPATH_MAX];
145 /** @} */
146#endif /* IN_RING3 */
147} RTLOGGERINTERNAL;
148
149/** The revision of the internal logger structure. */
150#define RTLOGGERINTERNAL_REV UINT32_C(9)
151
152#ifdef IN_RING3
153/** The size of the RTLOGGERINTERNAL structure in ring-0. */
154# define RTLOGGERINTERNAL_R0_SIZE RT_OFFSETOF(RTLOGGERINTERNAL, pfnPhase)
155AssertCompileMemberAlignment(RTLOGGERINTERNAL, hFile, sizeof(void *));
156AssertCompileMemberAlignment(RTLOGGERINTERNAL, cbHistoryFileMax, sizeof(uint64_t));
157#endif
158
159/*******************************************************************************
160* Internal Functions *
161*******************************************************************************/
162#ifndef IN_RC
163static unsigned rtlogGroupFlags(const char *psz);
164#endif
165#ifdef IN_RING0
166static void rtR0LogLoggerExFallback(uint32_t fDestFlags, uint32_t fFlags, const char *pszFormat, va_list va);
167#endif
168#ifdef IN_RING3
169static int rtlogFileOpen(PRTLOGGER pLogger, char *pszErrorMsg, size_t cchErrorMsg);
170static void rtlogRotate(PRTLOGGER pLogger, uint32_t uTimeSlot, bool fFirst);
171#endif
172static void rtlogFlush(PRTLOGGER pLogger);
173static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars);
174static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars);
175static void rtlogLoggerExVLocked(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args);
176#ifndef IN_RC
177static void rtlogLoggerExFLocked(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...);
178#endif
179
180
181/*******************************************************************************
182* Global Variables *
183*******************************************************************************/
184#ifdef IN_RC
185/** Default logger instance. */
186extern "C" DECLIMPORT(RTLOGGERRC) g_Logger;
187#else /* !IN_RC */
188/** Default logger instance. */
189static PRTLOGGER g_pLogger;
190#endif /* !IN_RC */
191#ifdef IN_RING3
192/** The RTThreadGetWriteLockCount() change caused by the logger mutex semaphore. */
193static uint32_t volatile g_cLoggerLockCount;
194#endif
195
196#ifdef IN_RING0
197/** Number of per-thread loggers. */
198static int32_t volatile g_cPerThreadLoggers;
199/** Per-thread loggers.
200 * This is just a quick TLS hack suitable for debug logging only.
201 * If we run out of entries, just unload and reload the driver. */
202static struct RTLOGGERPERTHREAD
203{
204 /** The thread. */
205 RTNATIVETHREAD volatile NativeThread;
206 /** The (process / session) key. */
207 uintptr_t volatile uKey;
208 /** The logger instance.*/
209 PRTLOGGER volatile pLogger;
210} g_aPerThreadLoggers[8] =
211{
212 { NIL_RTNATIVETHREAD, 0, 0},
213 { NIL_RTNATIVETHREAD, 0, 0},
214 { NIL_RTNATIVETHREAD, 0, 0},
215 { NIL_RTNATIVETHREAD, 0, 0},
216 { NIL_RTNATIVETHREAD, 0, 0},
217 { NIL_RTNATIVETHREAD, 0, 0},
218 { NIL_RTNATIVETHREAD, 0, 0},
219 { NIL_RTNATIVETHREAD, 0, 0}
220};
221#endif /* IN_RING0 */
222
223/**
224 * Logger flags instructions.
225 */
226static struct
227{
228 const char *pszInstr; /**< The name */
229 size_t cchInstr; /**< The size of the name. */
230 uint32_t fFlag; /**< The flag value. */
231 bool fInverted; /**< Inverse meaning? */
232} const s_aLogFlags[] =
233{
234 { "disabled", sizeof("disabled" ) - 1, RTLOGFLAGS_DISABLED, false },
235 { "enabled", sizeof("enabled" ) - 1, RTLOGFLAGS_DISABLED, true },
236 { "buffered", sizeof("buffered" ) - 1, RTLOGFLAGS_BUFFERED, false },
237 { "unbuffered", sizeof("unbuffered" ) - 1, RTLOGFLAGS_BUFFERED, true },
238 { "usecrlf", sizeof("usecrlf" ) - 1, RTLOGFLAGS_USECRLF, false },
239 { "uself", sizeof("uself" ) - 1, RTLOGFLAGS_USECRLF, true },
240 { "append", sizeof("append" ) - 1, RTLOGFLAGS_APPEND, false },
241 { "overwrite", sizeof("overwrite" ) - 1, RTLOGFLAGS_APPEND, true },
242 { "rel", sizeof("rel" ) - 1, RTLOGFLAGS_REL_TS, false },
243 { "abs", sizeof("abs" ) - 1, RTLOGFLAGS_REL_TS, true },
244 { "dec", sizeof("dec" ) - 1, RTLOGFLAGS_DECIMAL_TS, false },
245 { "hex", sizeof("hex" ) - 1, RTLOGFLAGS_DECIMAL_TS, true },
246 { "writethru", sizeof("writethru" ) - 1, RTLOGFLAGS_WRITE_THROUGH, false },
247 { "writethrough", sizeof("writethrough") - 1, RTLOGFLAGS_WRITE_THROUGH, false },
248 { "flush", sizeof("flush" ) - 1, RTLOGFLAGS_FLUSH, false },
249 { "lockcnts", sizeof("lockcnts" ) - 1, RTLOGFLAGS_PREFIX_LOCK_COUNTS, false },
250 { "cpuid", sizeof("cpuid" ) - 1, RTLOGFLAGS_PREFIX_CPUID, false },
251 { "pid", sizeof("pid" ) - 1, RTLOGFLAGS_PREFIX_PID, false },
252 { "flagno", sizeof("flagno" ) - 1, RTLOGFLAGS_PREFIX_FLAG_NO, false },
253 { "flag", sizeof("flag" ) - 1, RTLOGFLAGS_PREFIX_FLAG, false },
254 { "groupno", sizeof("groupno" ) - 1, RTLOGFLAGS_PREFIX_GROUP_NO, false },
255 { "group", sizeof("group" ) - 1, RTLOGFLAGS_PREFIX_GROUP, false },
256 { "tid", sizeof("tid" ) - 1, RTLOGFLAGS_PREFIX_TID, false },
257 { "thread", sizeof("thread" ) - 1, RTLOGFLAGS_PREFIX_THREAD, false },
258 { "custom", sizeof("custom" ) - 1, RTLOGFLAGS_PREFIX_CUSTOM, false },
259 { "timeprog", sizeof("timeprog" ) - 1, RTLOGFLAGS_PREFIX_TIME_PROG, false },
260 { "time", sizeof("time" ) - 1, RTLOGFLAGS_PREFIX_TIME, false },
261 { "msprog", sizeof("msprog" ) - 1, RTLOGFLAGS_PREFIX_MS_PROG, false },
262 { "tsc", sizeof("tsc" ) - 1, RTLOGFLAGS_PREFIX_TSC, false }, /* before ts! */
263 { "ts", sizeof("ts" ) - 1, RTLOGFLAGS_PREFIX_TS, false },
264 /* We intentionally omit RTLOGFLAGS_RESTRICT_GROUPS. */
265};
266
267/**
268 * Logger destination instructions.
269 */
270static struct
271{
272 const char *pszInstr; /**< The name. */
273 size_t cchInstr; /**< The size of the name. */
274 uint32_t fFlag; /**< The corresponding destination flag. */
275} const s_aLogDst[] =
276{
277 { "file", sizeof("file" ) - 1, RTLOGDEST_FILE }, /* Must be 1st! */
278 { "dir", sizeof("dir" ) - 1, RTLOGDEST_FILE }, /* Must be 2nd! */
279 { "history", sizeof("history" ) - 1, 0 }, /* Must be 3rd! */
280 { "histsize", sizeof("histsize") - 1, 0 }, /* Must be 4th! */
281 { "histtime", sizeof("histtime") - 1, 0 }, /* Must be 5th! */
282 { "stdout", sizeof("stdout" ) - 1, RTLOGDEST_STDOUT },
283 { "stderr", sizeof("stderr" ) - 1, RTLOGDEST_STDERR },
284 { "debugger", sizeof("debugger") - 1, RTLOGDEST_DEBUGGER },
285 { "com", sizeof("com" ) - 1, RTLOGDEST_COM },
286 { "user", sizeof("user" ) - 1, RTLOGDEST_USER },
287};
288
289
290/**
291 * Locks the logger instance.
292 *
293 * @returns See RTSemSpinMutexRequest().
294 * @param pLogger The logger instance.
295 */
296DECLINLINE(int) rtlogLock(PRTLOGGER pLogger)
297{
298#ifndef IN_RC
299 PRTLOGGERINTERNAL pInt = pLogger->pInt;
300 AssertMsgReturn(pInt->uRevision == RTLOGGERINTERNAL_REV, ("%#x != %#x\n", pInt->uRevision, RTLOGGERINTERNAL_REV),
301 VERR_LOG_REVISION_MISMATCH);
302 AssertMsgReturn(pInt->cbSelf == sizeof(*pInt), ("%#x != %#x\n", pInt->cbSelf, sizeof(*pInt)),
303 VERR_LOG_REVISION_MISMATCH);
304 if (pInt->hSpinMtx != NIL_RTSEMSPINMUTEX)
305 {
306 int rc = RTSemSpinMutexRequest(pInt->hSpinMtx);
307 if (RT_FAILURE(rc))
308 return rc;
309 }
310#else
311 NOREF(pLogger);
312#endif
313 return VINF_SUCCESS;
314}
315
316
317/**
318 * Unlocks the logger instance.
319 * @param pLogger The logger instance.
320 */
321DECLINLINE(void) rtlogUnlock(PRTLOGGER pLogger)
322{
323#ifndef IN_RC
324 if (pLogger->pInt->hSpinMtx != NIL_RTSEMSPINMUTEX)
325 RTSemSpinMutexRelease(pLogger->pInt->hSpinMtx);
326#else
327 NOREF(pLogger);
328#endif
329 return;
330}
331
332#ifndef IN_RC
333# ifdef IN_RING3
334
335# ifdef SOME_UNUSED_FUNCTION
336/**
337 * Logging to file, output callback.
338 *
339 * @param pvArg User argument.
340 * @param pachChars Pointer to an array of utf-8 characters.
341 * @param cbChars Number of bytes in the character array pointed to by pachChars.
342 */
343static DECLCALLBACK(size_t) rtlogPhaseWrite(void *pvArg, const char *pachChars, size_t cbChars)
344{
345 PRTLOGGER pLogger = (PRTLOGGER)pvArg;
346 RTFileWrite(pLogger->pInt->hFile, pachChars, cbChars, NULL);
347 return cbChars;
348}
349
350
351/**
352 * Callback to format VBox formatting extentions.
353 * See @ref pg_rt_str_format for a reference on the format types.
354 *
355 * @returns The number of bytes formatted.
356 * @param pvArg Formatter argument.
357 * @param pfnOutput Pointer to output function.
358 * @param pvArgOutput Argument for the output function.
359 * @param ppszFormat Pointer to the format string pointer. Advance this till the char
360 * after the format specifier.
361 * @param pArgs Pointer to the argument list. Use this to fetch the arguments.
362 * @param cchWidth Format Width. -1 if not specified.
363 * @param cchPrecision Format Precision. -1 if not specified.
364 * @param fFlags Flags (RTSTR_NTFS_*).
365 * @param chArgSize The argument size specifier, 'l' or 'L'.
366 */
367static DECLCALLBACK(size_t) rtlogPhaseFormatStr(void *pvArg, PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
368 const char **ppszFormat, va_list *pArgs, int cchWidth,
369 int cchPrecision, unsigned fFlags, char chArgSize)
370{
371 char ch = *(*ppszFormat)++;
372
373 AssertMsgFailed(("Invalid logger phase format type '%%%c%.10s'!\n", ch, *ppszFormat)); NOREF(ch);
374
375 return 0;
376}
377
378# endif /* SOME_UNUSED_FUNCTION */
379
380
381/**
382 * Log phase callback function, assumes the lock is already held
383 *
384 * @param pLogger The logger instance.
385 * @param pszFormat Format string.
386 * @param ... Optional arguments as specified in the format string.
387 */
388static DECLCALLBACK(void) rtlogPhaseMsgLocked(PRTLOGGER pLogger, const char *pszFormat, ...)
389{
390 va_list args;
391 AssertPtrReturnVoid(pLogger);
392 AssertPtrReturnVoid(pLogger->pInt);
393 Assert(pLogger->pInt->hSpinMtx != NIL_RTSEMSPINMUTEX);
394
395 va_start(args, pszFormat);
396 rtlogLoggerExVLocked(pLogger, 0, ~0, pszFormat, args);
397 va_end(args);
398}
399
400
401/**
402 * Log phase callback function, assumes the lock is not held.
403 *
404 * @param pLogger The logger instance.
405 * @param pszFormat Format string.
406 * @param ... Optional arguments as specified in the format string.
407 */
408static DECLCALLBACK(void) rtlogPhaseMsgNormal(PRTLOGGER pLogger, const char *pszFormat, ...)
409{
410 va_list args;
411 AssertPtrReturnVoid(pLogger);
412 AssertPtrReturnVoid(pLogger->pInt);
413 Assert(pLogger->pInt->hSpinMtx != NIL_RTSEMSPINMUTEX);
414
415 va_start(args, pszFormat);
416 RTLogLoggerExV(pLogger, 0, ~0, pszFormat, args);
417 va_end(args);
418}
419
420# endif /* IN_RING3 */
421
422RTDECL(int) RTLogCreateExV(PRTLOGGER *ppLogger, uint32_t fFlags, const char *pszGroupSettings,
423 const char *pszEnvVarBase, unsigned cGroups, const char * const *papszGroups,
424 uint32_t fDestFlags, PFNRTLOGPHASE pfnPhase, uint32_t cHistory,
425 uint64_t cbHistoryFileMax, uint32_t cSecsHistoryTimeSlot,
426 char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, va_list args)
427{
428 int rc;
429 size_t offInternal;
430 size_t cbLogger;
431 PRTLOGGER pLogger;
432
433 /*
434 * Validate input.
435 */
436 if ( (cGroups && !papszGroups)
437 || !VALID_PTR(ppLogger) )
438 {
439 AssertMsgFailed(("Invalid parameters!\n"));
440 return VERR_INVALID_PARAMETER;
441 }
442 *ppLogger = NULL;
443
444 if (pszErrorMsg)
445 RTStrPrintf(pszErrorMsg, cchErrorMsg, N_("unknown error"));
446
447 AssertMsgReturn(cHistory < _1M, ("%#x", cHistory), VERR_OUT_OF_RANGE);
448
449 /*
450 * Allocate a logger instance.
451 */
452 offInternal = RT_OFFSETOF(RTLOGGER, afGroups[cGroups]);
453 offInternal = RT_ALIGN_Z(offInternal, sizeof(uint64_t));
454 cbLogger = offInternal + sizeof(RTLOGGERINTERNAL);
455 if (fFlags & RTLOGFLAGS_RESTRICT_GROUPS)
456 cbLogger += cGroups * sizeof(uint32_t);
457 pLogger = (PRTLOGGER)RTMemAllocZVar(cbLogger);
458 if (pLogger)
459 {
460# if defined(RT_ARCH_X86) && (!defined(LOG_USE_C99) || !defined(RT_WITHOUT_EXEC_ALLOC))
461 uint8_t *pu8Code;
462# endif
463 pLogger->u32Magic = RTLOGGER_MAGIC;
464 pLogger->cGroups = cGroups;
465 pLogger->fFlags = fFlags;
466 pLogger->fDestFlags = fDestFlags;
467 pLogger->pInt = (PRTLOGGERINTERNAL)((uintptr_t)pLogger + offInternal);
468 pLogger->pInt->uRevision = RTLOGGERINTERNAL_REV;
469 pLogger->pInt->cbSelf = sizeof(RTLOGGERINTERNAL);
470 pLogger->pInt->hSpinMtx = NIL_RTSEMSPINMUTEX;
471 pLogger->pInt->pfnFlush = NULL;
472 pLogger->pInt->pfnPrefix = NULL;
473 pLogger->pInt->pvPrefixUserArg = NULL;
474 pLogger->pInt->afPadding1[0] = false;
475 pLogger->pInt->afPadding1[1] = false;
476 pLogger->pInt->afPadding1[2] = false;
477 pLogger->pInt->cMaxGroups = cGroups;
478 pLogger->pInt->papszGroups = papszGroups;
479 if (fFlags & RTLOGFLAGS_RESTRICT_GROUPS)
480 pLogger->pInt->pacEntriesPerGroup = (uint32_t *)(pLogger->pInt + 1);
481 else
482 pLogger->pInt->pacEntriesPerGroup = NULL;
483 pLogger->pInt->cMaxEntriesPerGroup = UINT32_MAX;
484# ifdef IN_RING3
485 pLogger->pInt->pfnPhase = pfnPhase;
486 pLogger->pInt->hFile = NIL_RTFILE;
487 pLogger->pInt->cHistory = cHistory;
488 if (cbHistoryFileMax == 0)
489 pLogger->pInt->cbHistoryFileMax = UINT64_MAX;
490 else
491 pLogger->pInt->cbHistoryFileMax = cbHistoryFileMax;
492 if (cSecsHistoryTimeSlot == 0)
493 pLogger->pInt->cSecsHistoryTimeSlot = UINT32_MAX;
494 else
495 pLogger->pInt->cSecsHistoryTimeSlot = cSecsHistoryTimeSlot;
496# endif /* IN_RING3 */
497 if (pszGroupSettings)
498 RTLogGroupSettings(pLogger, pszGroupSettings);
499
500# if defined(RT_ARCH_X86) && (!defined(LOG_USE_C99) || !defined(RT_WITHOUT_EXEC_ALLOC))
501 /*
502 * Emit wrapper code.
503 */
504 pu8Code = (uint8_t *)RTMemExecAlloc(64);
505 if (pu8Code)
506 {
507 pLogger->pfnLogger = *(PFNRTLOGGER*)&pu8Code;
508 *pu8Code++ = 0x68; /* push imm32 */
509 *(void **)pu8Code = pLogger;
510 pu8Code += sizeof(void *);
511 *pu8Code++ = 0xe8; /* call rel32 */
512 *(uint32_t *)pu8Code = (uintptr_t)RTLogLogger - ((uintptr_t)pu8Code + sizeof(uint32_t));
513 pu8Code += sizeof(uint32_t);
514 *pu8Code++ = 0x8d; /* lea esp, [esp + 4] */
515 *pu8Code++ = 0x64;
516 *pu8Code++ = 0x24;
517 *pu8Code++ = 0x04;
518 *pu8Code++ = 0xc3; /* ret near */
519 AssertMsg((uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger <= 64,
520 ("Wrapper assembly is too big! %d bytes\n", (uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger));
521 rc = VINF_SUCCESS;
522 }
523 else
524 {
525# ifdef RT_OS_LINUX
526 if (pszErrorMsg) /* Most probably SELinux causing trouble since the larger RTMemAlloc succeeded. */
527 RTStrPrintf(pszErrorMsg, cchErrorMsg, N_("mmap(PROT_WRITE | PROT_EXEC) failed -- SELinux?"));
528# endif
529 rc = VERR_NO_MEMORY;
530 }
531 if (RT_SUCCESS(rc))
532# endif /* X86 wrapper code*/
533 {
534# ifdef IN_RING3 /* files and env.vars. are only accessible when in R3 at the present time. */
535 /*
536 * Format the filename.
537 */
538 if (pszFilenameFmt)
539 {
540 /** @todo validate the length, fail on overflow. */
541 RTStrPrintfV(pLogger->pInt->szFilename, sizeof(pLogger->pInt->szFilename), pszFilenameFmt, args);
542 pLogger->fDestFlags |= RTLOGDEST_FILE;
543 }
544
545 /*
546 * Parse the environment variables.
547 */
548 if (pszEnvVarBase)
549 {
550 /* make temp copy of environment variable base. */
551 size_t cchEnvVarBase = strlen(pszEnvVarBase);
552 char *pszEnvVar = (char *)alloca(cchEnvVarBase + 16);
553 memcpy(pszEnvVar, pszEnvVarBase, cchEnvVarBase);
554
555 /*
556 * Destination.
557 */
558 strcpy(pszEnvVar + cchEnvVarBase, "_DEST");
559 const char *pszVar = RTEnvGet(pszEnvVar);
560 if (pszVar)
561 RTLogDestinations(pLogger, pszVar);
562
563 /*
564 * The flags.
565 */
566 strcpy(pszEnvVar + cchEnvVarBase, "_FLAGS");
567 pszVar = RTEnvGet(pszEnvVar);
568 if (pszVar)
569 RTLogFlags(pLogger, pszVar);
570
571 /*
572 * The group settings.
573 */
574 pszEnvVar[cchEnvVarBase] = '\0';
575 pszVar = RTEnvGet(pszEnvVar);
576 if (pszVar)
577 RTLogGroupSettings(pLogger, pszVar);
578 }
579# endif /* IN_RING3 */
580
581 /*
582 * Open the destination(s).
583 */
584 rc = VINF_SUCCESS;
585# ifdef IN_RING3
586 if (pLogger->fDestFlags & RTLOGDEST_FILE)
587 {
588 if (pLogger->fFlags & RTLOGFLAGS_APPEND)
589 {
590 rc = rtlogFileOpen(pLogger, pszErrorMsg, cchErrorMsg);
591
592 /* Rotate in case of appending to a too big log file,
593 otherwise this simply doesn't do anything. */
594 rtlogRotate(pLogger, 0, true /* fFirst */);
595 }
596 else
597 {
598 /* Force rotation if it is configured. */
599 pLogger->pInt->cbHistoryFileWritten = UINT64_MAX;
600 rtlogRotate(pLogger, 0, true /* fFirst */);
601
602 /* If the file is not open then rotation is not set up. */
603 if (pLogger->pInt->hFile == NIL_RTFILE)
604 {
605 pLogger->pInt->cbHistoryFileWritten = 0;
606 rc = rtlogFileOpen(pLogger, pszErrorMsg, cchErrorMsg);
607 }
608 }
609 }
610# endif /* IN_RING3 */
611
612 /*
613 * Create mutex and check how much it counts when entering the lock
614 * so that we can report the values for RTLOGFLAGS_PREFIX_LOCK_COUNTS.
615 */
616 if (RT_SUCCESS(rc))
617 {
618 rc = RTSemSpinMutexCreate(&pLogger->pInt->hSpinMtx, RTSEMSPINMUTEX_FLAGS_IRQ_SAFE);
619 if (RT_SUCCESS(rc))
620 {
621# ifdef IN_RING3 /** @todo do counters in ring-0 too? */
622 RTTHREAD Thread = RTThreadSelf();
623 if (Thread != NIL_RTTHREAD)
624 {
625 int32_t c = RTLockValidatorWriteLockGetCount(Thread);
626 RTSemSpinMutexRequest(pLogger->pInt->hSpinMtx);
627 c = RTLockValidatorWriteLockGetCount(Thread) - c;
628 RTSemSpinMutexRelease(pLogger->pInt->hSpinMtx);
629 ASMAtomicWriteU32(&g_cLoggerLockCount, c);
630 }
631
632 /* Use the callback to generate some initial log contents. */
633 Assert(VALID_PTR(pLogger->pInt->pfnPhase) || pLogger->pInt->pfnPhase == NULL);
634 if (pLogger->pInt->pfnPhase)
635 pLogger->pInt->pfnPhase(pLogger, RTLOGPHASE_BEGIN, rtlogPhaseMsgNormal);
636# endif
637 *ppLogger = pLogger;
638 return VINF_SUCCESS;
639 }
640
641 if (pszErrorMsg)
642 RTStrPrintf(pszErrorMsg, cchErrorMsg, N_("failed to create semaphore"));
643 }
644# ifdef IN_RING3
645 RTFileClose(pLogger->pInt->hFile);
646# endif
647# if defined(LOG_USE_C99) && defined(RT_WITHOUT_EXEC_ALLOC)
648 RTMemFree(*(void **)&pLogger->pfnLogger);
649# else
650 RTMemExecFree(*(void **)&pLogger->pfnLogger, 64);
651# endif
652 }
653 RTMemFree(pLogger);
654 }
655 else
656 rc = VERR_NO_MEMORY;
657
658 return rc;
659}
660RT_EXPORT_SYMBOL(RTLogCreateExV);
661
662
663RTDECL(int) RTLogCreate(PRTLOGGER *ppLogger, uint32_t fFlags, const char *pszGroupSettings,
664 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
665 uint32_t fDestFlags, const char *pszFilenameFmt, ...)
666{
667 va_list args;
668 int rc;
669
670 va_start(args, pszFilenameFmt);
671 rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups,
672 fDestFlags, NULL /*pfnPhase*/, 0 /*cHistory*/, 0 /*cbHistoryFileMax*/, 0 /*cSecsHistoryTimeSlot*/,
673 NULL /*pszErrorMsg*/, 0 /*cchErrorMsg*/, pszFilenameFmt, args);
674 va_end(args);
675 return rc;
676}
677RT_EXPORT_SYMBOL(RTLogCreate);
678
679
680RTDECL(int) RTLogCreateEx(PRTLOGGER *ppLogger, uint32_t fFlags, const char *pszGroupSettings,
681 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
682 uint32_t fDestFlags, PFNRTLOGPHASE pfnPhase, uint32_t cHistory,
683 uint64_t cbHistoryFileMax, uint32_t cSecsHistoryTimeSlot,
684 char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, ...)
685{
686 va_list args;
687 int rc;
688
689 va_start(args, pszFilenameFmt);
690 rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups,
691 fDestFlags, pfnPhase, cHistory, cbHistoryFileMax, cSecsHistoryTimeSlot,
692 pszErrorMsg, cchErrorMsg, pszFilenameFmt, args);
693 va_end(args);
694 return rc;
695}
696RT_EXPORT_SYMBOL(RTLogCreateEx);
697
698
699/**
700 * Destroys a logger instance.
701 *
702 * The instance is flushed and all output destinations closed (where applicable).
703 *
704 * @returns iprt status code.
705 * @param pLogger The logger instance which close destroyed. NULL is fine.
706 */
707RTDECL(int) RTLogDestroy(PRTLOGGER pLogger)
708{
709 int rc;
710 uint32_t iGroup;
711 RTSEMSPINMUTEX hSpinMtx;
712
713 /*
714 * Validate input.
715 */
716 if (!pLogger)
717 return VINF_SUCCESS;
718 AssertPtrReturn(pLogger, VERR_INVALID_POINTER);
719 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
720 AssertPtrReturn(pLogger->pInt, VERR_INVALID_POINTER);
721
722 /*
723 * Acquire logger instance sem and disable all logging. (paranoia)
724 */
725 rc = rtlogLock(pLogger);
726 AssertMsgRCReturn(rc, ("%Rrc\n", rc), rc);
727
728 pLogger->fFlags |= RTLOGFLAGS_DISABLED;
729 iGroup = pLogger->cGroups;
730 while (iGroup-- > 0)
731 pLogger->afGroups[iGroup] = 0;
732
733 /*
734 * Flush it.
735 */
736 rtlogFlush(pLogger);
737
738# ifdef IN_RING3
739 /*
740 * Add end of logging message.
741 */
742 if ( (pLogger->fDestFlags & RTLOGDEST_FILE)
743 && pLogger->pInt->hFile != NIL_RTFILE)
744 pLogger->pInt->pfnPhase(pLogger, RTLOGPHASE_END, rtlogPhaseMsgLocked);
745
746 /*
747 * Close output stuffs.
748 */
749 if (pLogger->pInt->hFile != NIL_RTFILE)
750 {
751 int rc2 = RTFileClose(pLogger->pInt->hFile);
752 AssertRC(rc2);
753 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
754 rc = rc2;
755 pLogger->pInt->hFile = NIL_RTFILE;
756 }
757# endif
758
759 /*
760 * Free the mutex, the wrapper and the instance memory.
761 */
762 hSpinMtx = pLogger->pInt->hSpinMtx;
763 pLogger->pInt->hSpinMtx = NIL_RTSEMSPINMUTEX;
764 if (hSpinMtx != NIL_RTSEMSPINMUTEX)
765 {
766 int rc2;
767 RTSemSpinMutexRelease(hSpinMtx);
768 rc2 = RTSemSpinMutexDestroy(hSpinMtx);
769 AssertRC(rc2);
770 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
771 rc = rc2;
772 }
773
774 if (pLogger->pfnLogger)
775 {
776# if defined(LOG_USE_C99) && defined(RT_WITHOUT_EXEC_ALLOC)
777 RTMemFree(*(void **)&pLogger->pfnLogger);
778# else
779 RTMemExecFree(*(void **)&pLogger->pfnLogger, 64);
780# endif
781 pLogger->pfnLogger = NULL;
782 }
783 RTMemFree(pLogger);
784
785 return rc;
786}
787RT_EXPORT_SYMBOL(RTLogDestroy);
788
789
790/**
791 * Create a logger instance clone for RC usage.
792 *
793 * @returns iprt status code.
794 *
795 * @param pLogger The logger instance to be cloned.
796 * @param pLoggerRC Where to create the RC logger instance.
797 * @param cbLoggerRC Amount of memory allocated to for the RC logger
798 * instance clone.
799 * @param pfnLoggerRCPtr Pointer to logger wrapper function for this
800 * instance (RC Ptr).
801 * @param pfnFlushRCPtr Pointer to flush function (RC Ptr).
802 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
803 */
804RTDECL(int) RTLogCloneRC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerRC, size_t cbLoggerRC,
805 RTRCPTR pfnLoggerRCPtr, RTRCPTR pfnFlushRCPtr, uint32_t fFlags)
806{
807 /*
808 * Validate input.
809 */
810 if ( !pLoggerRC
811 || !pfnFlushRCPtr
812 || !pfnLoggerRCPtr)
813 {
814 AssertMsgFailed(("Invalid parameters!\n"));
815 return VERR_INVALID_PARAMETER;
816 }
817 if (cbLoggerRC < sizeof(*pLoggerRC))
818 {
819 AssertMsgFailed(("%d min=%d\n", cbLoggerRC, sizeof(*pLoggerRC)));
820 return VERR_INVALID_PARAMETER;
821 }
822
823 /*
824 * Initialize GC instance.
825 */
826 pLoggerRC->offScratch = 0;
827 pLoggerRC->fPendingPrefix = false;
828 pLoggerRC->pfnLogger = pfnLoggerRCPtr;
829 pLoggerRC->pfnFlush = pfnFlushRCPtr;
830 pLoggerRC->u32Magic = RTLOGGERRC_MAGIC;
831 pLoggerRC->fFlags = fFlags | RTLOGFLAGS_DISABLED;
832 pLoggerRC->cGroups = 1;
833 pLoggerRC->afGroups[0] = 0;
834
835 /*
836 * Resolve defaults.
837 */
838 if (!pLogger)
839 {
840 pLogger = RTLogDefaultInstance();
841 if (!pLogger)
842 return VINF_SUCCESS;
843 }
844
845 /*
846 * Check if there's enough space for the groups.
847 */
848 if (cbLoggerRC < (size_t)RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]))
849 {
850 AssertMsgFailed(("%d req=%d cGroups=%d\n", cbLoggerRC, RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]), pLogger->cGroups));
851 return VERR_BUFFER_OVERFLOW;
852 }
853 memcpy(&pLoggerRC->afGroups[0], &pLogger->afGroups[0], pLogger->cGroups * sizeof(pLoggerRC->afGroups[0]));
854 pLoggerRC->cGroups = pLogger->cGroups;
855
856 /*
857 * Copy bits from the HC instance.
858 */
859 pLoggerRC->fPendingPrefix = pLogger->pInt->fPendingPrefix;
860 pLoggerRC->fFlags |= pLogger->fFlags;
861
862 /*
863 * Check if we can remove the disabled flag.
864 */
865 if ( pLogger->fDestFlags
866 && !((pLogger->fFlags | fFlags) & RTLOGFLAGS_DISABLED))
867 pLoggerRC->fFlags &= ~RTLOGFLAGS_DISABLED;
868
869 return VINF_SUCCESS;
870}
871RT_EXPORT_SYMBOL(RTLogCloneRC);
872
873
874/**
875 * Flushes a RC logger instance to a R3 logger.
876 *
877 *
878 * @returns iprt status code.
879 * @param pLogger The R3 logger instance to flush pLoggerRC to. If NULL
880 * the default logger is used.
881 * @param pLoggerRC The RC logger instance to flush.
882 */
883RTDECL(void) RTLogFlushRC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerRC)
884{
885 /*
886 * Resolve defaults.
887 */
888 if (!pLogger)
889 {
890 pLogger = RTLogDefaultInstance();
891 if (!pLogger)
892 {
893 pLoggerRC->offScratch = 0;
894 return;
895 }
896 }
897
898 /*
899 * Any thing to flush?
900 */
901 if ( pLogger->offScratch
902 || pLoggerRC->offScratch)
903 {
904 /*
905 * Acquire logger instance sem.
906 */
907 int rc = rtlogLock(pLogger);
908 if (RT_FAILURE(rc))
909 return;
910
911 /*
912 * Write whatever the GC instance contains to the HC one, and then
913 * flush the HC instance.
914 */
915 if (pLoggerRC->offScratch)
916 {
917 rtLogOutput(pLogger, pLoggerRC->achScratch, pLoggerRC->offScratch);
918 rtLogOutput(pLogger, NULL, 0);
919 pLoggerRC->offScratch = 0;
920 }
921
922 /*
923 * Release the semaphore.
924 */
925 rtlogUnlock(pLogger);
926 }
927}
928RT_EXPORT_SYMBOL(RTLogFlushRC);
929
930# ifdef IN_RING3
931
932RTDECL(int) RTLogCreateForR0(PRTLOGGER pLogger, size_t cbLogger,
933 RTR0PTR pLoggerR0Ptr, RTR0PTR pfnLoggerR0Ptr, RTR0PTR pfnFlushR0Ptr,
934 uint32_t fFlags, uint32_t fDestFlags)
935{
936 /*
937 * Validate input.
938 */
939 AssertPtrReturn(pLogger, VERR_INVALID_PARAMETER);
940 size_t const cbRequired = sizeof(*pLogger) + RTLOGGERINTERNAL_R0_SIZE;
941 AssertReturn(cbLogger >= cbRequired, VERR_BUFFER_OVERFLOW);
942 AssertReturn(pLoggerR0Ptr != NIL_RTR0PTR, VERR_INVALID_PARAMETER);
943 AssertReturn(pfnLoggerR0Ptr != NIL_RTR0PTR, VERR_INVALID_PARAMETER);
944
945 /*
946 * Initialize the ring-0 instance.
947 */
948 pLogger->achScratch[0] = 0;
949 pLogger->offScratch = 0;
950 pLogger->pfnLogger = (PFNRTLOGGER)pfnLoggerR0Ptr;
951 pLogger->fFlags = fFlags;
952 pLogger->fDestFlags = fDestFlags & ~RTLOGDEST_FILE;
953 pLogger->pInt = NULL;
954 pLogger->cGroups = 1;
955 pLogger->afGroups[0] = 0;
956
957 uint32_t cMaxGroups = (uint32_t)((cbLogger - cbRequired) / sizeof(pLogger->afGroups[0]));
958 if (fFlags & RTLOGFLAGS_RESTRICT_GROUPS)
959 cMaxGroups /= 2;
960 PRTLOGGERINTERNAL pInt;
961 for (;;)
962 {
963 AssertReturn(cMaxGroups > 0, VERR_BUFFER_OVERFLOW);
964 pInt = (PRTLOGGERINTERNAL)&pLogger->afGroups[cMaxGroups];
965 if (!((uintptr_t)pInt & (sizeof(uint64_t) - 1)))
966 break;
967 cMaxGroups--;
968 }
969 pLogger->pInt = (PRTLOGGERINTERNAL)(pLoggerR0Ptr + (uintptr_t)pInt - (uintptr_t)pLogger);
970 pInt->uRevision = RTLOGGERINTERNAL_REV;
971 pInt->cbSelf = RTLOGGERINTERNAL_R0_SIZE;
972 pInt->hSpinMtx = NIL_RTSEMSPINMUTEX; /* Not serialized. */
973 pInt->pfnFlush = (PFNRTLOGFLUSH)pfnFlushR0Ptr;
974 pInt->pfnPrefix = NULL;
975 pInt->pvPrefixUserArg = NULL;
976 pInt->fPendingPrefix = false;
977 pInt->cMaxGroups = cMaxGroups;
978 pInt->papszGroups = NULL;
979 pInt->cMaxEntriesPerGroup = UINT32_MAX;
980 if (fFlags & RTLOGFLAGS_RESTRICT_GROUPS)
981 {
982 memset(pInt + 1, 0, sizeof(uint32_t) * cMaxGroups);
983 pInt->pacEntriesPerGroup= (uint32_t *)(pLogger->pInt + 1);
984 }
985 else
986 pInt->pacEntriesPerGroup= NULL;
987
988 pLogger->u32Magic = RTLOGGER_MAGIC;
989 return VINF_SUCCESS;
990}
991RT_EXPORT_SYMBOL(RTLogCreateForR0);
992
993
994RTDECL(size_t) RTLogCalcSizeForR0(uint32_t cGroups, uint32_t fFlags)
995{
996 size_t cb = RT_OFFSETOF(RTLOGGER, afGroups[cGroups]);
997 cb = RT_ALIGN_Z(cb, sizeof(uint64_t));
998 cb += sizeof(RTLOGGERINTERNAL);
999 if (fFlags & RTLOGFLAGS_RESTRICT_GROUPS)
1000 cb += sizeof(uint32_t) * cGroups;
1001 return cb;
1002}
1003RT_EXPORT_SYMBOL(RTLogCalcSizeForR0);
1004
1005
1006RTDECL(int) RTLogCopyGroupsAndFlagsForR0(PRTLOGGER pDstLogger, RTR0PTR pDstLoggerR0Ptr,
1007 PCRTLOGGER pSrcLogger, uint32_t fFlagsOr, uint32_t fFlagsAnd)
1008{
1009 /*
1010 * Validate input.
1011 */
1012 AssertPtrReturn(pDstLogger, VERR_INVALID_PARAMETER);
1013 AssertPtrNullReturn(pSrcLogger, VERR_INVALID_PARAMETER);
1014
1015 /*
1016 * Resolve defaults.
1017 */
1018 if (!pSrcLogger)
1019 {
1020 pSrcLogger = RTLogDefaultInstance();
1021 if (!pSrcLogger)
1022 {
1023 pDstLogger->fFlags |= RTLOGFLAGS_DISABLED | fFlagsOr;
1024 pDstLogger->cGroups = 1;
1025 pDstLogger->afGroups[0] = 0;
1026 return VINF_SUCCESS;
1027 }
1028 }
1029
1030 /*
1031 * Copy flags and group settings.
1032 */
1033 pDstLogger->fFlags = (pSrcLogger->fFlags & fFlagsAnd & ~RTLOGFLAGS_RESTRICT_GROUPS) | fFlagsOr;
1034
1035 PRTLOGGERINTERNAL pDstInt = (PRTLOGGERINTERNAL)((uintptr_t)pDstLogger->pInt - pDstLoggerR0Ptr + (uintptr_t)pDstLogger);
1036 int rc = VINF_SUCCESS;
1037 uint32_t cGroups = pSrcLogger->cGroups;
1038 if (cGroups > pDstInt->cMaxGroups)
1039 {
1040 AssertMsgFailed(("cMaxGroups=%zd cGroups=%zd (min size %d)\n", pDstInt->cMaxGroups,
1041 pSrcLogger->cGroups, RT_OFFSETOF(RTLOGGER, afGroups[pSrcLogger->cGroups]) + RTLOGGERINTERNAL_R0_SIZE));
1042 rc = VERR_INVALID_PARAMETER;
1043 cGroups = pDstInt->cMaxGroups;
1044 }
1045 memcpy(&pDstLogger->afGroups[0], &pSrcLogger->afGroups[0], cGroups * sizeof(pDstLogger->afGroups[0]));
1046 pDstLogger->cGroups = cGroups;
1047
1048 return rc;
1049}
1050RT_EXPORT_SYMBOL(RTLogCopyGroupsAndFlagsForR0);
1051
1052
1053RTDECL(int) RTLogSetCustomPrefixCallbackForR0(PRTLOGGER pLogger, RTR0PTR pLoggerR0Ptr,
1054 RTR0PTR pfnCallbackR0Ptr, RTR0PTR pvUserR0Ptr)
1055{
1056 AssertPtrReturn(pLogger, VERR_INVALID_POINTER);
1057 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
1058
1059 /*
1060 * Do the work.
1061 */
1062 PRTLOGGERINTERNAL pInt = (PRTLOGGERINTERNAL)((uintptr_t)pLogger->pInt - pLoggerR0Ptr + (uintptr_t)pLogger);
1063 AssertReturn(pInt->uRevision == RTLOGGERINTERNAL_REV, VERR_LOG_REVISION_MISMATCH);
1064 pInt->pvPrefixUserArg = (void *)pvUserR0Ptr;
1065 pInt->pfnPrefix = (PFNRTLOGPREFIX)pfnCallbackR0Ptr;
1066
1067 return VINF_SUCCESS;
1068}
1069RT_EXPORT_SYMBOL(RTLogSetCustomPrefixCallbackForR0);
1070
1071RTDECL(void) RTLogFlushR0(PRTLOGGER pLogger, PRTLOGGER pLoggerR0)
1072{
1073 /*
1074 * Resolve defaults.
1075 */
1076 if (!pLogger)
1077 {
1078 pLogger = RTLogDefaultInstance();
1079 if (!pLogger)
1080 {
1081 /* flushing to "/dev/null". */
1082 if (pLoggerR0->offScratch)
1083 pLoggerR0->offScratch = 0;
1084 return;
1085 }
1086 }
1087
1088 /*
1089 * Any thing to flush?
1090 */
1091 if ( pLoggerR0->offScratch
1092 || pLogger->offScratch)
1093 {
1094 /*
1095 * Acquire logger semaphores.
1096 */
1097 int rc = rtlogLock(pLogger);
1098 if (RT_FAILURE(rc))
1099 return;
1100 if (RT_SUCCESS(rc))
1101 {
1102 /*
1103 * Write whatever the GC instance contains to the HC one, and then
1104 * flush the HC instance.
1105 */
1106 if (pLoggerR0->offScratch)
1107 {
1108 rtLogOutput(pLogger, pLoggerR0->achScratch, pLoggerR0->offScratch);
1109 rtLogOutput(pLogger, NULL, 0);
1110 pLoggerR0->offScratch = 0;
1111 }
1112 }
1113 rtlogUnlock(pLogger);
1114 }
1115}
1116RT_EXPORT_SYMBOL(RTLogFlushR0);
1117
1118# endif /* IN_RING3 */
1119
1120
1121/**
1122 * Flushes the buffer in one logger instance onto another logger.
1123 *
1124 * @returns iprt status code.
1125 *
1126 * @param pSrcLogger The logger instance to flush.
1127 * @param pDstLogger The logger instance to flush onto.
1128 * If NULL the default logger will be used.
1129 */
1130RTDECL(void) RTLogFlushToLogger(PRTLOGGER pSrcLogger, PRTLOGGER pDstLogger)
1131{
1132 /*
1133 * Resolve defaults.
1134 */
1135 if (!pDstLogger)
1136 {
1137 pDstLogger = RTLogDefaultInstance();
1138 if (!pDstLogger)
1139 {
1140 /* flushing to "/dev/null". */
1141 if (pSrcLogger->offScratch)
1142 {
1143 int rc = rtlogLock(pSrcLogger);
1144 if (RT_SUCCESS(rc))
1145 {
1146 pSrcLogger->offScratch = 0;
1147 rtlogUnlock(pSrcLogger);
1148 }
1149 }
1150 return;
1151 }
1152 }
1153
1154 /*
1155 * Any thing to flush?
1156 */
1157 if ( pSrcLogger->offScratch
1158 || pDstLogger->offScratch)
1159 {
1160 /*
1161 * Acquire logger semaphores.
1162 */
1163 int rc = rtlogLock(pDstLogger);
1164 if (RT_FAILURE(rc))
1165 return;
1166 rc = rtlogLock(pSrcLogger);
1167 if (RT_SUCCESS(rc))
1168 {
1169 /*
1170 * Write whatever the GC instance contains to the HC one, and then
1171 * flush the HC instance.
1172 */
1173 if (pSrcLogger->offScratch)
1174 {
1175 rtLogOutput(pDstLogger, pSrcLogger->achScratch, pSrcLogger->offScratch);
1176 rtLogOutput(pDstLogger, NULL, 0);
1177 pSrcLogger->offScratch = 0;
1178 }
1179
1180 /*
1181 * Release the semaphores.
1182 */
1183 rtlogUnlock(pSrcLogger);
1184 }
1185 rtlogUnlock(pDstLogger);
1186 }
1187}
1188RT_EXPORT_SYMBOL(RTLogFlushToLogger);
1189
1190
1191/**
1192 * Sets the custom prefix callback.
1193 *
1194 * @returns IPRT status code.
1195 * @param pLogger The logger instance.
1196 * @param pfnCallback The callback.
1197 * @param pvUser The user argument for the callback.
1198 * */
1199RTDECL(int) RTLogSetCustomPrefixCallback(PRTLOGGER pLogger, PFNRTLOGPREFIX pfnCallback, void *pvUser)
1200{
1201 /*
1202 * Resolve defaults.
1203 */
1204 if (!pLogger)
1205 {
1206 pLogger = RTLogDefaultInstance();
1207 if (!pLogger)
1208 return VINF_SUCCESS;
1209 }
1210 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
1211
1212 /*
1213 * Do the work.
1214 */
1215 rtlogLock(pLogger);
1216 pLogger->pInt->pvPrefixUserArg = pvUser;
1217 pLogger->pInt->pfnPrefix = pfnCallback;
1218 rtlogUnlock(pLogger);
1219
1220 return VINF_SUCCESS;
1221}
1222RT_EXPORT_SYMBOL(RTLogSetCustomPrefixCallback);
1223
1224
1225/**
1226 * Matches a group name with a pattern mask in an case insensitive manner (ASCII).
1227 *
1228 * @returns true if matching and *ppachMask set to the end of the pattern.
1229 * @returns false if no match.
1230 * @param pszGrp The group name.
1231 * @param ppachMask Pointer to the pointer to the mask. Only wildcard supported is '*'.
1232 * @param cchMask The length of the mask, including modifiers. The modifiers is why
1233 * we update *ppachMask on match.
1234 */
1235static bool rtlogIsGroupMatching(const char *pszGrp, const char **ppachMask, size_t cchMask)
1236{
1237 const char *pachMask;
1238
1239 if (!pszGrp || !*pszGrp)
1240 return false;
1241 pachMask = *ppachMask;
1242 for (;;)
1243 {
1244 if (RT_C_TO_LOWER(*pszGrp) != RT_C_TO_LOWER(*pachMask))
1245 {
1246 const char *pszTmp;
1247
1248 /*
1249 * Check for wildcard and do a minimal match if found.
1250 */
1251 if (*pachMask != '*')
1252 return false;
1253
1254 /* eat '*'s. */
1255 do pachMask++;
1256 while (--cchMask && *pachMask == '*');
1257
1258 /* is there more to match? */
1259 if ( !cchMask
1260 || *pachMask == '.'
1261 || *pachMask == '=')
1262 break; /* we're good */
1263
1264 /* do extremely minimal matching (fixme) */
1265 pszTmp = strchr(pszGrp, RT_C_TO_LOWER(*pachMask));
1266 if (!pszTmp)
1267 pszTmp = strchr(pszGrp, RT_C_TO_UPPER(*pachMask));
1268 if (!pszTmp)
1269 return false;
1270 pszGrp = pszTmp;
1271 continue;
1272 }
1273
1274 /* done? */
1275 if (!*++pszGrp)
1276 {
1277 /* trailing wildcard is ok. */
1278 do
1279 {
1280 pachMask++;
1281 cchMask--;
1282 } while (cchMask && *pachMask == '*');
1283 if ( !cchMask
1284 || *pachMask == '.'
1285 || *pachMask == '=')
1286 break; /* we're good */
1287 return false;
1288 }
1289
1290 if (!--cchMask)
1291 return false;
1292 pachMask++;
1293 }
1294
1295 /* match */
1296 *ppachMask = pachMask;
1297 return true;
1298}
1299
1300
1301/**
1302 * Updates the group settings for the logger instance using the specified
1303 * specification string.
1304 *
1305 * @returns iprt status code.
1306 * Failures can safely be ignored.
1307 * @param pLogger Logger instance.
1308 * @param pszVar Value to parse.
1309 */
1310RTDECL(int) RTLogGroupSettings(PRTLOGGER pLogger, const char *pszVar)
1311{
1312 /*
1313 * Resolve defaults.
1314 */
1315 if (!pLogger)
1316 {
1317 pLogger = RTLogDefaultInstance();
1318 if (!pLogger)
1319 return VINF_SUCCESS;
1320 }
1321
1322 /*
1323 * Iterate the string.
1324 */
1325 while (*pszVar)
1326 {
1327 /*
1328 * Skip prefixes (blanks, ;, + and -).
1329 */
1330 bool fEnabled = true;
1331 char ch;
1332 const char *pszStart;
1333 unsigned i;
1334 size_t cch;
1335
1336 while ((ch = *pszVar) == '+' || ch == '-' || ch == ' ' || ch == '\t' || ch == '\n' || ch == ';')
1337 {
1338 if (ch == '+' || ch == '-' || ch == ';')
1339 fEnabled = ch != '-';
1340 pszVar++;
1341 }
1342 if (!*pszVar)
1343 break;
1344
1345 /*
1346 * Find end.
1347 */
1348 pszStart = pszVar;
1349 while ((ch = *pszVar) != '\0' && ch != '+' && ch != '-' && ch != ' ' && ch != '\t')
1350 pszVar++;
1351
1352 /*
1353 * Find the group (ascii case insensitive search).
1354 * Special group 'all'.
1355 */
1356 cch = pszVar - pszStart;
1357 if ( cch >= 3
1358 && (pszStart[0] == 'a' || pszStart[0] == 'A')
1359 && (pszStart[1] == 'l' || pszStart[1] == 'L')
1360 && (pszStart[2] == 'l' || pszStart[2] == 'L')
1361 && (cch == 3 || pszStart[3] == '.' || pszStart[3] == '='))
1362 {
1363 /*
1364 * All.
1365 */
1366 unsigned fFlags = cch == 3
1367 ? RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1
1368 : rtlogGroupFlags(&pszStart[3]);
1369 for (i = 0; i < pLogger->cGroups; i++)
1370 {
1371 if (fEnabled)
1372 pLogger->afGroups[i] |= fFlags;
1373 else
1374 pLogger->afGroups[i] &= ~fFlags;
1375 }
1376 }
1377 else
1378 {
1379 /*
1380 * Specific group(s).
1381 */
1382 for (i = 0; i < pLogger->cGroups; i++)
1383 {
1384 const char *psz2 = (const char*)pszStart;
1385 if (rtlogIsGroupMatching(pLogger->pInt->papszGroups[i], &psz2, cch))
1386 {
1387 unsigned fFlags = RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1;
1388 if (*psz2 == '.' || *psz2 == '=')
1389 fFlags = rtlogGroupFlags(psz2);
1390 if (fEnabled)
1391 pLogger->afGroups[i] |= fFlags;
1392 else
1393 pLogger->afGroups[i] &= ~fFlags;
1394 }
1395 } /* for each group */
1396 }
1397
1398 } /* parse specification */
1399
1400 return VINF_SUCCESS;
1401}
1402RT_EXPORT_SYMBOL(RTLogGroupSettings);
1403
1404
1405/**
1406 * Interprets the group flags suffix.
1407 *
1408 * @returns Flags specified. (0 is possible!)
1409 * @param psz Start of Suffix. (Either dot or equal sign.)
1410 */
1411static unsigned rtlogGroupFlags(const char *psz)
1412{
1413 unsigned fFlags = 0;
1414
1415 /*
1416 * Literal flags.
1417 */
1418 while (*psz == '.')
1419 {
1420 static struct
1421 {
1422 const char *pszFlag; /* lowercase!! */
1423 unsigned fFlag;
1424 } aFlags[] =
1425 {
1426 { "eo", RTLOGGRPFLAGS_ENABLED },
1427 { "enabledonly",RTLOGGRPFLAGS_ENABLED },
1428 { "e", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },
1429 { "enabled", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },
1430 { "l1", RTLOGGRPFLAGS_LEVEL_1 },
1431 { "level1", RTLOGGRPFLAGS_LEVEL_1 },
1432 { "l", RTLOGGRPFLAGS_LEVEL_2 },
1433 { "l2", RTLOGGRPFLAGS_LEVEL_2 },
1434 { "level2", RTLOGGRPFLAGS_LEVEL_2 },
1435 { "l3", RTLOGGRPFLAGS_LEVEL_3 },
1436 { "level3", RTLOGGRPFLAGS_LEVEL_3 },
1437 { "l4", RTLOGGRPFLAGS_LEVEL_4 },
1438 { "level4", RTLOGGRPFLAGS_LEVEL_4 },
1439 { "l5", RTLOGGRPFLAGS_LEVEL_5 },
1440 { "level5", RTLOGGRPFLAGS_LEVEL_5 },
1441 { "l6", RTLOGGRPFLAGS_LEVEL_6 },
1442 { "level6", RTLOGGRPFLAGS_LEVEL_6 },
1443 { "f", RTLOGGRPFLAGS_FLOW },
1444 { "flow", RTLOGGRPFLAGS_FLOW },
1445 { "restrict", RTLOGGRPFLAGS_RESTRICT },
1446
1447 { "lelik", RTLOGGRPFLAGS_LELIK },
1448 { "michael", RTLOGGRPFLAGS_MICHAEL },
1449 { "sunlover", RTLOGGRPFLAGS_SUNLOVER },
1450 { "achim", RTLOGGRPFLAGS_ACHIM },
1451 { "achimha", RTLOGGRPFLAGS_ACHIM },
1452 { "s", RTLOGGRPFLAGS_SANDER },
1453 { "sander", RTLOGGRPFLAGS_SANDER },
1454 { "sandervl", RTLOGGRPFLAGS_SANDER },
1455 { "klaus", RTLOGGRPFLAGS_KLAUS },
1456 { "frank", RTLOGGRPFLAGS_FRANK },
1457 { "b", RTLOGGRPFLAGS_BIRD },
1458 { "bird", RTLOGGRPFLAGS_BIRD },
1459 { "aleksey", RTLOGGRPFLAGS_ALEKSEY },
1460 { "dj", RTLOGGRPFLAGS_DJ },
1461 { "n", RTLOGGRPFLAGS_NONAME },
1462 { "noname", RTLOGGRPFLAGS_NONAME }
1463 };
1464 unsigned i;
1465 bool fFound = false;
1466 psz++;
1467 for (i = 0; i < RT_ELEMENTS(aFlags) && !fFound; i++)
1468 {
1469 const char *psz1 = aFlags[i].pszFlag;
1470 const char *psz2 = psz;
1471 while (*psz1 == RT_C_TO_LOWER(*psz2))
1472 {
1473 psz1++;
1474 psz2++;
1475 if (!*psz1)
1476 {
1477 if ( (*psz2 >= 'a' && *psz2 <= 'z')
1478 || (*psz2 >= 'A' && *psz2 <= 'Z')
1479 || (*psz2 >= '0' && *psz2 <= '9') )
1480 break;
1481 fFlags |= aFlags[i].fFlag;
1482 fFound = true;
1483 psz = psz2;
1484 break;
1485 }
1486 } /* strincmp */
1487 } /* for each flags */
1488 }
1489
1490 /*
1491 * Flag value.
1492 */
1493 if (*psz == '=')
1494 {
1495 psz++;
1496 if (*psz == '~')
1497 fFlags = ~RTStrToInt32(psz + 1);
1498 else
1499 fFlags = RTStrToInt32(psz);
1500 }
1501
1502 return fFlags;
1503}
1504
1505/**
1506 * Helper for RTLogGetGroupSettings.
1507 */
1508static int rtLogGetGroupSettingsAddOne(const char *pszName, uint32_t fGroup, char **ppszBuf, size_t *pcchBuf, bool *pfNotFirst)
1509{
1510# define APPEND_PSZ(psz,cch) do { memcpy(*ppszBuf, (psz), (cch)); *ppszBuf += (cch); *pcchBuf -= (cch); } while (0)
1511# define APPEND_SZ(sz) APPEND_PSZ(sz, sizeof(sz) - 1)
1512# define APPEND_CH(ch) do { **ppszBuf = (ch); *ppszBuf += 1; *pcchBuf -= 1; } while (0)
1513
1514 /*
1515 * Add the name.
1516 */
1517 size_t cchName = strlen(pszName);
1518 if (cchName + 1 + *pfNotFirst > *pcchBuf)
1519 return VERR_BUFFER_OVERFLOW;
1520 if (*pfNotFirst)
1521 APPEND_CH(' ');
1522 else
1523 *pfNotFirst = true;
1524 APPEND_PSZ(pszName, cchName);
1525
1526 /*
1527 * Only generate mnemonics for the simple+common bits.
1528 */
1529 if (fGroup == (RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1))
1530 /* nothing */;
1531 else if ( fGroup == (RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 | RTLOGGRPFLAGS_LEVEL_2 | RTLOGGRPFLAGS_FLOW)
1532 && *pcchBuf >= sizeof(".e.l.f"))
1533 APPEND_SZ(".e.l.f");
1534 else if ( fGroup == (RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 | RTLOGGRPFLAGS_FLOW)
1535 && *pcchBuf >= sizeof(".e.f"))
1536 APPEND_SZ(".e.f");
1537 else if (*pcchBuf >= 1 + 10 + 1)
1538 {
1539 size_t cch;
1540 APPEND_CH('=');
1541 cch = RTStrFormatNumber(*ppszBuf, fGroup, 16, 0, 0, RTSTR_F_SPECIAL | RTSTR_F_32BIT);
1542 *ppszBuf += cch;
1543 *pcchBuf -= cch;
1544 }
1545 else
1546 return VERR_BUFFER_OVERFLOW;
1547
1548# undef APPEND_PSZ
1549# undef APPEND_SZ
1550# undef APPEND_CH
1551 return VINF_SUCCESS;
1552}
1553
1554
1555/**
1556 * Get the current log group settings as a string.
1557 *
1558 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
1559 * @param pLogger Logger instance (NULL for default logger).
1560 * @param pszBuf The output buffer.
1561 * @param cchBuf The size of the output buffer. Must be greater
1562 * than zero.
1563 */
1564RTDECL(int) RTLogGetGroupSettings(PRTLOGGER pLogger, char *pszBuf, size_t cchBuf)
1565{
1566 bool fNotFirst = false;
1567 int rc = VINF_SUCCESS;
1568 uint32_t cGroups;
1569 uint32_t fGroup;
1570 uint32_t i;
1571
1572 Assert(cchBuf);
1573
1574 /*
1575 * Resolve defaults.
1576 */
1577 if (!pLogger)
1578 {
1579 pLogger = RTLogDefaultInstance();
1580 if (!pLogger)
1581 {
1582 *pszBuf = '\0';
1583 return VINF_SUCCESS;
1584 }
1585 }
1586
1587 cGroups = pLogger->cGroups;
1588
1589 /*
1590 * Check if all are the same.
1591 */
1592 fGroup = pLogger->afGroups[0];
1593 for (i = 1; i < cGroups; i++)
1594 if (pLogger->afGroups[i] != fGroup)
1595 break;
1596 if (i >= cGroups)
1597 rc = rtLogGetGroupSettingsAddOne("all", fGroup, &pszBuf, &cchBuf, &fNotFirst);
1598 else
1599 {
1600
1601 /*
1602 * Iterate all the groups and print all that are enabled.
1603 */
1604 for (i = 0; i < cGroups; i++)
1605 {
1606 fGroup = pLogger->afGroups[i];
1607 if (fGroup)
1608 {
1609 const char *pszName = pLogger->pInt->papszGroups[i];
1610 if (pszName)
1611 {
1612 rc = rtLogGetGroupSettingsAddOne(pszName, fGroup, &pszBuf, &cchBuf, &fNotFirst);
1613 if (rc)
1614 break;
1615 }
1616 }
1617 }
1618 }
1619
1620 *pszBuf = '\0';
1621 return rc;
1622}
1623RT_EXPORT_SYMBOL(RTLogGetGroupSettings);
1624
1625#endif /* !IN_RC */
1626
1627/**
1628 * Updates the flags for the logger instance using the specified
1629 * specification string.
1630 *
1631 * @returns iprt status code.
1632 * Failures can safely be ignored.
1633 * @param pLogger Logger instance (NULL for default logger).
1634 * @param pszVar Value to parse.
1635 */
1636RTDECL(int) RTLogFlags(PRTLOGGER pLogger, const char *pszVar)
1637{
1638 int rc = VINF_SUCCESS;
1639
1640 /*
1641 * Resolve defaults.
1642 */
1643 if (!pLogger)
1644 {
1645 pLogger = RTLogDefaultInstance();
1646 if (!pLogger)
1647 return VINF_SUCCESS;
1648 }
1649
1650 /*
1651 * Iterate the string.
1652 */
1653 while (*pszVar)
1654 {
1655 /* check no prefix. */
1656 bool fNo = false;
1657 char ch;
1658 unsigned i;
1659
1660 /* skip blanks. */
1661 while (RT_C_IS_SPACE(*pszVar))
1662 pszVar++;
1663 if (!*pszVar)
1664 return rc;
1665
1666 while ((ch = *pszVar) != '\0')
1667 {
1668 if (ch == 'n' && pszVar[1] == 'o')
1669 {
1670 pszVar += 2;
1671 fNo = !fNo;
1672 }
1673 else if (ch == '+')
1674 {
1675 pszVar++;
1676 fNo = true;
1677 }
1678 else if (ch == '-' || ch == '!' || ch == '~')
1679 {
1680 pszVar++;
1681 fNo = !fNo;
1682 }
1683 else
1684 break;
1685 }
1686
1687 /* instruction. */
1688 for (i = 0; i < RT_ELEMENTS(s_aLogFlags); i++)
1689 {
1690 if (!strncmp(pszVar, s_aLogFlags[i].pszInstr, s_aLogFlags[i].cchInstr))
1691 {
1692 if (fNo == s_aLogFlags[i].fInverted)
1693 pLogger->fFlags |= s_aLogFlags[i].fFlag;
1694 else
1695 pLogger->fFlags &= ~s_aLogFlags[i].fFlag;
1696 pszVar += s_aLogFlags[i].cchInstr;
1697 break;
1698 }
1699 }
1700
1701 /* unknown instruction? */
1702 if (i >= RT_ELEMENTS(s_aLogFlags))
1703 {
1704 AssertMsgFailed(("Invalid flags! unknown instruction %.20s\n", pszVar));
1705 pszVar++;
1706 }
1707
1708 /* skip blanks and delimiters. */
1709 while (RT_C_IS_SPACE(*pszVar) || *pszVar == ';')
1710 pszVar++;
1711 } /* while more environment variable value left */
1712
1713 return rc;
1714}
1715RT_EXPORT_SYMBOL(RTLogFlags);
1716
1717
1718/**
1719 * Changes the buffering setting of the specified logger.
1720 *
1721 * This can be used for optimizing longish logging sequences.
1722 *
1723 * @returns The old state.
1724 * @param pLogger The logger instance (NULL is an alias for the
1725 * default logger).
1726 * @param fBuffered The new state.
1727 */
1728RTDECL(bool) RTLogSetBuffering(PRTLOGGER pLogger, bool fBuffered)
1729{
1730 bool fOld;
1731
1732 /*
1733 * Resolve the logger instance.
1734 */
1735 if (!pLogger)
1736 {
1737 pLogger = RTLogDefaultInstance();
1738 if (!pLogger)
1739 return false;
1740 }
1741
1742 rtlogLock(pLogger);
1743 fOld = !!(pLogger->fFlags & RTLOGFLAGS_BUFFERED);
1744 if (fBuffered)
1745 pLogger->fFlags |= RTLOGFLAGS_BUFFERED;
1746 else
1747 pLogger->fFlags &= ~RTLOGFLAGS_BUFFERED;
1748 rtlogUnlock(pLogger);
1749
1750 return fOld;
1751}
1752RT_EXPORT_SYMBOL(RTLogSetBuffering);
1753
1754
1755#ifdef IN_RING3
1756RTDECL(uint32_t) RTLogSetGroupLimit(PRTLOGGER pLogger, uint32_t cMaxEntriesPerGroup)
1757{
1758 /*
1759 * Resolve the logger instance.
1760 */
1761 if (!pLogger)
1762 {
1763 pLogger = RTLogDefaultInstance();
1764 if (!pLogger)
1765 return UINT32_MAX;
1766 }
1767
1768 rtlogLock(pLogger);
1769 uint32_t cOld = pLogger->pInt->cMaxEntriesPerGroup;
1770 pLogger->pInt->cMaxEntriesPerGroup = cMaxEntriesPerGroup;
1771 rtlogUnlock(pLogger);
1772
1773 return cOld;
1774}
1775#endif
1776
1777#ifndef IN_RC
1778
1779/**
1780 * Get the current log flags as a string.
1781 *
1782 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
1783 * @param pLogger Logger instance (NULL for default logger).
1784 * @param pszBuf The output buffer.
1785 * @param cchBuf The size of the output buffer. Must be greater
1786 * than zero.
1787 */
1788RTDECL(int) RTLogGetFlags(PRTLOGGER pLogger, char *pszBuf, size_t cchBuf)
1789{
1790 bool fNotFirst = false;
1791 int rc = VINF_SUCCESS;
1792 uint32_t fFlags;
1793 unsigned i;
1794
1795 Assert(cchBuf);
1796
1797 /*
1798 * Resolve defaults.
1799 */
1800 if (!pLogger)
1801 {
1802 pLogger = RTLogDefaultInstance();
1803 if (!pLogger)
1804 {
1805 *pszBuf = '\0';
1806 return VINF_SUCCESS;
1807 }
1808 }
1809
1810 /*
1811 * Add the flags in the list.
1812 */
1813 fFlags = pLogger->fFlags;
1814 for (i = 0; i < RT_ELEMENTS(s_aLogFlags); i++)
1815 if ( !s_aLogFlags[i].fInverted
1816 ? (s_aLogFlags[i].fFlag & fFlags)
1817 : !(s_aLogFlags[i].fFlag & fFlags))
1818 {
1819 size_t cchInstr = s_aLogFlags[i].cchInstr;
1820 if (cchInstr + fNotFirst + 1 > cchBuf)
1821 {
1822 rc = VERR_BUFFER_OVERFLOW;
1823 break;
1824 }
1825 if (fNotFirst)
1826 {
1827 *pszBuf++ = ' ';
1828 cchBuf--;
1829 }
1830 memcpy(pszBuf, s_aLogFlags[i].pszInstr, cchInstr);
1831 pszBuf += cchInstr;
1832 cchBuf -= cchInstr;
1833 fNotFirst = true;
1834 }
1835 *pszBuf = '\0';
1836 return rc;
1837}
1838RT_EXPORT_SYMBOL(RTLogGetFlags);
1839
1840
1841/**
1842 * Updates the logger destination using the specified string.
1843 *
1844 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
1845 * @param pLogger Logger instance (NULL for default logger).
1846 * @param pszVar The value to parse.
1847 */
1848RTDECL(int) RTLogDestinations(PRTLOGGER pLogger, char const *pszVar)
1849{
1850 /*
1851 * Resolve defaults.
1852 */
1853 if (!pLogger)
1854 {
1855 pLogger = RTLogDefaultInstance();
1856 if (!pLogger)
1857 return VINF_SUCCESS;
1858 }
1859
1860 /*
1861 * Do the parsing.
1862 */
1863 while (*pszVar)
1864 {
1865 bool fNo;
1866 unsigned i;
1867
1868 /* skip blanks. */
1869 while (RT_C_IS_SPACE(*pszVar))
1870 pszVar++;
1871 if (!*pszVar)
1872 break;
1873
1874 /* check no prefix. */
1875 fNo = false;
1876 if (pszVar[0] == 'n' && pszVar[1] == 'o')
1877 {
1878 fNo = true;
1879 pszVar += 2;
1880 }
1881
1882 /* instruction. */
1883 for (i = 0; i < RT_ELEMENTS(s_aLogDst); i++)
1884 {
1885 size_t cchInstr = strlen(s_aLogDst[i].pszInstr);
1886 if (!strncmp(pszVar, s_aLogDst[i].pszInstr, cchInstr))
1887 {
1888 if (!fNo)
1889 pLogger->fDestFlags |= s_aLogDst[i].fFlag;
1890 else
1891 pLogger->fDestFlags &= ~s_aLogDst[i].fFlag;
1892 pszVar += cchInstr;
1893
1894 /* check for value. */
1895 while (RT_C_IS_SPACE(*pszVar))
1896 pszVar++;
1897 if (*pszVar == '=' || *pszVar == ':')
1898 {
1899 const char *pszEnd;
1900
1901 pszVar++;
1902 pszEnd = strchr(pszVar, ';');
1903 if (!pszEnd)
1904 pszEnd = strchr(pszVar, '\0');
1905# ifdef IN_RING3
1906 size_t cch = pszEnd - pszVar;
1907
1908 /* log file name */
1909 if (i == 0 /* file */ && !fNo)
1910 {
1911 AssertReturn(cch < sizeof(pLogger->pInt->szFilename), VERR_OUT_OF_RANGE);
1912 memcpy(pLogger->pInt->szFilename, pszVar, cch);
1913 pLogger->pInt->szFilename[cch] = '\0';
1914 }
1915 /* log directory */
1916 else if (i == 1 /* dir */ && !fNo)
1917 {
1918 char szTmp[sizeof(pLogger->pInt->szFilename)];
1919 const char *pszFile = RTPathFilename(pLogger->pInt->szFilename);
1920 size_t cchFile = pszFile ? strlen(pszFile) : 0;
1921 AssertReturn(cchFile + cch + 1 < sizeof(pLogger->pInt->szFilename), VERR_OUT_OF_RANGE);
1922 memcpy(szTmp, cchFile ? pszFile : "", cchFile + 1);
1923
1924 memcpy(pLogger->pInt->szFilename, pszVar, cch);
1925 pLogger->pInt->szFilename[cch] = '\0';
1926 RTPathStripTrailingSlash(pLogger->pInt->szFilename);
1927
1928 cch = strlen(pLogger->pInt->szFilename);
1929 pLogger->pInt->szFilename[cch++] = '/';
1930 memcpy(&pLogger->pInt->szFilename[cch], szTmp, cchFile);
1931 pLogger->pInt->szFilename[cch + cchFile] = '\0';
1932 }
1933 else if (i == 2 /* history */)
1934 {
1935 if (!fNo)
1936 {
1937 uint32_t cHistory = 0;
1938 char szTmp[32];
1939 int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszVar, cch);
1940 if (RT_SUCCESS(rc))
1941 rc = RTStrToUInt32Full(szTmp, 0, &cHistory);
1942 AssertMsgReturn(RT_SUCCESS(rc) && cHistory < _1M, ("Invalid history value %s (%Rrc)!\n", szTmp, rc), rc);
1943 pLogger->pInt->cHistory = cHistory;
1944 }
1945 else
1946 pLogger->pInt->cHistory = 0;
1947 }
1948 else if (i == 3 /* histsize */)
1949 {
1950 if (!fNo)
1951 {
1952 char szTmp[32];
1953 int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszVar, cch);
1954 if (RT_SUCCESS(rc))
1955 rc = RTStrToUInt64Full(szTmp, 0, &pLogger->pInt->cbHistoryFileMax);
1956 AssertMsgRCReturn(rc, ("Invalid history file size value %s (%Rrc)!\n", szTmp, rc), rc);
1957 if (pLogger->pInt->cbHistoryFileMax == 0)
1958 pLogger->pInt->cbHistoryFileMax = UINT64_MAX;
1959 }
1960 else
1961 pLogger->pInt->cbHistoryFileMax = UINT64_MAX;
1962 }
1963 else if (i == 4 /* histtime */)
1964 {
1965 if (!fNo)
1966 {
1967 char szTmp[32];
1968 int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszVar, cch);
1969 if (RT_SUCCESS(rc))
1970 rc = RTStrToUInt32Full(szTmp, 0, &pLogger->pInt->cSecsHistoryTimeSlot);
1971 AssertMsgRCReturn(rc, ("Invalid history time slot value %s (%Rrc)!\n", szTmp, rc), rc);
1972 if (pLogger->pInt->cSecsHistoryTimeSlot == 0)
1973 pLogger->pInt->cSecsHistoryTimeSlot = UINT32_MAX;
1974 }
1975 else
1976 pLogger->pInt->cSecsHistoryTimeSlot = UINT32_MAX;
1977 }
1978 else
1979 AssertMsgFailedReturn(("Invalid destination value! %s%s doesn't take a value!\n",
1980 fNo ? "no" : "", s_aLogDst[i].pszInstr),
1981 VERR_INVALID_PARAMETER);
1982# endif /* IN_RING3 */
1983 pszVar = pszEnd + (*pszEnd != '\0');
1984 }
1985 break;
1986 }
1987 }
1988
1989 /* assert known instruction */
1990 AssertMsgReturn(i < RT_ELEMENTS(s_aLogDst),
1991 ("Invalid destination value! unknown instruction %.20s\n", pszVar),
1992 VERR_INVALID_PARAMETER);
1993
1994 /* skip blanks and delimiters. */
1995 while (RT_C_IS_SPACE(*pszVar) || *pszVar == ';')
1996 pszVar++;
1997 } /* while more environment variable value left */
1998
1999 return VINF_SUCCESS;
2000}
2001RT_EXPORT_SYMBOL(RTLogDestinations);
2002
2003
2004/**
2005 * Get the current log destinations as a string.
2006 *
2007 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
2008 * @param pLogger Logger instance (NULL for default logger).
2009 * @param pszBuf The output buffer.
2010 * @param cchBuf The size of the output buffer. Must be greater
2011 * than 0.
2012 */
2013RTDECL(int) RTLogGetDestinations(PRTLOGGER pLogger, char *pszBuf, size_t cchBuf)
2014{
2015 bool fNotFirst = false;
2016 int rc = VINF_SUCCESS;
2017 uint32_t fDestFlags;
2018 unsigned i;
2019
2020 AssertReturn(cchBuf, VERR_INVALID_PARAMETER);
2021 *pszBuf = '\0';
2022
2023 /*
2024 * Resolve defaults.
2025 */
2026 if (!pLogger)
2027 {
2028 pLogger = RTLogDefaultInstance();
2029 if (!pLogger)
2030 return VINF_SUCCESS;
2031 }
2032
2033 /*
2034 * Add the flags in the list.
2035 */
2036 fDestFlags = pLogger->fDestFlags;
2037 for (i = 2; i < RT_ELEMENTS(s_aLogDst); i++)
2038 if (s_aLogDst[i].fFlag & fDestFlags)
2039 {
2040 if (fNotFirst)
2041 {
2042 rc = RTStrCopyP(&pszBuf, &cchBuf, " ");
2043 if (RT_FAILURE(rc))
2044 return rc;
2045 }
2046 rc = RTStrCopyP(&pszBuf, &cchBuf, s_aLogDst[i].pszInstr);
2047 if (RT_FAILURE(rc))
2048 return rc;
2049 fNotFirst = true;
2050 }
2051
2052# ifdef IN_RING3
2053 /*
2054 * Add the filename.
2055 */
2056 if (fDestFlags & RTLOGDEST_FILE)
2057 {
2058 rc = RTStrCopyP(&pszBuf, &cchBuf, fNotFirst ? " file=" : "file=");
2059 if (RT_FAILURE(rc))
2060 return rc;
2061 rc = RTStrCopyP(&pszBuf, &cchBuf, pLogger->pInt->szFilename);
2062 if (RT_FAILURE(rc))
2063 return rc;
2064 fNotFirst = true;
2065 }
2066
2067 if (fDestFlags & RTLOGDEST_FILE)
2068 {
2069 char szNum[32];
2070 if (pLogger->pInt->cHistory)
2071 {
2072 RTStrPrintf(szNum, sizeof(szNum), fNotFirst ? "history=%u" : " history=%u", pLogger->pInt->cHistory);
2073 rc = RTStrCopyP(&pszBuf, &cchBuf, szNum);
2074 if (RT_FAILURE(rc))
2075 return rc;
2076 }
2077 if (pLogger->pInt->cbHistoryFileMax != UINT64_MAX)
2078 {
2079 RTStrPrintf(szNum, sizeof(szNum), fNotFirst ? "histsize=%llu" : " histsize=%llu", pLogger->pInt->cbHistoryFileMax);
2080 rc = RTStrCopyP(&pszBuf, &cchBuf, szNum);
2081 if (RT_FAILURE(rc))
2082 return rc;
2083 }
2084 if (pLogger->pInt->cSecsHistoryTimeSlot != UINT32_MAX)
2085 {
2086 RTStrPrintf(szNum, sizeof(szNum), fNotFirst ? "histtime=%llu" : " histtime=%llu", pLogger->pInt->cSecsHistoryTimeSlot);
2087 rc = RTStrCopyP(&pszBuf, &cchBuf, szNum);
2088 if (RT_FAILURE(rc))
2089 return rc;
2090 }
2091 }
2092# endif /* IN_RING3 */
2093
2094 return VINF_SUCCESS;
2095}
2096RT_EXPORT_SYMBOL(RTLogGetDestinations);
2097
2098#endif /* !IN_RC */
2099
2100/**
2101 * Flushes the specified logger.
2102 *
2103 * @param pLogger The logger instance to flush.
2104 * If NULL the default instance is used. The default instance
2105 * will not be initialized by this call.
2106 */
2107RTDECL(void) RTLogFlush(PRTLOGGER pLogger)
2108{
2109 /*
2110 * Resolve defaults.
2111 */
2112 if (!pLogger)
2113 {
2114#ifdef IN_RC
2115 pLogger = &g_Logger;
2116#else
2117 pLogger = g_pLogger;
2118#endif
2119 if (!pLogger)
2120 return;
2121 }
2122
2123 /*
2124 * Any thing to flush?
2125 */
2126 if (pLogger->offScratch)
2127 {
2128#ifndef IN_RC
2129 /*
2130 * Acquire logger instance sem.
2131 */
2132 int rc = rtlogLock(pLogger);
2133 if (RT_FAILURE(rc))
2134 return;
2135#endif
2136 /*
2137 * Call worker.
2138 */
2139 rtlogFlush(pLogger);
2140
2141#ifndef IN_RC
2142 /*
2143 * Release the semaphore.
2144 */
2145 rtlogUnlock(pLogger);
2146#endif
2147 }
2148}
2149RT_EXPORT_SYMBOL(RTLogFlush);
2150
2151
2152/**
2153 * Gets the default logger instance, creating it if necessary.
2154 *
2155 * @returns Pointer to default logger instance.
2156 * @returns NULL if no default logger instance available.
2157 */
2158RTDECL(PRTLOGGER) RTLogDefaultInstance(void)
2159{
2160#ifdef IN_RC
2161 return &g_Logger;
2162
2163#else /* !IN_RC */
2164# ifdef IN_RING0
2165 /*
2166 * Check per thread loggers first.
2167 */
2168 if (g_cPerThreadLoggers)
2169 {
2170 const RTNATIVETHREAD Self = RTThreadNativeSelf();
2171 int32_t i = RT_ELEMENTS(g_aPerThreadLoggers);
2172 while (i-- > 0)
2173 if (g_aPerThreadLoggers[i].NativeThread == Self)
2174 return g_aPerThreadLoggers[i].pLogger;
2175 }
2176# endif /* IN_RING0 */
2177
2178 /*
2179 * If no per thread logger, use the default one.
2180 */
2181 if (!g_pLogger)
2182 g_pLogger = RTLogDefaultInit();
2183 return g_pLogger;
2184#endif /* !IN_RC */
2185}
2186RT_EXPORT_SYMBOL(RTLogDefaultInstance);
2187
2188
2189/**
2190 * Gets the default logger instance.
2191 *
2192 * @returns Pointer to default logger instance.
2193 * @returns NULL if no default logger instance available.
2194 */
2195RTDECL(PRTLOGGER) RTLogGetDefaultInstance(void)
2196{
2197#ifdef IN_RC
2198 return &g_Logger;
2199#else
2200# ifdef IN_RING0
2201 /*
2202 * Check per thread loggers first.
2203 */
2204 if (g_cPerThreadLoggers)
2205 {
2206 const RTNATIVETHREAD Self = RTThreadNativeSelf();
2207 int32_t i = RT_ELEMENTS(g_aPerThreadLoggers);
2208 while (i-- > 0)
2209 if (g_aPerThreadLoggers[i].NativeThread == Self)
2210 return g_aPerThreadLoggers[i].pLogger;
2211 }
2212# endif /* IN_RING0 */
2213
2214 return g_pLogger;
2215#endif
2216}
2217RT_EXPORT_SYMBOL(RTLogGetDefaultInstance);
2218
2219
2220#ifndef IN_RC
2221/**
2222 * Sets the default logger instance.
2223 *
2224 * @returns iprt status code.
2225 * @param pLogger The new default logger instance.
2226 */
2227RTDECL(PRTLOGGER) RTLogSetDefaultInstance(PRTLOGGER pLogger)
2228{
2229 return ASMAtomicXchgPtrT(&g_pLogger, pLogger, PRTLOGGER);
2230}
2231RT_EXPORT_SYMBOL(RTLogSetDefaultInstance);
2232#endif /* !IN_RC */
2233
2234
2235#ifdef IN_RING0
2236/**
2237 * Changes the default logger instance for the current thread.
2238 *
2239 * @returns IPRT status code.
2240 * @param pLogger The logger instance. Pass NULL for deregistration.
2241 * @param uKey Associated key for cleanup purposes. If pLogger is NULL,
2242 * all instances with this key will be deregistered. So in
2243 * order to only deregister the instance associated with the
2244 * current thread use 0.
2245 */
2246RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey)
2247{
2248 int rc;
2249 RTNATIVETHREAD Self = RTThreadNativeSelf();
2250 if (pLogger)
2251 {
2252 int32_t i;
2253 unsigned j;
2254
2255 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
2256
2257 /*
2258 * Iterate the table to see if there is already an entry for this thread.
2259 */
2260 i = RT_ELEMENTS(g_aPerThreadLoggers);
2261 while (i-- > 0)
2262 if (g_aPerThreadLoggers[i].NativeThread == Self)
2263 {
2264 ASMAtomicWritePtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
2265 g_aPerThreadLoggers[i].pLogger = pLogger;
2266 return VINF_SUCCESS;
2267 }
2268
2269 /*
2270 * Allocate a new table entry.
2271 */
2272 i = ASMAtomicIncS32(&g_cPerThreadLoggers);
2273 if (i > (int32_t)RT_ELEMENTS(g_aPerThreadLoggers))
2274 {
2275 ASMAtomicDecS32(&g_cPerThreadLoggers);
2276 return VERR_BUFFER_OVERFLOW; /* horrible error code! */
2277 }
2278
2279 for (j = 0; j < 10; j++)
2280 {
2281 i = RT_ELEMENTS(g_aPerThreadLoggers);
2282 while (i-- > 0)
2283 {
2284 AssertCompile(sizeof(RTNATIVETHREAD) == sizeof(void*));
2285 if ( g_aPerThreadLoggers[i].NativeThread == NIL_RTNATIVETHREAD
2286 && ASMAtomicCmpXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)Self, (void *)NIL_RTNATIVETHREAD))
2287 {
2288 ASMAtomicWritePtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
2289 ASMAtomicWritePtr(&g_aPerThreadLoggers[i].pLogger, pLogger);
2290 return VINF_SUCCESS;
2291 }
2292 }
2293 }
2294
2295 ASMAtomicDecS32(&g_cPerThreadLoggers);
2296 rc = VERR_INTERNAL_ERROR;
2297 }
2298 else
2299 {
2300 /*
2301 * Search the array for the current thread.
2302 */
2303 int32_t i = RT_ELEMENTS(g_aPerThreadLoggers);
2304 while (i-- > 0)
2305 if ( g_aPerThreadLoggers[i].NativeThread == Self
2306 || g_aPerThreadLoggers[i].uKey == uKey)
2307 {
2308 ASMAtomicWriteNullPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey);
2309 ASMAtomicWriteNullPtr(&g_aPerThreadLoggers[i].pLogger);
2310 ASMAtomicWriteHandle(&g_aPerThreadLoggers[i].NativeThread, NIL_RTNATIVETHREAD);
2311 ASMAtomicDecS32(&g_cPerThreadLoggers);
2312 }
2313
2314 rc = VINF_SUCCESS;
2315 }
2316 return rc;
2317}
2318RT_EXPORT_SYMBOL(RTLogSetDefaultInstanceThread);
2319#endif /* IN_RING0 */
2320
2321
2322/**
2323 * Write to a logger instance.
2324 *
2325 * @param pLogger Pointer to logger instance.
2326 * @param pszFormat Format string.
2327 * @param args Format arguments.
2328 */
2329RTDECL(void) RTLogLoggerV(PRTLOGGER pLogger, const char *pszFormat, va_list args)
2330{
2331 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
2332}
2333RT_EXPORT_SYMBOL(RTLogLoggerV);
2334
2335
2336/**
2337 * Write to a logger instance.
2338 *
2339 * This function will check whether the instance, group and flags makes up a
2340 * logging kind which is currently enabled before writing anything to the log.
2341 *
2342 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.
2343 * @param fFlags The logging flags.
2344 * @param iGroup The group.
2345 * The value ~0U is reserved for compatibility with RTLogLogger[V] and is
2346 * only for internal usage!
2347 * @param pszFormat Format string.
2348 * @param args Format arguments.
2349 */
2350RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
2351{
2352 int rc;
2353
2354 /*
2355 * A NULL logger means default instance.
2356 */
2357 if (!pLogger)
2358 {
2359 pLogger = RTLogDefaultInstance();
2360 if (!pLogger)
2361 return;
2362 }
2363
2364 /*
2365 * Validate and correct iGroup.
2366 */
2367 if (iGroup != ~0U && iGroup >= pLogger->cGroups)
2368 iGroup = 0;
2369
2370 /*
2371 * If no output, then just skip it.
2372 */
2373 if ( (pLogger->fFlags & RTLOGFLAGS_DISABLED)
2374#ifndef IN_RC
2375 || !pLogger->fDestFlags
2376#endif
2377 || !pszFormat || !*pszFormat)
2378 return;
2379 if ( iGroup != ~0U
2380 && (pLogger->afGroups[iGroup] & (fFlags | RTLOGGRPFLAGS_ENABLED)) != (fFlags | RTLOGGRPFLAGS_ENABLED))
2381 return;
2382
2383 /*
2384 * Acquire logger instance sem.
2385 */
2386 rc = rtlogLock(pLogger);
2387 if (RT_FAILURE(rc))
2388 {
2389#ifdef IN_RING0
2390 if (pLogger->fDestFlags & ~RTLOGDEST_FILE)
2391 rtR0LogLoggerExFallback(pLogger->fDestFlags, pLogger->fFlags, pszFormat, args);
2392#endif
2393 return;
2394 }
2395
2396 /*
2397 * Check restrictions and call worker.
2398 */
2399#ifndef IN_RC
2400 if (RT_UNLIKELY( (pLogger->fFlags & RTLOGFLAGS_RESTRICT_GROUPS)
2401 && iGroup < pLogger->cGroups
2402 && (pLogger->afGroups[iGroup] & RTLOGGRPFLAGS_RESTRICT)
2403 && ++pLogger->pInt->pacEntriesPerGroup[iGroup] >= pLogger->pInt->cMaxEntriesPerGroup ))
2404 {
2405 uint32_t cEntries = pLogger->pInt->pacEntriesPerGroup[iGroup];
2406 if (cEntries > pLogger->pInt->cMaxEntriesPerGroup)
2407 pLogger->pInt->pacEntriesPerGroup[iGroup] = cEntries - 1;
2408 else
2409 {
2410 rtlogLoggerExVLocked(pLogger, fFlags, iGroup, pszFormat, args);
2411 if ( pLogger->pInt->papszGroups
2412 && pLogger->pInt->papszGroups[iGroup])
2413 rtlogLoggerExFLocked(pLogger, fFlags, iGroup, "%u messages from group %s (#%u), muting it.\n",
2414 cEntries, pLogger->pInt->papszGroups[iGroup], iGroup);
2415 else
2416 rtlogLoggerExFLocked(pLogger, fFlags, iGroup, "%u messages from group #%u, muting it.\n",
2417 cEntries, iGroup);
2418 }
2419 }
2420 else
2421#endif
2422 rtlogLoggerExVLocked(pLogger, fFlags, iGroup, pszFormat, args);
2423
2424 /*
2425 * Release the semaphore.
2426 */
2427 rtlogUnlock(pLogger);
2428}
2429RT_EXPORT_SYMBOL(RTLogLoggerExV);
2430
2431
2432#ifdef IN_RING0
2433/**
2434 * For rtR0LogLoggerExFallbackOutput and rtR0LogLoggerExFallbackFlush.
2435 */
2436typedef struct RTR0LOGLOGGERFALLBACK
2437{
2438 /** The current scratch buffer offset. */
2439 uint32_t offScratch;
2440 /** The destination flags. */
2441 uint32_t fDestFlags;
2442 /** The scratch buffer. */
2443 char achScratch[80];
2444} RTR0LOGLOGGERFALLBACK;
2445/** Pointer to RTR0LOGLOGGERFALLBACK which is used by
2446 * rtR0LogLoggerExFallbackOutput. */
2447typedef RTR0LOGLOGGERFALLBACK *PRTR0LOGLOGGERFALLBACK;
2448
2449
2450/**
2451 * Flushes the fallback buffer.
2452 *
2453 * @param pThis The scratch buffer.
2454 */
2455static void rtR0LogLoggerExFallbackFlush(PRTR0LOGLOGGERFALLBACK pThis)
2456{
2457 if (!pThis->offScratch)
2458 return;
2459
2460 if (pThis->fDestFlags & RTLOGDEST_USER)
2461 RTLogWriteUser(pThis->achScratch, pThis->offScratch);
2462
2463 if (pThis->fDestFlags & RTLOGDEST_DEBUGGER)
2464 RTLogWriteDebugger(pThis->achScratch, pThis->offScratch);
2465
2466 if (pThis->fDestFlags & RTLOGDEST_STDOUT)
2467 RTLogWriteStdOut(pThis->achScratch, pThis->offScratch);
2468
2469 if (pThis->fDestFlags & RTLOGDEST_STDERR)
2470 RTLogWriteStdErr(pThis->achScratch, pThis->offScratch);
2471
2472# ifndef LOG_NO_COM
2473 if (pThis->fDestFlags & RTLOGDEST_COM)
2474 RTLogWriteCom(pThis->achScratch, pThis->offScratch);
2475# endif
2476
2477 /* empty the buffer. */
2478 pThis->offScratch = 0;
2479}
2480
2481
2482/**
2483 * Callback for RTLogFormatV used by rtR0LogLoggerExFallback.
2484 * See PFNLOGOUTPUT() for details.
2485 */
2486static DECLCALLBACK(size_t) rtR0LogLoggerExFallbackOutput(void *pv, const char *pachChars, size_t cbChars)
2487{
2488 PRTR0LOGLOGGERFALLBACK pThis = (PRTR0LOGLOGGERFALLBACK)pv;
2489 if (cbChars)
2490 {
2491 size_t cbRet = 0;
2492 for (;;)
2493 {
2494 /* how much */
2495 uint32_t cb = sizeof(pThis->achScratch) - pThis->offScratch - 1; /* minus 1 - for the string terminator. */
2496 if (cb > cbChars)
2497 cb = (uint32_t)cbChars;
2498
2499 /* copy */
2500 memcpy(&pThis->achScratch[pThis->offScratch], pachChars, cb);
2501
2502 /* advance */
2503 pThis->offScratch += cb;
2504 cbRet += cb;
2505 cbChars -= cb;
2506
2507 /* done? */
2508 if (cbChars <= 0)
2509 return cbRet;
2510
2511 pachChars += cb;
2512
2513 /* flush */
2514 pThis->achScratch[pThis->offScratch] = '\0';
2515 rtR0LogLoggerExFallbackFlush(pThis);
2516 }
2517
2518 /* won't ever get here! */
2519 }
2520 else
2521 {
2522 /*
2523 * Termination call, flush the log.
2524 */
2525 pThis->achScratch[pThis->offScratch] = '\0';
2526 rtR0LogLoggerExFallbackFlush(pThis);
2527 return 0;
2528 }
2529}
2530
2531
2532/**
2533 * Ring-0 fallback for cases where we're unable to grab the lock.
2534 *
2535 * This will happen when we're at a too high IRQL on Windows for instance and
2536 * needs to be dealt with or we'll drop a lot of log output. This fallback will
2537 * only output to some of the log destinations as a few of them may be doing
2538 * dangerous things. We won't be doing any prefixing here either, at least not
2539 * for the present, because it's too much hassle.
2540 *
2541 * @param fDestFlags The destination flags.
2542 * @param fFlags The logger flags.
2543 * @param pszFormat The format string.
2544 * @param va The format arguments.
2545 */
2546static void rtR0LogLoggerExFallback(uint32_t fDestFlags, uint32_t fFlags, const char *pszFormat, va_list va)
2547{
2548 RTR0LOGLOGGERFALLBACK This;
2549 This.fDestFlags = fDestFlags;
2550
2551 /* fallback indicator. */
2552 This.offScratch = 2;
2553 This.achScratch[0] = '[';
2554 This.achScratch[1] = 'F';
2555
2556 /* selected prefixes */
2557 if (fFlags & RTLOGFLAGS_PREFIX_PID)
2558 {
2559 RTPROCESS Process = RTProcSelf();
2560 This.achScratch[This.offScratch++] = ' ';
2561 This.offScratch += RTStrFormatNumber(&This.achScratch[This.offScratch], Process, 16, sizeof(RTPROCESS) * 2, 0, RTSTR_F_ZEROPAD);
2562 }
2563 if (fFlags & RTLOGFLAGS_PREFIX_TID)
2564 {
2565 RTNATIVETHREAD Thread = RTThreadNativeSelf();
2566 This.achScratch[This.offScratch++] = ' ';
2567 This.offScratch += RTStrFormatNumber(&This.achScratch[This.offScratch], Thread, 16, sizeof(RTNATIVETHREAD) * 2, 0, RTSTR_F_ZEROPAD);
2568 }
2569
2570 This.achScratch[This.offScratch++] = ']';
2571 This.achScratch[This.offScratch++] = ' ';
2572
2573 RTLogFormatV(rtR0LogLoggerExFallbackOutput, &This, pszFormat, va);
2574}
2575#endif /* IN_RING0 */
2576
2577
2578/**
2579 * vprintf like function for writing to the default log.
2580 *
2581 * @param pszFormat Printf like format string.
2582 * @param args Optional arguments as specified in pszFormat.
2583 *
2584 * @remark The API doesn't support formatting of floating point numbers at the moment.
2585 */
2586RTDECL(void) RTLogPrintfV(const char *pszFormat, va_list args)
2587{
2588 RTLogLoggerV(NULL, pszFormat, args);
2589}
2590RT_EXPORT_SYMBOL(RTLogPrintfV);
2591
2592#ifdef IN_RING3
2593
2594/**
2595 * Opens/creates the log file.
2596 *
2597 * @param pLogger The logger instance to update. NULL is not allowed!
2598 * @param pszErrorMsg A buffer which is filled with an error message if
2599 * something fails. May be NULL.
2600 * @param cchErrorMsg The size of the error message buffer.
2601 */
2602static int rtlogFileOpen(PRTLOGGER pLogger, char *pszErrorMsg, size_t cchErrorMsg)
2603{
2604 uint32_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
2605 if (pLogger->fFlags & RTLOGFLAGS_APPEND)
2606 fOpen |= RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND;
2607 else
2608 fOpen |= RTFILE_O_CREATE_REPLACE;
2609 if (pLogger->fFlags & RTLOGFLAGS_WRITE_THROUGH)
2610 fOpen |= RTFILE_O_WRITE_THROUGH;
2611
2612 int rc = RTFileOpen(&pLogger->pInt->hFile, pLogger->pInt->szFilename, fOpen);
2613 if (RT_FAILURE(rc))
2614 {
2615 pLogger->pInt->hFile = NIL_RTFILE;
2616 if (pszErrorMsg)
2617 RTStrPrintf(pszErrorMsg, cchErrorMsg, N_("could not open file '%s' (fOpen=%#x)"), pLogger->pInt->szFilename, fOpen);
2618 }
2619 else
2620 {
2621 rc = RTFileGetSize(pLogger->pInt->hFile, &pLogger->pInt->cbHistoryFileWritten);
2622 if (RT_FAILURE(rc))
2623 {
2624 /* Don't complain if this fails, assume the file is empty. */
2625 pLogger->pInt->cbHistoryFileWritten = 0;
2626 rc = VINF_SUCCESS;
2627 }
2628 }
2629 return rc;
2630}
2631
2632
2633/**
2634 * Closes, rotates and opens the log files if necessary.
2635 *
2636 * Used by the rtlogFlush() function as well as RTLogCreateExV.
2637 *
2638 * @param pLogger The logger instance to update. NULL is not allowed!
2639 * @param uTimeSlit Current time slot (for tikme based rotation).
2640 * @param fFirst Flag whether this is the beginning of logging, i.e.
2641 * called from RTLogCreateExV. Prevents pfnPhase from
2642 * being called.
2643 */
2644static void rtlogRotate(PRTLOGGER pLogger, uint32_t uTimeSlot, bool fFirst)
2645{
2646 /* Suppress rotating empty log files simply because the time elapsed. */
2647 if (RT_UNLIKELY(!pLogger->pInt->cbHistoryFileWritten))
2648 pLogger->pInt->uHistoryTimeSlotStart = uTimeSlot;
2649
2650 /* Check rotation condition: file still small enough and not too old? */
2651 if (RT_LIKELY( pLogger->pInt->cbHistoryFileWritten < pLogger->pInt->cbHistoryFileMax
2652 && uTimeSlot == pLogger->pInt->uHistoryTimeSlotStart))
2653 return;
2654
2655 /*
2656 * Save "disabled" log flag and make sure logging is disabled.
2657 * The logging in the functions called during log file history
2658 * rotation would cause severe trouble otherwise.
2659 */
2660 uint32_t const fSavedFlags = pLogger->fFlags;
2661 pLogger->fFlags |= RTLOGFLAGS_DISABLED;
2662
2663 /*
2664 * Disable log rotation temporarily, otherwise with extreme settings and
2665 * chatty phase logging we could run into endless rotation.
2666 */
2667 uint32_t const cSavedHistory = pLogger->pInt->cHistory;
2668 pLogger->pInt->cHistory = 0;
2669
2670 /*
2671 * Close the old log file.
2672 */
2673 if (pLogger->pInt->hFile != NIL_RTFILE)
2674 {
2675 /* Use the callback to generate some final log contents, but only if
2676 * this is a rotation with a fully set up logger. Leave the other case
2677 * to the RTLogCreateExV function. */
2678 if (pLogger->pInt->pfnPhase && !fFirst)
2679 {
2680 uint32_t fODestFlags = pLogger->fDestFlags;
2681 pLogger->fDestFlags &= RTLOGDEST_FILE;
2682 pLogger->pInt->pfnPhase(pLogger, RTLOGPHASE_PREROTATE, rtlogPhaseMsgLocked);
2683 pLogger->fDestFlags = fODestFlags;
2684 }
2685 RTFileClose(pLogger->pInt->hFile);
2686 pLogger->pInt->hFile = NIL_RTFILE;
2687 }
2688
2689 if (cSavedHistory)
2690 {
2691 /*
2692 * Rotate the log files.
2693 */
2694 for (uint32_t i = cSavedHistory - 1; i + 1 > 0; i--)
2695 {
2696 char szOldName[sizeof(pLogger->pInt->szFilename) + 32];
2697 if (i > 0)
2698 RTStrPrintf(szOldName, sizeof(szOldName), "%s.%u", pLogger->pInt->szFilename, i);
2699 else
2700 RTStrCopy(szOldName, sizeof(szOldName), pLogger->pInt->szFilename);
2701
2702 char szNewName[sizeof(pLogger->pInt->szFilename) + 32];
2703 RTStrPrintf(szNewName, sizeof(szNewName), "%s.%u", pLogger->pInt->szFilename, i + 1);
2704 if ( RTFileRename(szOldName, szNewName, RTFILEMOVE_FLAGS_REPLACE)
2705 == VERR_FILE_NOT_FOUND)
2706 RTFileDelete(szNewName);
2707 }
2708
2709 /*
2710 * Delete excess log files.
2711 */
2712 for (uint32_t i = cSavedHistory + 1; ; i++)
2713 {
2714 char szExcessName[sizeof(pLogger->pInt->szFilename) + 32];
2715 RTStrPrintf(szExcessName, sizeof(szExcessName), "%s.%u", pLogger->pInt->szFilename, i);
2716 int rc = RTFileDelete(szExcessName);
2717 if (RT_FAILURE(rc))
2718 break;
2719 }
2720 }
2721
2722 /*
2723 * Update logger state and create new log file.
2724 */
2725 pLogger->pInt->cbHistoryFileWritten = 0;
2726 pLogger->pInt->uHistoryTimeSlotStart = uTimeSlot;
2727 rtlogFileOpen(pLogger, NULL, 0);
2728
2729 /*
2730 * Use the callback to generate some initial log contents, but only if this
2731 * is a rotation with a fully set up logger. Leave the other case to the
2732 * RTLogCreateExV function.
2733 */
2734 if (pLogger->pInt->pfnPhase && !fFirst)
2735 {
2736 uint32_t const fSavedDestFlags = pLogger->fDestFlags;
2737 pLogger->fDestFlags &= RTLOGDEST_FILE;
2738 pLogger->pInt->pfnPhase(pLogger, RTLOGPHASE_POSTROTATE, rtlogPhaseMsgLocked);
2739 pLogger->fDestFlags = fSavedDestFlags;
2740 }
2741
2742 /* Restore saved values. */
2743 pLogger->pInt->cHistory = cSavedHistory;
2744 pLogger->fFlags = fSavedFlags;
2745}
2746
2747#endif /* IN_RING3 */
2748
2749/**
2750 * Writes the buffer to the given log device without checking for buffered
2751 * data or anything.
2752 * Used by the RTLogFlush() function.
2753 *
2754 * @param pLogger The logger instance to write to. NULL is not allowed!
2755 */
2756static void rtlogFlush(PRTLOGGER pLogger)
2757{
2758 if (pLogger->offScratch == 0)
2759 return; /* nothing to flush. */
2760
2761#ifndef IN_RC
2762 if (pLogger->fDestFlags & RTLOGDEST_USER)
2763 RTLogWriteUser(pLogger->achScratch, pLogger->offScratch);
2764
2765 if (pLogger->fDestFlags & RTLOGDEST_DEBUGGER)
2766 RTLogWriteDebugger(pLogger->achScratch, pLogger->offScratch);
2767
2768# ifdef IN_RING3
2769 if (pLogger->fDestFlags & RTLOGDEST_FILE)
2770 {
2771 if (pLogger->pInt->hFile != NIL_RTFILE)
2772 {
2773 RTFileWrite(pLogger->pInt->hFile, pLogger->achScratch, pLogger->offScratch, NULL);
2774 if (pLogger->fFlags & RTLOGFLAGS_FLUSH)
2775 RTFileFlush(pLogger->pInt->hFile);
2776 }
2777 if (pLogger->pInt->cHistory)
2778 pLogger->pInt->cbHistoryFileWritten += pLogger->offScratch;
2779 }
2780# endif
2781
2782 if (pLogger->fDestFlags & RTLOGDEST_STDOUT)
2783 RTLogWriteStdOut(pLogger->achScratch, pLogger->offScratch);
2784
2785 if (pLogger->fDestFlags & RTLOGDEST_STDERR)
2786 RTLogWriteStdErr(pLogger->achScratch, pLogger->offScratch);
2787
2788# if (defined(IN_RING0) || defined(IN_RC)) && !defined(LOG_NO_COM)
2789 if (pLogger->fDestFlags & RTLOGDEST_COM)
2790 RTLogWriteCom(pLogger->achScratch, pLogger->offScratch);
2791# endif
2792#endif /* !IN_RC */
2793
2794#ifdef IN_RC
2795 if (pLogger->pfnFlush)
2796 pLogger->pfnFlush(pLogger);
2797#else
2798 if (pLogger->pInt->pfnFlush)
2799 pLogger->pInt->pfnFlush(pLogger);
2800#endif
2801
2802 /* empty the buffer. */
2803 pLogger->offScratch = 0;
2804
2805#ifdef IN_RING3
2806 /*
2807 * Rotate the log file if configured. Must be done after everything is
2808 * flushed, since this will also use logging/flushing to write the header
2809 * and footer messages.
2810 */
2811 if ( (pLogger->fDestFlags & RTLOGDEST_FILE)
2812 && pLogger->pInt->cHistory)
2813 rtlogRotate(pLogger, RTTimeProgramSecTS() / pLogger->pInt->cSecsHistoryTimeSlot, false /* fFirst */);
2814#endif
2815}
2816
2817
2818/**
2819 * Callback for RTLogFormatV which writes to the com port.
2820 * See PFNLOGOUTPUT() for details.
2821 */
2822static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars)
2823{
2824 PRTLOGGER pLogger = (PRTLOGGER)pv;
2825 if (cbChars)
2826 {
2827 size_t cbRet = 0;
2828 for (;;)
2829 {
2830#if defined(DEBUG) && defined(IN_RING3)
2831 /* sanity */
2832 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
2833 {
2834 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
2835 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
2836 AssertBreakpoint(); AssertBreakpoint();
2837 }
2838#endif
2839
2840 /* how much */
2841 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2842 if (cb > cbChars)
2843 cb = cbChars;
2844
2845 /* copy */
2846 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
2847
2848 /* advance */
2849 pLogger->offScratch += (uint32_t)cb;
2850 cbRet += cb;
2851 cbChars -= cb;
2852
2853 /* done? */
2854 if (cbChars <= 0)
2855 return cbRet;
2856
2857 pachChars += cb;
2858
2859 /* flush */
2860 rtlogFlush(pLogger);
2861 }
2862
2863 /* won't ever get here! */
2864 }
2865 else
2866 {
2867 /*
2868 * Termination call.
2869 * There's always space for a terminator, and it's not counted.
2870 */
2871 pLogger->achScratch[pLogger->offScratch] = '\0';
2872 return 0;
2873 }
2874}
2875
2876
2877/**
2878 * stpncpy implementation for use in rtLogOutputPrefixed w/ padding.
2879 *
2880 * @returns Pointer to the destination buffer byte following the copied string.
2881 * @param pszDst The destination buffer.
2882 * @param pszSrc The source string.
2883 * @param cchSrcMax The maximum number of characters to copy from
2884 * the string.
2885 * @param cchMinWidth The minimum field with, padd with spaces to
2886 * reach this.
2887 */
2888DECLINLINE(char *) rtLogStPNCpyPad(char *pszDst, const char *pszSrc, size_t cchSrcMax, size_t cchMinWidth)
2889{
2890 size_t cchSrc = 0;
2891 if (pszSrc)
2892 {
2893 cchSrc = strlen(pszSrc);
2894 if (cchSrc > cchSrcMax)
2895 cchSrc = cchSrcMax;
2896
2897 memcpy(pszDst, pszSrc, cchSrc);
2898 pszDst += cchSrc;
2899 }
2900 do
2901 *pszDst++ = ' ';
2902 while (cchSrc++ < cchMinWidth);
2903
2904 return pszDst;
2905}
2906
2907
2908
2909/**
2910 * Callback for RTLogFormatV which writes to the logger instance.
2911 * This version supports prefixes.
2912 *
2913 * See PFNLOGOUTPUT() for details.
2914 */
2915static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars)
2916{
2917 PRTLOGOUTPUTPREFIXEDARGS pArgs = (PRTLOGOUTPUTPREFIXEDARGS)pv;
2918 PRTLOGGER pLogger = pArgs->pLogger;
2919 if (cbChars)
2920 {
2921 size_t cbRet = 0;
2922 for (;;)
2923 {
2924 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2925 const char *pszNewLine;
2926 char *psz;
2927#ifdef IN_RC
2928 bool *pfPendingPrefix = &pLogger->fPendingPrefix;
2929#else
2930 bool *pfPendingPrefix = &pLogger->pInt->fPendingPrefix;
2931#endif
2932
2933 /*
2934 * Pending prefix?
2935 */
2936 if (*pfPendingPrefix)
2937 {
2938 *pfPendingPrefix = false;
2939
2940#if defined(DEBUG) && defined(IN_RING3)
2941 /* sanity */
2942 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
2943 {
2944 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
2945 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
2946 AssertBreakpoint(); AssertBreakpoint();
2947 }
2948#endif
2949
2950 /*
2951 * Flush the buffer if there isn't enough room for the maximum prefix config.
2952 * Max is 256, add a couple of extra bytes. See CCH_PREFIX check way below.
2953 */
2954 if (cb < 256 + 16)
2955 {
2956 rtlogFlush(pLogger);
2957 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2958 }
2959
2960 /*
2961 * Write the prefixes.
2962 * psz is pointing to the current position.
2963 */
2964 psz = &pLogger->achScratch[pLogger->offScratch];
2965 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TS)
2966 {
2967 uint64_t u64 = RTTimeNanoTS();
2968 int iBase = 16;
2969 unsigned int fFlags = RTSTR_F_ZEROPAD;
2970 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
2971 {
2972 iBase = 10;
2973 fFlags = 0;
2974 }
2975 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
2976 {
2977 static volatile uint64_t s_u64LastTs;
2978 uint64_t u64DiffTs = u64 - s_u64LastTs;
2979 s_u64LastTs = u64;
2980 /* We could have been preempted just before reading of s_u64LastTs by
2981 * another thread which wrote s_u64LastTs. In that case the difference
2982 * is negative which we simply ignore. */
2983 u64 = (int64_t)u64DiffTs < 0 ? 0 : u64DiffTs;
2984 }
2985 /* 1E15 nanoseconds = 11 days */
2986 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags);
2987 *psz++ = ' ';
2988 }
2989#define CCH_PREFIX_01 0 + 17
2990
2991 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TSC)
2992 {
2993#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
2994 uint64_t u64 = ASMReadTSC();
2995#else
2996 uint64_t u64 = RTTimeNanoTS();
2997#endif
2998 int iBase = 16;
2999 unsigned int fFlags = RTSTR_F_ZEROPAD;
3000 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
3001 {
3002 iBase = 10;
3003 fFlags = 0;
3004 }
3005 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
3006 {
3007 static volatile uint64_t s_u64LastTsc;
3008 int64_t i64DiffTsc = u64 - s_u64LastTsc;
3009 s_u64LastTsc = u64;
3010 /* We could have been preempted just before reading of s_u64LastTsc by
3011 * another thread which wrote s_u64LastTsc. In that case the difference
3012 * is negative which we simply ignore. */
3013 u64 = i64DiffTsc < 0 ? 0 : i64DiffTsc;
3014 }
3015 /* 1E15 ticks at 4GHz = 69 hours */
3016 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags);
3017 *psz++ = ' ';
3018 }
3019#define CCH_PREFIX_02 CCH_PREFIX_01 + 17
3020
3021 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_MS_PROG)
3022 {
3023#if defined(IN_RING3) || defined(IN_RC)
3024 uint64_t u64 = RTTimeProgramMilliTS();
3025#else
3026 uint64_t u64 = 0;
3027#endif
3028 /* 1E8 milliseconds = 27 hours */
3029 psz += RTStrFormatNumber(psz, u64, 10, 9, 0, RTSTR_F_ZEROPAD);
3030 *psz++ = ' ';
3031 }
3032#define CCH_PREFIX_03 CCH_PREFIX_02 + 21
3033
3034 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME)
3035 {
3036#if defined(IN_RING3) || defined(IN_RING0)
3037 RTTIMESPEC TimeSpec;
3038 RTTIME Time;
3039 RTTimeExplode(&Time, RTTimeNow(&TimeSpec));
3040 psz += RTStrFormatNumber(psz, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD);
3041 *psz++ = ':';
3042 psz += RTStrFormatNumber(psz, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD);
3043 *psz++ = ':';
3044 psz += RTStrFormatNumber(psz, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD);
3045 *psz++ = '.';
3046 psz += RTStrFormatNumber(psz, Time.u32Nanosecond / 1000, 10, 6, 0, RTSTR_F_ZEROPAD);
3047 *psz++ = ' ';
3048#else
3049 memset(psz, ' ', 16);
3050 psz += 16;
3051#endif
3052 }
3053#define CCH_PREFIX_04 CCH_PREFIX_03 + (3+1+3+1+3+1+7+1)
3054
3055 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME_PROG)
3056 {
3057
3058#if defined(IN_RING3) || defined(IN_RC)
3059 uint64_t u64 = RTTimeProgramMicroTS();
3060 psz += RTStrFormatNumber(psz, (uint32_t)(u64 / RT_US_1HOUR), 10, 2, 0, RTSTR_F_ZEROPAD);
3061 *psz++ = ':';
3062 uint32_t u32 = (uint32_t)(u64 % RT_US_1HOUR);
3063 psz += RTStrFormatNumber(psz, u32 / RT_US_1MIN, 10, 2, 0, RTSTR_F_ZEROPAD);
3064 *psz++ = ':';
3065 u32 %= RT_US_1MIN;
3066
3067 psz += RTStrFormatNumber(psz, u32 / RT_US_1SEC, 10, 2, 0, RTSTR_F_ZEROPAD);
3068 *psz++ = '.';
3069 psz += RTStrFormatNumber(psz, u32 % RT_US_1SEC, 10, 6, 0, RTSTR_F_ZEROPAD);
3070 *psz++ = ' ';
3071#else
3072 memset(psz, ' ', 16);
3073 psz += 16;
3074#endif
3075 }
3076#define CCH_PREFIX_05 CCH_PREFIX_04 + (9+1+2+1+2+1+6+1)
3077
3078# if 0
3079 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_DATETIME)
3080 {
3081 char szDate[32];
3082 RTTIMESPEC Time;
3083 RTTimeSpecToString(RTTimeNow(&Time), szDate, sizeof(szDate));
3084 size_t cch = strlen(szDate);
3085 memcpy(psz, szDate, cch);
3086 psz += cch;
3087 *psz++ = ' ';
3088 }
3089# define CCH_PREFIX_06 CCH_PREFIX_05 + 32
3090# else
3091# define CCH_PREFIX_06 CCH_PREFIX_05 + 0
3092# endif
3093
3094 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_PID)
3095 {
3096#ifndef IN_RC
3097 RTPROCESS Process = RTProcSelf();
3098#else
3099 RTPROCESS Process = NIL_RTPROCESS;
3100#endif
3101 psz += RTStrFormatNumber(psz, Process, 16, sizeof(RTPROCESS) * 2, 0, RTSTR_F_ZEROPAD);
3102 *psz++ = ' ';
3103 }
3104#define CCH_PREFIX_07 CCH_PREFIX_06 + 9
3105
3106 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TID)
3107 {
3108#ifndef IN_RC
3109 RTNATIVETHREAD Thread = RTThreadNativeSelf();
3110#else
3111 RTNATIVETHREAD Thread = NIL_RTNATIVETHREAD;
3112#endif
3113 psz += RTStrFormatNumber(psz, Thread, 16, sizeof(RTNATIVETHREAD) * 2, 0, RTSTR_F_ZEROPAD);
3114 *psz++ = ' ';
3115 }
3116#define CCH_PREFIX_08 CCH_PREFIX_07 + 17
3117
3118 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_THREAD)
3119 {
3120#ifdef IN_RING3
3121 const char *pszName = RTThreadSelfName();
3122#elif defined IN_RC
3123 const char *pszName = "EMT-RC";
3124#else
3125 const char *pszName = "R0";
3126#endif
3127 psz = rtLogStPNCpyPad(psz, pszName, 16, 8);
3128 }
3129#define CCH_PREFIX_09 CCH_PREFIX_08 + 17
3130
3131 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_CPUID)
3132 {
3133#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
3134 const uint8_t idCpu = ASMGetApicId();
3135#else
3136 const RTCPUID idCpu = RTMpCpuId();
3137#endif
3138 psz += RTStrFormatNumber(psz, idCpu, 16, sizeof(idCpu) * 2, 0, RTSTR_F_ZEROPAD);
3139 *psz++ = ' ';
3140 }
3141#define CCH_PREFIX_10 CCH_PREFIX_09 + 17
3142
3143#ifndef IN_RC
3144 if ( (pLogger->fFlags & RTLOGFLAGS_PREFIX_CUSTOM)
3145 && pLogger->pInt->pfnPrefix)
3146 {
3147 psz += pLogger->pInt->pfnPrefix(pLogger, psz, 31, pLogger->pInt->pvPrefixUserArg);
3148 *psz++ = ' '; /* +32 */
3149 }
3150#endif
3151#define CCH_PREFIX_11 CCH_PREFIX_10 + 32
3152
3153 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_LOCK_COUNTS)
3154 {
3155#ifdef IN_RING3 /** @todo implement these counters in ring-0 too? */
3156 RTTHREAD Thread = RTThreadSelf();
3157 if (Thread != NIL_RTTHREAD)
3158 {
3159 uint32_t cReadLocks = RTLockValidatorReadLockGetCount(Thread);
3160 uint32_t cWriteLocks = RTLockValidatorWriteLockGetCount(Thread) - g_cLoggerLockCount;
3161 cReadLocks = RT_MIN(0xfff, cReadLocks);
3162 cWriteLocks = RT_MIN(0xfff, cWriteLocks);
3163 psz += RTStrFormatNumber(psz, cReadLocks, 16, 1, 0, RTSTR_F_ZEROPAD);
3164 *psz++ = '/';
3165 psz += RTStrFormatNumber(psz, cWriteLocks, 16, 1, 0, RTSTR_F_ZEROPAD);
3166 }
3167 else
3168#endif
3169 {
3170 *psz++ = '?';
3171 *psz++ = '/';
3172 *psz++ = '?';
3173 }
3174 *psz++ = ' ';
3175 }
3176#define CCH_PREFIX_12 CCH_PREFIX_11 + 8
3177
3178 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG_NO)
3179 {
3180 psz += RTStrFormatNumber(psz, pArgs->fFlags, 16, 8, 0, RTSTR_F_ZEROPAD);
3181 *psz++ = ' ';
3182 }
3183#define CCH_PREFIX_13 CCH_PREFIX_12 + 9
3184
3185 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG)
3186 {
3187#ifdef IN_RING3
3188 const char *pszGroup = pArgs->iGroup != ~0U ? pLogger->pInt->papszGroups[pArgs->iGroup] : NULL;
3189#else
3190 const char *pszGroup = NULL;
3191#endif
3192 psz = rtLogStPNCpyPad(psz, pszGroup, 16, 8);
3193 }
3194#define CCH_PREFIX_14 CCH_PREFIX_13 + 17
3195
3196 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP_NO)
3197 {
3198 if (pArgs->iGroup != ~0U)
3199 {
3200 psz += RTStrFormatNumber(psz, pArgs->iGroup, 16, 3, 0, RTSTR_F_ZEROPAD);
3201 *psz++ = ' ';
3202 }
3203 else
3204 {
3205 memcpy(psz, "-1 ", sizeof("-1 ") - 1);
3206 psz += sizeof("-1 ") - 1;
3207 } /* +9 */
3208 }
3209#define CCH_PREFIX_15 CCH_PREFIX_14 + 9
3210
3211 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP)
3212 {
3213 const unsigned fGrp = pLogger->afGroups[pArgs->iGroup != ~0U ? pArgs->iGroup : 0];
3214 const char *pszGroup;
3215 size_t cch;
3216 switch (pArgs->fFlags & fGrp)
3217 {
3218 case 0: pszGroup = "--------"; cch = sizeof("--------") - 1; break;
3219 case RTLOGGRPFLAGS_ENABLED: pszGroup = "enabled" ; cch = sizeof("enabled" ) - 1; break;
3220 case RTLOGGRPFLAGS_LEVEL_1: pszGroup = "level 1" ; cch = sizeof("level 1" ) - 1; break;
3221 case RTLOGGRPFLAGS_LEVEL_2: pszGroup = "level 2" ; cch = sizeof("level 2" ) - 1; break;
3222 case RTLOGGRPFLAGS_LEVEL_3: pszGroup = "level 3" ; cch = sizeof("level 3" ) - 1; break;
3223 case RTLOGGRPFLAGS_LEVEL_4: pszGroup = "level 4" ; cch = sizeof("level 4" ) - 1; break;
3224 case RTLOGGRPFLAGS_LEVEL_5: pszGroup = "level 5" ; cch = sizeof("level 5" ) - 1; break;
3225 case RTLOGGRPFLAGS_LEVEL_6: pszGroup = "level 6" ; cch = sizeof("level 6" ) - 1; break;
3226 case RTLOGGRPFLAGS_FLOW: pszGroup = "flow" ; cch = sizeof("flow" ) - 1; break;
3227
3228 /* personal groups */
3229 case RTLOGGRPFLAGS_LELIK: pszGroup = "lelik" ; cch = sizeof("lelik" ) - 1; break;
3230 case RTLOGGRPFLAGS_MICHAEL: pszGroup = "Michael" ; cch = sizeof("Michael" ) - 1; break;
3231 case RTLOGGRPFLAGS_SUNLOVER: pszGroup = "sunlover"; cch = sizeof("sunlover") - 1; break;
3232 case RTLOGGRPFLAGS_ACHIM: pszGroup = "Achim" ; cch = sizeof("Achim" ) - 1; break;
3233 case RTLOGGRPFLAGS_SANDER: pszGroup = "Sander" ; cch = sizeof("Sander" ) - 1; break;
3234 case RTLOGGRPFLAGS_KLAUS: pszGroup = "Klaus" ; cch = sizeof("Klaus" ) - 1; break;
3235 case RTLOGGRPFLAGS_FRANK: pszGroup = "Frank" ; cch = sizeof("Frank" ) - 1; break;
3236 case RTLOGGRPFLAGS_BIRD: pszGroup = "bird" ; cch = sizeof("bird" ) - 1; break;
3237 case RTLOGGRPFLAGS_NONAME: pszGroup = "noname" ; cch = sizeof("noname" ) - 1; break;
3238 default: pszGroup = "????????"; cch = sizeof("????????") - 1; break;
3239 }
3240 psz = rtLogStPNCpyPad(psz, pszGroup, 16, 8);
3241 }
3242#define CCH_PREFIX_16 CCH_PREFIX_15 + 17
3243
3244#define CCH_PREFIX ( CCH_PREFIX_16 )
3245 AssertCompile(CCH_PREFIX < 256);
3246
3247 /*
3248 * Done, figure what we've used and advance the buffer and free size.
3249 */
3250 cb = psz - &pLogger->achScratch[pLogger->offScratch];
3251 AssertMsg(cb <= 223, ("%#zx (%zd) - fFlags=%#x\n", cb, cb, pLogger->fFlags));
3252 pLogger->offScratch += (uint32_t)cb;
3253 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
3254 }
3255 else if (cb <= 0)
3256 {
3257 rtlogFlush(pLogger);
3258 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
3259 }
3260
3261#if defined(DEBUG) && defined(IN_RING3)
3262 /* sanity */
3263 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
3264 {
3265 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
3266 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
3267 AssertBreakpoint(); AssertBreakpoint();
3268 }
3269#endif
3270
3271 /* how much */
3272 if (cb > cbChars)
3273 cb = cbChars;
3274
3275 /* have newline? */
3276 pszNewLine = (const char *)memchr(pachChars, '\n', cb);
3277 if (pszNewLine)
3278 {
3279 if (pLogger->fFlags & RTLOGFLAGS_USECRLF)
3280 cb = pszNewLine - pachChars;
3281 else
3282 {
3283 cb = pszNewLine - pachChars + 1;
3284 *pfPendingPrefix = true;
3285 }
3286 }
3287
3288 /* copy */
3289 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
3290
3291 /* advance */
3292 pLogger->offScratch += (uint32_t)cb;
3293 cbRet += cb;
3294 cbChars -= cb;
3295
3296 if ( pszNewLine
3297 && (pLogger->fFlags & RTLOGFLAGS_USECRLF)
3298 && pLogger->offScratch + 2 < sizeof(pLogger->achScratch))
3299 {
3300 memcpy(&pLogger->achScratch[pLogger->offScratch], "\r\n", 2);
3301 pLogger->offScratch += 2;
3302 cbRet++;
3303 cbChars--;
3304 cb++;
3305 *pfPendingPrefix = true;
3306 }
3307
3308 /* done? */
3309 if (cbChars <= 0)
3310 return cbRet;
3311 pachChars += cb;
3312 }
3313
3314 /* won't ever get here! */
3315 }
3316 else
3317 {
3318 /*
3319 * Termination call.
3320 * There's always space for a terminator, and it's not counted.
3321 */
3322 pLogger->achScratch[pLogger->offScratch] = '\0';
3323 return 0;
3324 }
3325}
3326
3327
3328/**
3329 * Write to a logger instance (worker function).
3330 *
3331 * This function will check whether the instance, group and flags makes up a
3332 * logging kind which is currently enabled before writing anything to the log.
3333 *
3334 * @param pLogger Pointer to logger instance. Must be non-NULL.
3335 * @param fFlags The logging flags.
3336 * @param iGroup The group.
3337 * The value ~0U is reserved for compatibility with RTLogLogger[V] and is
3338 * only for internal usage!
3339 * @param pszFormat Format string.
3340 * @param args Format arguments.
3341 */
3342static void rtlogLoggerExVLocked(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
3343{
3344 /*
3345 * Format the message and perhaps flush it.
3346 */
3347 if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF))
3348 {
3349 RTLOGOUTPUTPREFIXEDARGS OutputArgs;
3350 OutputArgs.pLogger = pLogger;
3351 OutputArgs.iGroup = iGroup;
3352 OutputArgs.fFlags = fFlags;
3353 RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);
3354 }
3355 else
3356 RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);
3357 if ( !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)
3358 && pLogger->offScratch)
3359 rtlogFlush(pLogger);
3360}
3361
3362
3363#ifndef IN_RC
3364/**
3365 * For calling rtlogLoggerExVLocked.
3366 *
3367 * @param pLogger The logger.
3368 * @param fFlags The logging flags.
3369 * @param iGroup The group.
3370 * The value ~0U is reserved for compatibility with RTLogLogger[V] and is
3371 * only for internal usage!
3372 * @param pszFormat Format string.
3373 * @param ... Format arguments.
3374 */
3375static void rtlogLoggerExFLocked(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
3376{
3377 va_list va;
3378 va_start(va, pszFormat);
3379 rtlogLoggerExVLocked(pLogger, fFlags, iGroup, pszFormat, va);
3380 va_end(va);
3381}
3382#endif /* !IN_RC */
3383
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