VirtualBox

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

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

IPRT: More unused parameters.

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

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