VirtualBox

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

Last change on this file since 60880 was 60880, checked in by vboxsync, 9 years ago

RTLogDestinations: Don't require ';' for separating values and more directives, space+valid_directive+space should suffice.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette