VirtualBox

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

Last change on this file was 104287, checked in by vboxsync, 8 days ago

IPRT/log,Main: Open the parent directory of the log file on Windows before we opening the log file and do any log rotating to prevent reparse hacks. [build fix] bugref:10632

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

© 2023 Oracle
ContactPrivacy policyTerms of Use