VirtualBox

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

Last change on this file since 103685 was 103685, checked in by vboxsync, 2 months ago

Backed out r161312 (no longer needed): iprt: Prevent array-index-out-of-bounds UBSAN warnings on Linux, bugref:10585.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use