VirtualBox

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

最後變更 在這個檔案從69716是 69111,由 vboxsync 提交於 7 年 前

(C) year

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

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