VirtualBox

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

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

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