VirtualBox

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

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

IPRT/log.cpp: Use RTLogWriteStdErr instead of fprintf/stderr in IPRT_NO_CRT mode. bugref:10261

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

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