VirtualBox

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

Last change on this file since 90829 was 90829, checked in by vboxsync, 3 years ago

IPRT,VMM,SUPDrv,++: Reworked the IPRT logger structure and how the VMM ring-0 uses it. bugref:10086

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