VirtualBox

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

Last change on this file since 46054 was 46054, checked in by vboxsync, 12 years ago

Runtime/log: warning when compiling as plain C file

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