VirtualBox

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

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

IPRT/log: Added RTLogSetFlushCallback. bugref:10086

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

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