VirtualBox

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

Last change on this file since 72436 was 72426, checked in by vboxsync, 7 years ago

IPRT,VMM: Added custom thread name for ring-0 logging (VMM).

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