3 * Runtime VBox - Logger.
7 * Copyright (C) 2006-2017 Oracle Corporation
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.
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.
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.
28 /*********************************************************************************************************************************
30 *********************************************************************************************************************************/
32 #include "internal/iprt.h"
35 # include <iprt/alloc.h>
36 # include <iprt/process.h>
37 # include <iprt/semaphore.h>
38 # include <iprt/thread.h>
42 # include <iprt/env.h>
43 # include <iprt/file.h>
44 # include <iprt/lockvalidator.h>
45 # include <iprt/path.h>
47 #include <iprt/time.h>
49 #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
50 # include <iprt/asm-amd64-x86.h>
52 #include <iprt/assert.h>
54 #include <iprt/param.h>
56 #include <iprt/stdarg.h>
57 #include <iprt/string.h>
58 #include <iprt/ctype.h>
60 # include <iprt/alloca.h>
65 /*********************************************************************************************************************************
66 * Defined Constants And Macros *
67 *********************************************************************************************************************************/
68 /** @def RTLOG_RINGBUF_DEFAULT_SIZE
69 * The default ring buffer size. */
70 /** @def RTLOG_RINGBUF_MAX_SIZE
71 * The max ring buffer size. */
72 /** @def RTLOG_RINGBUF_MIN_SIZE
73 * The min ring buffer size. */
75 # define RTLOG_RINGBUF_DEFAULT_SIZE _64K
76 # define RTLOG_RINGBUF_MAX_SIZE _4M
77 # define RTLOG_RINGBUF_MIN_SIZE _1K
78 #elif defined(IN_RING3) || defined(DOXYGEN_RUNNING)
79 # define RTLOG_RINGBUF_DEFAULT_SIZE _512K
80 # define RTLOG_RINGBUF_MAX_SIZE _1G
81 # define RTLOG_RINGBUF_MIN_SIZE _4K
83 /** The start of ring buffer eye catcher (16 bytes). */
84 #define RTLOG_RINGBUF_EYE_CATCHER "START RING BUF\0"
85 AssertCompile(sizeof(RTLOG_RINGBUF_EYE_CATCHER
) == 16);
86 /** The end of ring buffer eye catcher (16 bytes). This also ensures that the ring buffer
87 * forms are properly terminated C string (leading zero chars). */
88 #define RTLOG_RINGBUF_EYE_CATCHER_END "\0\0\0END RING BUF"
89 AssertCompile(sizeof(RTLOG_RINGBUF_EYE_CATCHER_END
) == 16);
92 /*********************************************************************************************************************************
93 * Structures and Typedefs *
94 *********************************************************************************************************************************/
96 * Arguments passed to the output function.
98 typedef struct RTLOGOUTPUTPREFIXEDARGS
100 /** The logger instance. */
102 /** The flags. (used for prefixing.) */
104 /** The group. (used for prefixing.) */
106 } RTLOGOUTPUTPREFIXEDARGS
, *PRTLOGOUTPUTPREFIXEDARGS
;
111 * Internal logger data.
113 * @remarks Don't make casual changes to this structure.
115 typedef struct RTLOGGERINTERNAL
117 /** The structure revision (RTLOGGERINTERNAL_REV). */
119 /** The size of the internal logger structure. */
122 /** Spinning mutex semaphore. Can be NIL. */
123 RTSEMSPINMUTEX hSpinMtx
;
124 /** Pointer to the flush function. */
125 PFNRTLOGFLUSH pfnFlush
;
127 /** Custom prefix callback. */
128 PFNRTLOGPREFIX pfnPrefix
;
129 /** Prefix callback argument. */
130 void *pvPrefixUserArg
;
131 /** This is set if a prefix is pending. */
133 /** Alignment padding. */
135 /** Set if fully created. Used to avoid confusing in a few functions used to
136 * parse logger settings from environment variables. */
139 /** The max number of groups that there is room for in afGroups and papszGroups.
140 * Used by RTLogCopyGroupAndFlags(). */
142 /** Pointer to the group name array.
143 * (The data is readonly and provided by the user.) */
144 const char * const *papszGroups
;
146 /** The number of log entries per group. NULL if
147 * RTLOGFLAGS_RESTRICT_GROUPS is not specified. */
148 uint32_t *pacEntriesPerGroup
;
149 /** The max number of entries per group. */
150 uint32_t cMaxEntriesPerGroup
;
152 /** @name Ring buffer logging
153 * The ring buffer records the last cbRingBuf - 1 of log output. The
154 * other configured log destinations are not touched until someone calls
155 * RTLogFlush(), when the ring buffer content is written to them all.
157 * The aim here is a fast logging destination, that avoids wasting storage
158 * space saving disk space when dealing with huge log volumes where the
159 * interesting bits usually are found near the end of the log. This is
160 * typically the case for scenarios that crashes or hits assertions.
162 * RTLogFlush() is called implicitly when hitting an assertion. While on a
163 * crash the most debuggers are able to make calls these days, it's usually
164 * possible to view the ring buffer memory.
167 /** Ring buffer size (including both eye catchers). */
169 /** Number of bytes passing thru the ring buffer since last RTLogFlush call.
170 * (This is used to avoid writing out the same bytes twice.) */
171 uint64_t volatile cbRingBufUnflushed
;
172 /** Ring buffer pointer (points at RTLOG_RINGBUF_EYE_CATCHER). */
174 /** Current ring buffer position (where to write the next char). */
175 char * volatile pchRingBufCur
;
178 # ifdef IN_RING3 /* Note! Must be at the end! */
179 /** @name File logging bits for the logger.
181 /** Pointer to the function called when starting logging, and when
182 * ending or starting a new log file as part of history rotation.
183 * This can be NULL. */
184 PFNRTLOGPHASE pfnPhase
;
186 /** Handle to log file (if open). */
188 /** Log file history settings: maximum amount of data to put in a file. */
189 uint64_t cbHistoryFileMax
;
190 /** Log file history settings: current amount of data in a file. */
191 uint64_t cbHistoryFileWritten
;
192 /** Log file history settings: maximum time to use a file (in seconds). */
193 uint32_t cSecsHistoryTimeSlot
;
194 /** Log file history settings: in what time slot was the file created. */
195 uint32_t uHistoryTimeSlotStart
;
196 /** Log file history settings: number of older files to keep.
197 * 0 means no history. */
199 /** Pointer to filename. */
200 char szFilename
[RTPATH_MAX
];
202 # endif /* IN_RING3 */
205 /** The revision of the internal logger structure. */
206 # define RTLOGGERINTERNAL_REV UINT32_C(10)
209 /** The size of the RTLOGGERINTERNAL structure in ring-0. */
210 # define RTLOGGERINTERNAL_R0_SIZE RT_OFFSETOF(RTLOGGERINTERNAL, pfnPhase)
211 AssertCompileMemberAlignment(RTLOGGERINTERNAL
, hFile
, sizeof(void *));
212 AssertCompileMemberAlignment(RTLOGGERINTERNAL
, cbHistoryFileMax
, sizeof(uint64_t));
214 AssertCompileMemberAlignment(RTLOGGERINTERNAL
, cbRingBufUnflushed
, sizeof(uint64_t));
219 /*********************************************************************************************************************************
220 * Internal Functions *
221 *********************************************************************************************************************************/
223 static unsigned rtlogGroupFlags(const char *psz
);
226 static void rtR0LogLoggerExFallback(uint32_t fDestFlags
, uint32_t fFlags
, PRTLOGGERINTERNAL pInt
,
227 const char *pszFormat
, va_list va
);
230 static int rtR3LogOpenFileDestination(PRTLOGGER pLogger
, PRTERRINFO pErrInfo
);
233 static void rtLogRingBufFlush(PRTLOGGER pLogger
);
235 static void rtlogFlush(PRTLOGGER pLogger
, bool fNeedSpace
);
236 static DECLCALLBACK(size_t) rtLogOutput(void *pv
, const char *pachChars
, size_t cbChars
);
237 static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv
, const char *pachChars
, size_t cbChars
);
238 static void rtlogLoggerExVLocked(PRTLOGGER pLogger
, unsigned fFlags
, unsigned iGroup
, const char *pszFormat
, va_list args
);
240 static void rtlogLoggerExFLocked(PRTLOGGER pLogger
, unsigned fFlags
, unsigned iGroup
, const char *pszFormat
, ...);
244 /*********************************************************************************************************************************
246 *********************************************************************************************************************************/
248 /** Default logger instance. Make it weak because our RC module loader does not
249 * necessarily resolve this symbol and the compiler _must_ check if this is
250 * the case or not. That doesn't work for Darwin (``incompatible feature used:
251 * .weak_reference (must specify "-dynamic" to be used'') */
253 extern "C" DECLIMPORT(RTLOGGERRC
) g_Logger
;
255 extern "C" DECLWEAK(DECLIMPORT(RTLOGGERRC
)) g_Logger
;
258 /** Default logger instance. */
259 static PRTLOGGER g_pLogger
;
262 /** The RTThreadGetWriteLockCount() change caused by the logger mutex semaphore. */
263 static uint32_t volatile g_cLoggerLockCount
;
267 /** Number of per-thread loggers. */
268 static int32_t volatile g_cPerThreadLoggers
;
269 /** Per-thread loggers.
270 * This is just a quick TLS hack suitable for debug logging only.
271 * If we run out of entries, just unload and reload the driver. */
272 static struct RTLOGGERPERTHREAD
275 RTNATIVETHREAD
volatile NativeThread
;
276 /** The (process / session) key. */
277 uintptr_t volatile uKey
;
278 /** The logger instance.*/
279 PRTLOGGER
volatile pLogger
;
280 } g_aPerThreadLoggers
[8] =
282 { NIL_RTNATIVETHREAD
, 0, 0},
283 { NIL_RTNATIVETHREAD
, 0, 0},
284 { NIL_RTNATIVETHREAD
, 0, 0},
285 { NIL_RTNATIVETHREAD
, 0, 0},
286 { NIL_RTNATIVETHREAD
, 0, 0},
287 { NIL_RTNATIVETHREAD
, 0, 0},
288 { NIL_RTNATIVETHREAD
, 0, 0},
289 { NIL_RTNATIVETHREAD
, 0, 0}
291 #endif /* IN_RING0 */
294 * Logger flags instructions.
298 const char *pszInstr
; /**< The name */
299 size_t cchInstr
; /**< The size of the name. */
300 uint32_t fFlag
; /**< The flag value. */
301 bool fInverted
; /**< Inverse meaning? */
302 } const g_aLogFlags
[] =
304 { "disabled", sizeof("disabled" ) - 1, RTLOGFLAGS_DISABLED
, false },
305 { "enabled", sizeof("enabled" ) - 1, RTLOGFLAGS_DISABLED
, true },
306 { "buffered", sizeof("buffered" ) - 1, RTLOGFLAGS_BUFFERED
, false },
307 { "unbuffered", sizeof("unbuffered" ) - 1, RTLOGFLAGS_BUFFERED
, true },
308 { "usecrlf", sizeof("usecrlf" ) - 1, RTLOGFLAGS_USECRLF
, false },
309 { "uself", sizeof("uself" ) - 1, RTLOGFLAGS_USECRLF
, true },
310 { "append", sizeof("append" ) - 1, RTLOGFLAGS_APPEND
, false },
311 { "overwrite", sizeof("overwrite" ) - 1, RTLOGFLAGS_APPEND
, true },
312 { "rel", sizeof("rel" ) - 1, RTLOGFLAGS_REL_TS
, false },
313 { "abs", sizeof("abs" ) - 1, RTLOGFLAGS_REL_TS
, true },
314 { "dec", sizeof("dec" ) - 1, RTLOGFLAGS_DECIMAL_TS
, false },
315 { "hex", sizeof("hex" ) - 1, RTLOGFLAGS_DECIMAL_TS
, true },
316 { "writethru", sizeof("writethru" ) - 1, RTLOGFLAGS_WRITE_THROUGH
, false },
317 { "writethrough", sizeof("writethrough") - 1, RTLOGFLAGS_WRITE_THROUGH
, false },
318 { "flush", sizeof("flush" ) - 1, RTLOGFLAGS_FLUSH
, false },
319 { "lockcnts", sizeof("lockcnts" ) - 1, RTLOGFLAGS_PREFIX_LOCK_COUNTS
, false },
320 { "cpuid", sizeof("cpuid" ) - 1, RTLOGFLAGS_PREFIX_CPUID
, false },
321 { "pid", sizeof("pid" ) - 1, RTLOGFLAGS_PREFIX_PID
, false },
322 { "flagno", sizeof("flagno" ) - 1, RTLOGFLAGS_PREFIX_FLAG_NO
, false },
323 { "flag", sizeof("flag" ) - 1, RTLOGFLAGS_PREFIX_FLAG
, false },
324 { "groupno", sizeof("groupno" ) - 1, RTLOGFLAGS_PREFIX_GROUP_NO
, false },
325 { "group", sizeof("group" ) - 1, RTLOGFLAGS_PREFIX_GROUP
, false },
326 { "tid", sizeof("tid" ) - 1, RTLOGFLAGS_PREFIX_TID
, false },
327 { "thread", sizeof("thread" ) - 1, RTLOGFLAGS_PREFIX_THREAD
, false },
328 { "custom", sizeof("custom" ) - 1, RTLOGFLAGS_PREFIX_CUSTOM
, false },
329 { "timeprog", sizeof("timeprog" ) - 1, RTLOGFLAGS_PREFIX_TIME_PROG
, false },
330 { "time", sizeof("time" ) - 1, RTLOGFLAGS_PREFIX_TIME
, false },
331 { "msprog", sizeof("msprog" ) - 1, RTLOGFLAGS_PREFIX_MS_PROG
, false },
332 { "tsc", sizeof("tsc" ) - 1, RTLOGFLAGS_PREFIX_TSC
, false }, /* before ts! */
333 { "ts", sizeof("ts" ) - 1, RTLOGFLAGS_PREFIX_TS
, false },
334 /* We intentionally omit RTLOGFLAGS_RESTRICT_GROUPS. */
338 * Logger destination instructions.
342 const char *pszInstr
; /**< The name. */
343 size_t cchInstr
; /**< The size of the name. */
344 uint32_t fFlag
; /**< The corresponding destination flag. */
345 } const g_aLogDst
[] =
347 { RT_STR_TUPLE("file"), RTLOGDEST_FILE
}, /* Must be 1st! */
348 { RT_STR_TUPLE("dir"), RTLOGDEST_FILE
}, /* Must be 2nd! */
349 { RT_STR_TUPLE("history"), 0 }, /* Must be 3rd! */
350 { RT_STR_TUPLE("histsize"), 0 }, /* Must be 4th! */
351 { RT_STR_TUPLE("histtime"), 0 }, /* Must be 5th! */
352 { RT_STR_TUPLE("ringbuf"), RTLOGDEST_RINGBUF
}, /* Must be 6th! */
353 { RT_STR_TUPLE("stdout"), RTLOGDEST_STDOUT
},
354 { RT_STR_TUPLE("stderr"), RTLOGDEST_STDERR
},
355 { RT_STR_TUPLE("debugger"), RTLOGDEST_DEBUGGER
},
356 { RT_STR_TUPLE("com"), RTLOGDEST_COM
},
357 { RT_STR_TUPLE("nodeny"), RTLOGDEST_F_NO_DENY
},
358 { RT_STR_TUPLE("user"), RTLOGDEST_USER
},
362 /** Log rotation backoff table - millisecond sleep intervals.
363 * Important on Windows host, especially for VBoxSVC release logging. Only a
364 * medium term solution, until a proper fix for log file handling is available.
367 static const uint32_t g_acMsLogBackoff
[] =
368 { 10, 10, 10, 20, 50, 100, 200, 200, 200, 200, 500, 500, 500, 500, 1000, 1000, 1000, 1000, 1000, 1000, 1000 };
373 * Locks the logger instance.
375 * @returns See RTSemSpinMutexRequest().
376 * @param pLogger The logger instance.
378 DECLINLINE(int) rtlogLock(PRTLOGGER pLogger
)
381 PRTLOGGERINTERNAL pInt
= pLogger
->pInt
;
382 AssertMsgReturn(pInt
->uRevision
== RTLOGGERINTERNAL_REV
, ("%#x != %#x\n", pInt
->uRevision
, RTLOGGERINTERNAL_REV
),
383 VERR_LOG_REVISION_MISMATCH
);
384 AssertMsgReturn(pInt
->cbSelf
== sizeof(*pInt
), ("%#x != %#x\n", pInt
->cbSelf
, sizeof(*pInt
)),
385 VERR_LOG_REVISION_MISMATCH
);
386 if (pInt
->hSpinMtx
!= NIL_RTSEMSPINMUTEX
)
388 int rc
= RTSemSpinMutexRequest(pInt
->hSpinMtx
);
400 * Unlocks the logger instance.
401 * @param pLogger The logger instance.
403 DECLINLINE(void) rtlogUnlock(PRTLOGGER pLogger
)
406 if (pLogger
->pInt
->hSpinMtx
!= NIL_RTSEMSPINMUTEX
)
407 RTSemSpinMutexRelease(pLogger
->pInt
->hSpinMtx
);
417 # ifdef SOME_UNUSED_FUNCTION
419 * Logging to file, output callback.
421 * @param pvArg User argument.
422 * @param pachChars Pointer to an array of utf-8 characters.
423 * @param cbChars Number of bytes in the character array pointed to by pachChars.
425 static DECLCALLBACK(size_t) rtlogPhaseWrite(void *pvArg
, const char *pachChars
, size_t cbChars
)
427 PRTLOGGER pLogger
= (PRTLOGGER
)pvArg
;
428 RTFileWrite(pLogger
->pInt
->hFile
, pachChars
, cbChars
, NULL
);
434 * Callback to format VBox formatting extentions.
435 * See @ref pg_rt_str_format for a reference on the format types.
437 * @returns The number of bytes formatted.
438 * @param pvArg Formatter argument.
439 * @param pfnOutput Pointer to output function.
440 * @param pvArgOutput Argument for the output function.
441 * @param ppszFormat Pointer to the format string pointer. Advance this till the char
442 * after the format specifier.
443 * @param pArgs Pointer to the argument list. Use this to fetch the arguments.
444 * @param cchWidth Format Width. -1 if not specified.
445 * @param cchPrecision Format Precision. -1 if not specified.
446 * @param fFlags Flags (RTSTR_NTFS_*).
447 * @param chArgSize The argument size specifier, 'l' or 'L'.
449 static DECLCALLBACK(size_t) rtlogPhaseFormatStr(void *pvArg
, PFNRTSTROUTPUT pfnOutput
, void *pvArgOutput
,
450 const char **ppszFormat
, va_list *pArgs
, int cchWidth
,
451 int cchPrecision
, unsigned fFlags
, char chArgSize
)
453 char ch
= *(*ppszFormat
)++;
455 AssertMsgFailed(("Invalid logger phase format type '%%%c%.10s'!\n", ch
, *ppszFormat
)); NOREF(ch
);
460 # endif /* SOME_UNUSED_FUNCTION */
464 * Log phase callback function, assumes the lock is already held
466 * @param pLogger The logger instance.
467 * @param pszFormat Format string.
468 * @param ... Optional arguments as specified in the format string.
470 static DECLCALLBACK(void) rtlogPhaseMsgLocked(PRTLOGGER pLogger
, const char *pszFormat
, ...)
473 AssertPtrReturnVoid(pLogger
);
474 AssertPtrReturnVoid(pLogger
->pInt
);
475 Assert(pLogger
->pInt
->hSpinMtx
!= NIL_RTSEMSPINMUTEX
);
477 va_start(args
, pszFormat
);
478 rtlogLoggerExVLocked(pLogger
, 0, ~0U, pszFormat
, args
);
484 * Log phase callback function, assumes the lock is not held.
486 * @param pLogger The logger instance.
487 * @param pszFormat Format string.
488 * @param ... Optional arguments as specified in the format string.
490 static DECLCALLBACK(void) rtlogPhaseMsgNormal(PRTLOGGER pLogger
, const char *pszFormat
, ...)
493 AssertPtrReturnVoid(pLogger
);
494 AssertPtrReturnVoid(pLogger
->pInt
);
495 Assert(pLogger
->pInt
->hSpinMtx
!= NIL_RTSEMSPINMUTEX
);
497 va_start(args
, pszFormat
);
498 RTLogLoggerExV(pLogger
, 0, ~0U, pszFormat
, args
);
502 # endif /* IN_RING3 */
505 * Adjusts the ring buffer.
507 * @returns IPRT status code.
508 * @param pLogger The logger instance.
509 * @param cbNewSize The new ring buffer size (0 == default).
510 * @param fForce Whether to do this even if the logger instance hasn't
511 * really been fully created yet (i.e. during RTLogCreate).
513 static int rtLogRingBufAdjust(PRTLOGGER pLogger
, uint32_t cbNewSize
, bool fForce
)
516 * If this is early logger init, don't do anything.
518 if (!pLogger
->pInt
->fCreated
&& !fForce
)
522 * Lock the logger and make the necessary changes.
524 int rc
= rtlogLock(pLogger
);
528 cbNewSize
= RTLOG_RINGBUF_DEFAULT_SIZE
;
529 if ( pLogger
->pInt
->cbRingBuf
!= cbNewSize
530 || !pLogger
->pInt
->pchRingBufCur
)
532 uintptr_t offOld
= pLogger
->pInt
->pchRingBufCur
- pLogger
->pInt
->pszRingBuf
;
533 if (offOld
< sizeof(RTLOG_RINGBUF_EYE_CATCHER
))
534 offOld
= sizeof(RTLOG_RINGBUF_EYE_CATCHER
);
535 else if (offOld
>= cbNewSize
)
537 memmove(pLogger
->pInt
->pszRingBuf
, &pLogger
->pInt
->pszRingBuf
[offOld
- cbNewSize
], cbNewSize
);
538 offOld
= sizeof(RTLOG_RINGBUF_EYE_CATCHER
);
541 void *pvNew
= RTMemRealloc(pLogger
->pInt
->pchRingBufCur
, cbNewSize
);
544 pLogger
->pInt
->pszRingBuf
= (char *)pvNew
;
545 pLogger
->pInt
->pchRingBufCur
= (char *)pvNew
+ offOld
;
546 pLogger
->pInt
->cbRingBuf
= cbNewSize
;
547 memcpy(pvNew
, RTLOG_RINGBUF_EYE_CATCHER
, sizeof(RTLOG_RINGBUF_EYE_CATCHER
));
548 memcpy((char *)pvNew
+ cbNewSize
- sizeof(RTLOG_RINGBUF_EYE_CATCHER_END
),
549 RTLOG_RINGBUF_EYE_CATCHER_END
, sizeof(RTLOG_RINGBUF_EYE_CATCHER_END
));
555 rtlogUnlock(pLogger
);
563 * Writes text to the ring buffer.
565 * @param pInt The internal logger data structure.
566 * @param pachText The text to write.
567 * @param cchText The number of chars (bytes) to write.
569 static void rtLogRingBufWrite(PRTLOGGERINTERNAL pInt
, const char *pachText
, size_t cchText
)
572 * Get the ring buffer data, adjusting it to only describe the writable
573 * part of the buffer.
575 char * const pchStart
= &pInt
->pszRingBuf
[sizeof(RTLOG_RINGBUF_EYE_CATCHER
)];
576 size_t const cchBuf
= pInt
->cbRingBuf
- sizeof(RTLOG_RINGBUF_EYE_CATCHER
) - sizeof(RTLOG_RINGBUF_EYE_CATCHER_END
);
577 char *pchCur
= pInt
->pchRingBufCur
;
578 size_t cchLeft
= pchCur
- pchStart
;
579 if (RT_LIKELY(cchLeft
< cchBuf
))
580 cchLeft
= cchBuf
- cchLeft
;
583 /* May happen in ring-0 where a thread or two went ahead without getting the lock. */
587 Assert(cchBuf
< pInt
->cbRingBuf
);
589 if (cchText
< cchLeft
)
592 * The text fits in the remaining space.
594 memcpy(pchCur
, pachText
, cchText
);
595 pchCur
[cchText
] = '\0';
596 pInt
->pchRingBufCur
= &pchCur
[cchText
];
597 pInt
->cbRingBufUnflushed
+= cchText
;
602 * The text wraps around. Taking the simple but inefficient approach
603 * to input texts that are longer than the ring buffer since that
604 * is unlikely to the be a frequent case.
606 /* Fill to the end of the buffer. */
607 memcpy(pchCur
, pachText
, cchLeft
);
610 pInt
->cbRingBufUnflushed
+= cchLeft
;
611 pInt
->pchRingBufCur
= pchStart
;
613 /* Ring buffer overflows (the plainly inefficient bit). */
614 while (cchText
>= cchBuf
)
616 memcpy(pchStart
, pachText
, cchBuf
);
619 pInt
->cbRingBufUnflushed
+= cchBuf
;
622 /* The final bit, if any. */
625 memcpy(pchStart
, pachText
, cchText
);
626 pInt
->cbRingBufUnflushed
+= cchText
;
628 pchStart
[cchText
] = '\0';
629 pInt
->pchRingBufCur
= &pchStart
[cchText
];
635 * Flushes the ring buffer to all the other log destinations.
637 * @param pLogger The logger instance which ring buffer should be flushed.
639 static void rtLogRingBufFlush(PRTLOGGER pLogger
)
641 const char *pszPreamble
;
643 const char *pszFirst
;
645 const char *pszSecond
;
649 * Get the ring buffer data, adjusting it to only describe the writable
650 * part of the buffer.
652 uint64_t cchUnflushed
= pLogger
->pInt
->cbRingBufUnflushed
;
653 char * const pszBuf
= &pLogger
->pInt
->pszRingBuf
[sizeof(RTLOG_RINGBUF_EYE_CATCHER
)];
654 size_t const cchBuf
= pLogger
->pInt
->cbRingBuf
- sizeof(RTLOG_RINGBUF_EYE_CATCHER
) - sizeof(RTLOG_RINGBUF_EYE_CATCHER_END
);
655 size_t offCur
= pLogger
->pInt
->pchRingBufCur
- pszBuf
;
657 if (RT_LIKELY(offCur
< cchBuf
))
658 cchAfter
= cchBuf
- offCur
;
659 else /* May happen in ring-0 where a thread or two went ahead without getting the lock. */
665 pLogger
->pInt
->cbRingBufUnflushed
= 0;
668 * Figure out whether there are one or two segments that needs writing,
669 * making the last segment is terminated. (The first is always
670 * terminated because of the eye-catcher at the end of the buffer.)
672 if (cchUnflushed
== 0)
674 pszBuf
[offCur
] = '\0';
675 if (cchUnflushed
>= cchBuf
)
677 pszFirst
= &pszBuf
[offCur
+ 1];
678 cchFirst
= cchAfter
? cchAfter
- 1 : 0;
681 pszPreamble
= "\n*FLUSH RING BUF*\n";
682 cchPreamble
= sizeof("\n*FLUSH RING BUF*\n") - 1;
684 else if ((size_t)cchUnflushed
<= offCur
)
686 cchFirst
= (size_t)cchUnflushed
;
687 pszFirst
= &pszBuf
[offCur
- cchFirst
];
695 cchFirst
= (size_t)cchUnflushed
- offCur
;
696 pszFirst
= &pszBuf
[cchBuf
- cchFirst
];
704 * Write the ring buffer to all other destiations.
706 if (pLogger
->fDestFlags
& RTLOGDEST_USER
)
709 RTLogWriteUser(pszPreamble
, cchPreamble
);
711 RTLogWriteUser(pszFirst
, cchFirst
);
713 RTLogWriteUser(pszSecond
, cchSecond
);
716 if (pLogger
->fDestFlags
& RTLOGDEST_DEBUGGER
)
719 RTLogWriteDebugger(pszPreamble
, cchPreamble
);
721 RTLogWriteDebugger(pszFirst
, cchFirst
);
723 RTLogWriteDebugger(pszSecond
, cchSecond
);
727 if (pLogger
->fDestFlags
& RTLOGDEST_FILE
)
729 if (pLogger
->pInt
->hFile
!= NIL_RTFILE
)
732 RTFileWrite(pLogger
->pInt
->hFile
, pszPreamble
, cchPreamble
, NULL
);
734 RTFileWrite(pLogger
->pInt
->hFile
, pszFirst
, cchFirst
, NULL
);
736 RTFileWrite(pLogger
->pInt
->hFile
, pszSecond
, cchSecond
, NULL
);
737 if (pLogger
->fFlags
& RTLOGFLAGS_FLUSH
)
738 RTFileFlush(pLogger
->pInt
->hFile
);
740 if (pLogger
->pInt
->cHistory
)
741 pLogger
->pInt
->cbHistoryFileWritten
+= cchFirst
+ cchSecond
;
745 if (pLogger
->fDestFlags
& RTLOGDEST_STDOUT
)
748 RTLogWriteStdOut(pszPreamble
, cchPreamble
);
750 RTLogWriteStdOut(pszFirst
, cchFirst
);
752 RTLogWriteStdOut(pszSecond
, cchSecond
);
755 if (pLogger
->fDestFlags
& RTLOGDEST_STDERR
)
758 RTLogWriteStdErr(pszPreamble
, cchPreamble
);
760 RTLogWriteStdErr(pszFirst
, cchFirst
);
762 RTLogWriteStdErr(pszSecond
, cchSecond
);
765 # if defined(IN_RING0) && !defined(LOG_NO_COM)
766 if (pLogger
->fDestFlags
& RTLOGDEST_COM
)
769 RTLogWriteCom(pszPreamble
, cchPreamble
);
771 RTLogWriteCom(pszFirst
, cchFirst
);
773 RTLogWriteCom(pszSecond
, cchSecond
);
779 RTDECL(int) RTLogCreateExV(PRTLOGGER
*ppLogger
, uint32_t fFlags
, const char *pszGroupSettings
,
780 const char *pszEnvVarBase
, unsigned cGroups
, const char * const *papszGroups
,
781 uint32_t fDestFlags
, PFNRTLOGPHASE pfnPhase
, uint32_t cHistory
,
782 uint64_t cbHistoryFileMax
, uint32_t cSecsHistoryTimeSlot
,
783 PRTERRINFO pErrInfo
, const char *pszFilenameFmt
, va_list args
)
793 if ( (cGroups
&& !papszGroups
)
794 || !VALID_PTR(ppLogger
) )
796 AssertMsgFailed(("Invalid parameters!\n"));
797 return VERR_INVALID_PARAMETER
;
801 AssertMsgReturn(cHistory
< _1M
, ("%#x", cHistory
), VERR_OUT_OF_RANGE
);
804 * Allocate a logger instance.
806 offInternal
= RT_OFFSETOF(RTLOGGER
, afGroups
[cGroups
]);
807 offInternal
= RT_ALIGN_Z(offInternal
, sizeof(uint64_t));
808 cbLogger
= offInternal
+ sizeof(RTLOGGERINTERNAL
);
809 if (fFlags
& RTLOGFLAGS_RESTRICT_GROUPS
)
810 cbLogger
+= cGroups
* sizeof(uint32_t);
811 pLogger
= (PRTLOGGER
)RTMemAllocZVar(cbLogger
);
814 # if defined(RT_ARCH_X86) && (!defined(LOG_USE_C99) || !defined(RT_WITHOUT_EXEC_ALLOC))
817 pLogger
->u32Magic
= RTLOGGER_MAGIC
;
818 pLogger
->cGroups
= cGroups
;
819 pLogger
->fFlags
= fFlags
;
820 pLogger
->fDestFlags
= fDestFlags
;
821 pLogger
->pInt
= (PRTLOGGERINTERNAL
)((uintptr_t)pLogger
+ offInternal
);
822 pLogger
->pInt
->uRevision
= RTLOGGERINTERNAL_REV
;
823 pLogger
->pInt
->cbSelf
= sizeof(RTLOGGERINTERNAL
);
824 pLogger
->pInt
->hSpinMtx
= NIL_RTSEMSPINMUTEX
;
825 pLogger
->pInt
->pfnFlush
= NULL
;
826 pLogger
->pInt
->pfnPrefix
= NULL
;
827 pLogger
->pInt
->pvPrefixUserArg
= NULL
;
828 pLogger
->pInt
->afPadding1
[0] = false;
829 pLogger
->pInt
->afPadding1
[1] = false;
830 pLogger
->pInt
->fCreated
= false;
831 pLogger
->pInt
->cMaxGroups
= cGroups
;
832 pLogger
->pInt
->papszGroups
= papszGroups
;
833 if (fFlags
& RTLOGFLAGS_RESTRICT_GROUPS
)
834 pLogger
->pInt
->pacEntriesPerGroup
= (uint32_t *)(pLogger
->pInt
+ 1);
836 pLogger
->pInt
->pacEntriesPerGroup
= NULL
;
837 pLogger
->pInt
->cMaxEntriesPerGroup
= UINT32_MAX
;
839 pLogger
->pInt
->pfnPhase
= pfnPhase
;
840 pLogger
->pInt
->hFile
= NIL_RTFILE
;
841 pLogger
->pInt
->cHistory
= cHistory
;
842 if (cbHistoryFileMax
== 0)
843 pLogger
->pInt
->cbHistoryFileMax
= UINT64_MAX
;
845 pLogger
->pInt
->cbHistoryFileMax
= cbHistoryFileMax
;
846 if (cSecsHistoryTimeSlot
== 0)
847 pLogger
->pInt
->cSecsHistoryTimeSlot
= UINT32_MAX
;
849 pLogger
->pInt
->cSecsHistoryTimeSlot
= cSecsHistoryTimeSlot
;
850 # else /* !IN_RING3 */
851 RT_NOREF_PV(pfnPhase
); RT_NOREF_PV(cHistory
); RT_NOREF_PV(cbHistoryFileMax
); RT_NOREF_PV(cSecsHistoryTimeSlot
);
852 # endif /* !IN_RING3 */
853 if (pszGroupSettings
)
854 RTLogGroupSettings(pLogger
, pszGroupSettings
);
856 # if defined(RT_ARCH_X86) && (!defined(LOG_USE_C99) || !defined(RT_WITHOUT_EXEC_ALLOC))
860 pu8Code
= (uint8_t *)RTMemExecAlloc(64);
863 pLogger
->pfnLogger
= *(PFNRTLOGGER
*)&pu8Code
;
864 *pu8Code
++ = 0x68; /* push imm32 */
865 *(void **)pu8Code
= pLogger
;
866 pu8Code
+= sizeof(void *);
867 *pu8Code
++ = 0xe8; /* call rel32 */
868 *(uint32_t *)pu8Code
= (uintptr_t)RTLogLogger
- ((uintptr_t)pu8Code
+ sizeof(uint32_t));
869 pu8Code
+= sizeof(uint32_t);
870 *pu8Code
++ = 0x8d; /* lea esp, [esp + 4] */
874 *pu8Code
++ = 0xc3; /* ret near */
875 AssertMsg((uintptr_t)pu8Code
- (uintptr_t)pLogger
->pfnLogger
<= 64,
876 ("Wrapper assembly is too big! %d bytes\n", (uintptr_t)pu8Code
- (uintptr_t)pLogger
->pfnLogger
));
883 /* Most probably SELinux causing trouble since the larger RTMemAlloc succeeded. */
884 RTErrInfoSet(pErrInfo
, rc
, N_("mmap(PROT_WRITE | PROT_EXEC) failed -- SELinux?"));
888 # endif /* X86 wrapper code*/
890 # ifdef IN_RING3 /* files and env.vars. are only accessible when in R3 at the present time. */
892 * Format the filename.
896 /** @todo validate the length, fail on overflow. */
897 RTStrPrintfV(pLogger
->pInt
->szFilename
, sizeof(pLogger
->pInt
->szFilename
), pszFilenameFmt
, args
);
898 if (pLogger
->pInt
->szFilename
[0])
899 pLogger
->fDestFlags
|= RTLOGDEST_FILE
;
903 * Parse the environment variables.
907 /* make temp copy of environment variable base. */
908 size_t cchEnvVarBase
= strlen(pszEnvVarBase
);
909 char *pszEnvVar
= (char *)alloca(cchEnvVarBase
+ 16);
910 memcpy(pszEnvVar
, pszEnvVarBase
, cchEnvVarBase
);
915 strcpy(pszEnvVar
+ cchEnvVarBase
, "_DEST");
916 const char *pszValue
= RTEnvGet(pszEnvVar
);
918 RTLogDestinations(pLogger
, pszValue
);
923 strcpy(pszEnvVar
+ cchEnvVarBase
, "_FLAGS");
924 pszValue
= RTEnvGet(pszEnvVar
);
926 RTLogFlags(pLogger
, pszValue
);
929 * The group settings.
931 pszEnvVar
[cchEnvVarBase
] = '\0';
932 pszValue
= RTEnvGet(pszEnvVar
);
934 RTLogGroupSettings(pLogger
, pszValue
);
936 # else /* !IN_RING3 */
937 RT_NOREF_PV(pszEnvVarBase
); RT_NOREF_PV(pszFilenameFmt
); RT_NOREF_PV(args
);
938 # endif /* !IN_RING3 */
941 * Open the destination(s).
944 if ((pLogger
->fDestFlags
& (RTLOGDEST_F_DELAY_FILE
| RTLOGDEST_FILE
)) == RTLOGDEST_F_DELAY_FILE
)
945 pLogger
->fDestFlags
&= ~RTLOGDEST_F_DELAY_FILE
;
947 if ((pLogger
->fDestFlags
& (RTLOGDEST_FILE
| RTLOGDEST_F_DELAY_FILE
)) == RTLOGDEST_FILE
)
948 rc
= rtR3LogOpenFileDestination(pLogger
, pErrInfo
);
951 if ((pLogger
->fDestFlags
& RTLOGDEST_RINGBUF
) && RT_SUCCESS(rc
))
952 rc
= rtLogRingBufAdjust(pLogger
, pLogger
->pInt
->cbRingBuf
, true /*fForce*/);
955 * Create mutex and check how much it counts when entering the lock
956 * so that we can report the values for RTLOGFLAGS_PREFIX_LOCK_COUNTS.
960 rc
= RTSemSpinMutexCreate(&pLogger
->pInt
->hSpinMtx
, RTSEMSPINMUTEX_FLAGS_IRQ_SAFE
);
963 # ifdef IN_RING3 /** @todo do counters in ring-0 too? */
964 RTTHREAD Thread
= RTThreadSelf();
965 if (Thread
!= NIL_RTTHREAD
)
967 int32_t c
= RTLockValidatorWriteLockGetCount(Thread
);
968 RTSemSpinMutexRequest(pLogger
->pInt
->hSpinMtx
);
969 c
= RTLockValidatorWriteLockGetCount(Thread
) - c
;
970 RTSemSpinMutexRelease(pLogger
->pInt
->hSpinMtx
);
971 ASMAtomicWriteU32(&g_cLoggerLockCount
, c
);
974 /* Use the callback to generate some initial log contents. */
975 Assert(VALID_PTR(pLogger
->pInt
->pfnPhase
) || pLogger
->pInt
->pfnPhase
== NULL
);
976 if (pLogger
->pInt
->pfnPhase
)
977 pLogger
->pInt
->pfnPhase(pLogger
, RTLOGPHASE_BEGIN
, rtlogPhaseMsgNormal
);
979 pLogger
->pInt
->fCreated
= true;
984 RTErrInfoSet(pErrInfo
, rc
, N_("failed to create semaphore"));
987 RTFileClose(pLogger
->pInt
->hFile
);
989 # if defined(LOG_USE_C99) && defined(RT_WITHOUT_EXEC_ALLOC)
990 RTMemFree(*(void **)&pLogger
->pfnLogger
);
992 RTMemExecFree(*(void **)&pLogger
->pfnLogger
, 64);
1002 RT_EXPORT_SYMBOL(RTLogCreateExV
);
1005 RTDECL(int) RTLogCreate(PRTLOGGER
*ppLogger
, uint32_t fFlags
, const char *pszGroupSettings
,
1006 const char *pszEnvVarBase
, unsigned cGroups
, const char * const * papszGroups
,
1007 uint32_t fDestFlags
, const char *pszFilenameFmt
, ...)
1012 va_start(args
, pszFilenameFmt
);
1013 rc
= RTLogCreateExV(ppLogger
, fFlags
, pszGroupSettings
, pszEnvVarBase
, cGroups
, papszGroups
,
1014 fDestFlags
, NULL
/*pfnPhase*/, 0 /*cHistory*/, 0 /*cbHistoryFileMax*/, 0 /*cSecsHistoryTimeSlot*/,
1015 NULL
/*pErrInfo*/, pszFilenameFmt
, args
);
1019 RT_EXPORT_SYMBOL(RTLogCreate
);
1022 RTDECL(int) RTLogCreateEx(PRTLOGGER
*ppLogger
, uint32_t fFlags
, const char *pszGroupSettings
,
1023 const char *pszEnvVarBase
, unsigned cGroups
, const char * const * papszGroups
,
1024 uint32_t fDestFlags
, PFNRTLOGPHASE pfnPhase
, uint32_t cHistory
,
1025 uint64_t cbHistoryFileMax
, uint32_t cSecsHistoryTimeSlot
,
1026 PRTERRINFO pErrInfo
, const char *pszFilenameFmt
, ...)
1031 va_start(args
, pszFilenameFmt
);
1032 rc
= RTLogCreateExV(ppLogger
, fFlags
, pszGroupSettings
, pszEnvVarBase
, cGroups
, papszGroups
,
1033 fDestFlags
, pfnPhase
, cHistory
, cbHistoryFileMax
, cSecsHistoryTimeSlot
,
1034 pErrInfo
, pszFilenameFmt
, args
);
1038 RT_EXPORT_SYMBOL(RTLogCreateEx
);
1042 * Destroys a logger instance.
1044 * The instance is flushed and all output destinations closed (where applicable).
1046 * @returns iprt status code.
1047 * @param pLogger The logger instance which close destroyed. NULL is fine.
1049 RTDECL(int) RTLogDestroy(PRTLOGGER pLogger
)
1053 RTSEMSPINMUTEX hSpinMtx
;
1059 return VINF_SUCCESS
;
1060 AssertPtrReturn(pLogger
, VERR_INVALID_POINTER
);
1061 AssertReturn(pLogger
->u32Magic
== RTLOGGER_MAGIC
, VERR_INVALID_MAGIC
);
1062 AssertPtrReturn(pLogger
->pInt
, VERR_INVALID_POINTER
);
1065 * Acquire logger instance sem and disable all logging. (paranoia)
1067 rc
= rtlogLock(pLogger
);
1068 AssertMsgRCReturn(rc
, ("%Rrc\n", rc
), rc
);
1070 pLogger
->fFlags
|= RTLOGFLAGS_DISABLED
;
1071 iGroup
= pLogger
->cGroups
;
1072 while (iGroup
-- > 0)
1073 pLogger
->afGroups
[iGroup
] = 0;
1078 rtlogFlush(pLogger
, false /*fNeedSpace*/);
1082 * Add end of logging message.
1084 if ( (pLogger
->fDestFlags
& RTLOGDEST_FILE
)
1085 && pLogger
->pInt
->hFile
!= NIL_RTFILE
)
1086 pLogger
->pInt
->pfnPhase(pLogger
, RTLOGPHASE_END
, rtlogPhaseMsgLocked
);
1089 * Close output stuffs.
1091 if (pLogger
->pInt
->hFile
!= NIL_RTFILE
)
1093 int rc2
= RTFileClose(pLogger
->pInt
->hFile
);
1095 if (RT_FAILURE(rc2
) && RT_SUCCESS(rc
))
1097 pLogger
->pInt
->hFile
= NIL_RTFILE
;
1102 * Free the mutex, the wrapper and the instance memory.
1104 hSpinMtx
= pLogger
->pInt
->hSpinMtx
;
1105 pLogger
->pInt
->hSpinMtx
= NIL_RTSEMSPINMUTEX
;
1106 if (hSpinMtx
!= NIL_RTSEMSPINMUTEX
)
1109 RTSemSpinMutexRelease(hSpinMtx
);
1110 rc2
= RTSemSpinMutexDestroy(hSpinMtx
);
1112 if (RT_FAILURE(rc2
) && RT_SUCCESS(rc
))
1116 if (pLogger
->pfnLogger
)
1118 # if defined(LOG_USE_C99) && defined(RT_WITHOUT_EXEC_ALLOC)
1119 RTMemFree(*(void **)&pLogger
->pfnLogger
);
1121 RTMemExecFree(*(void **)&pLogger
->pfnLogger
, 64);
1123 pLogger
->pfnLogger
= NULL
;
1129 RT_EXPORT_SYMBOL(RTLogDestroy
);
1133 * Create a logger instance clone for RC usage.
1135 * @returns iprt status code.
1137 * @param pLogger The logger instance to be cloned.
1138 * @param pLoggerRC Where to create the RC logger instance.
1139 * @param cbLoggerRC Amount of memory allocated to for the RC logger
1141 * @param pfnLoggerRCPtr Pointer to logger wrapper function for this
1142 * instance (RC Ptr).
1143 * @param pfnFlushRCPtr Pointer to flush function (RC Ptr).
1144 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
1146 RTDECL(int) RTLogCloneRC(PRTLOGGER pLogger
, PRTLOGGERRC pLoggerRC
, size_t cbLoggerRC
,
1147 RTRCPTR pfnLoggerRCPtr
, RTRCPTR pfnFlushRCPtr
, uint32_t fFlags
)
1156 AssertMsgFailed(("Invalid parameters!\n"));
1157 return VERR_INVALID_PARAMETER
;
1159 if (cbLoggerRC
< sizeof(*pLoggerRC
))
1161 AssertMsgFailed(("%d min=%d\n", cbLoggerRC
, sizeof(*pLoggerRC
)));
1162 return VERR_INVALID_PARAMETER
;
1166 * Initialize GC instance.
1168 pLoggerRC
->offScratch
= 0;
1169 pLoggerRC
->fPendingPrefix
= false;
1170 pLoggerRC
->pfnLogger
= pfnLoggerRCPtr
;
1171 pLoggerRC
->pfnFlush
= pfnFlushRCPtr
;
1172 pLoggerRC
->u32Magic
= RTLOGGERRC_MAGIC
;
1173 pLoggerRC
->fFlags
= fFlags
| RTLOGFLAGS_DISABLED
;
1174 pLoggerRC
->cGroups
= 1;
1175 pLoggerRC
->afGroups
[0] = 0;
1182 pLogger
= RTLogDefaultInstance();
1184 return VINF_SUCCESS
;
1188 * Check if there's enough space for the groups.
1190 if (cbLoggerRC
< (size_t)RT_OFFSETOF(RTLOGGERRC
, afGroups
[pLogger
->cGroups
]))
1192 AssertMsgFailed(("%d req=%d cGroups=%d\n", cbLoggerRC
, RT_OFFSETOF(RTLOGGERRC
, afGroups
[pLogger
->cGroups
]), pLogger
->cGroups
));
1193 return VERR_BUFFER_OVERFLOW
;
1195 memcpy(&pLoggerRC
->afGroups
[0], &pLogger
->afGroups
[0], pLogger
->cGroups
* sizeof(pLoggerRC
->afGroups
[0]));
1196 pLoggerRC
->cGroups
= pLogger
->cGroups
;
1199 * Copy bits from the HC instance.
1201 pLoggerRC
->fPendingPrefix
= pLogger
->pInt
->fPendingPrefix
;
1202 pLoggerRC
->fFlags
|= pLogger
->fFlags
;
1205 * Check if we can remove the disabled flag.
1207 if ( pLogger
->fDestFlags
1208 && !((pLogger
->fFlags
| fFlags
) & RTLOGFLAGS_DISABLED
))
1209 pLoggerRC
->fFlags
&= ~RTLOGFLAGS_DISABLED
;
1211 return VINF_SUCCESS
;
1213 RT_EXPORT_SYMBOL(RTLogCloneRC
);
1217 * Flushes a RC logger instance to a R3 logger.
1220 * @returns iprt status code.
1221 * @param pLogger The R3 logger instance to flush pLoggerRC to. If NULL
1222 * the default logger is used.
1223 * @param pLoggerRC The RC logger instance to flush.
1225 RTDECL(void) RTLogFlushRC(PRTLOGGER pLogger
, PRTLOGGERRC pLoggerRC
)
1232 pLogger
= RTLogDefaultInstance();
1235 pLoggerRC
->offScratch
= 0;
1241 * Any thing to flush?
1243 if ( pLogger
->offScratch
1244 || pLoggerRC
->offScratch
)
1247 * Acquire logger instance sem.
1249 int rc
= rtlogLock(pLogger
);
1254 * Write whatever the GC instance contains to the HC one, and then
1255 * flush the HC instance.
1257 if (pLoggerRC
->offScratch
)
1259 rtLogOutput(pLogger
, pLoggerRC
->achScratch
, pLoggerRC
->offScratch
);
1260 rtLogOutput(pLogger
, NULL
, 0);
1261 pLoggerRC
->offScratch
= 0;
1265 * Release the semaphore.
1267 rtlogUnlock(pLogger
);
1270 RT_EXPORT_SYMBOL(RTLogFlushRC
);
1274 RTDECL(int) RTLogCreateForR0(PRTLOGGER pLogger
, size_t cbLogger
,
1275 RTR0PTR pLoggerR0Ptr
, RTR0PTR pfnLoggerR0Ptr
, RTR0PTR pfnFlushR0Ptr
,
1276 uint32_t fFlags
, uint32_t fDestFlags
)
1281 AssertPtrReturn(pLogger
, VERR_INVALID_PARAMETER
);
1282 size_t const cbRequired
= sizeof(*pLogger
) + RTLOGGERINTERNAL_R0_SIZE
;
1283 AssertReturn(cbLogger
>= cbRequired
, VERR_BUFFER_OVERFLOW
);
1284 AssertReturn(pLoggerR0Ptr
!= NIL_RTR0PTR
, VERR_INVALID_PARAMETER
);
1285 AssertReturn(pfnLoggerR0Ptr
!= NIL_RTR0PTR
, VERR_INVALID_PARAMETER
);
1288 * Initialize the ring-0 instance.
1290 pLogger
->achScratch
[0] = 0;
1291 pLogger
->offScratch
= 0;
1292 pLogger
->pfnLogger
= (PFNRTLOGGER
)pfnLoggerR0Ptr
;
1293 pLogger
->fFlags
= fFlags
;
1294 pLogger
->fDestFlags
= fDestFlags
& ~RTLOGDEST_FILE
;
1295 pLogger
->pInt
= NULL
;
1296 pLogger
->cGroups
= 1;
1297 pLogger
->afGroups
[0] = 0;
1299 uint32_t cMaxGroups
= (uint32_t)((cbLogger
- cbRequired
) / sizeof(pLogger
->afGroups
[0]));
1300 if (fFlags
& RTLOGFLAGS_RESTRICT_GROUPS
)
1302 PRTLOGGERINTERNAL pInt
;
1305 AssertReturn(cMaxGroups
> 0, VERR_BUFFER_OVERFLOW
);
1306 pInt
= (PRTLOGGERINTERNAL
)&pLogger
->afGroups
[cMaxGroups
];
1307 if (!((uintptr_t)pInt
& (sizeof(uint64_t) - 1)))
1311 pLogger
->pInt
= (PRTLOGGERINTERNAL
)(pLoggerR0Ptr
+ (uintptr_t)pInt
- (uintptr_t)pLogger
);
1312 pInt
->uRevision
= RTLOGGERINTERNAL_REV
;
1313 pInt
->cbSelf
= RTLOGGERINTERNAL_R0_SIZE
;
1314 pInt
->hSpinMtx
= NIL_RTSEMSPINMUTEX
; /* Not serialized. */
1315 pInt
->pfnFlush
= (PFNRTLOGFLUSH
)pfnFlushR0Ptr
;
1316 pInt
->pfnPrefix
= NULL
;
1317 pInt
->pvPrefixUserArg
= NULL
;
1318 pInt
->fPendingPrefix
= false;
1319 pInt
->cMaxGroups
= cMaxGroups
;
1320 pInt
->papszGroups
= NULL
;
1321 pInt
->cMaxEntriesPerGroup
= UINT32_MAX
;
1322 if (fFlags
& RTLOGFLAGS_RESTRICT_GROUPS
)
1324 memset(pInt
+ 1, 0, sizeof(uint32_t) * cMaxGroups
);
1325 pInt
->pacEntriesPerGroup
= (uint32_t *)(pLogger
->pInt
+ 1);
1328 pInt
->pacEntriesPerGroup
= NULL
;
1330 pInt
->fCreated
= true;
1331 pLogger
->u32Magic
= RTLOGGER_MAGIC
;
1332 return VINF_SUCCESS
;
1334 RT_EXPORT_SYMBOL(RTLogCreateForR0
);
1337 RTDECL(size_t) RTLogCalcSizeForR0(uint32_t cGroups
, uint32_t fFlags
)
1339 size_t cb
= RT_OFFSETOF(RTLOGGER
, afGroups
[cGroups
]);
1340 cb
= RT_ALIGN_Z(cb
, sizeof(uint64_t));
1341 cb
+= sizeof(RTLOGGERINTERNAL
);
1342 if (fFlags
& RTLOGFLAGS_RESTRICT_GROUPS
)
1343 cb
+= sizeof(uint32_t) * cGroups
;
1346 RT_EXPORT_SYMBOL(RTLogCalcSizeForR0
);
1349 RTDECL(int) RTLogCopyGroupsAndFlagsForR0(PRTLOGGER pDstLogger
, RTR0PTR pDstLoggerR0Ptr
,
1350 PCRTLOGGER pSrcLogger
, uint32_t fFlagsOr
, uint32_t fFlagsAnd
)
1355 AssertPtrReturn(pDstLogger
, VERR_INVALID_PARAMETER
);
1356 AssertPtrNullReturn(pSrcLogger
, VERR_INVALID_PARAMETER
);
1363 pSrcLogger
= RTLogDefaultInstance();
1366 pDstLogger
->fFlags
|= RTLOGFLAGS_DISABLED
| fFlagsOr
;
1367 pDstLogger
->cGroups
= 1;
1368 pDstLogger
->afGroups
[0] = 0;
1369 return VINF_SUCCESS
;
1374 * Copy flags and group settings.
1376 pDstLogger
->fFlags
= (pSrcLogger
->fFlags
& fFlagsAnd
& ~RTLOGFLAGS_RESTRICT_GROUPS
) | fFlagsOr
;
1378 PRTLOGGERINTERNAL pDstInt
= (PRTLOGGERINTERNAL
)((uintptr_t)pDstLogger
->pInt
- pDstLoggerR0Ptr
+ (uintptr_t)pDstLogger
);
1379 int rc
= VINF_SUCCESS
;
1380 uint32_t cGroups
= pSrcLogger
->cGroups
;
1381 if (cGroups
> pDstInt
->cMaxGroups
)
1383 AssertMsgFailed(("cMaxGroups=%zd cGroups=%zd (min size %d)\n", pDstInt
->cMaxGroups
,
1384 pSrcLogger
->cGroups
, RT_OFFSETOF(RTLOGGER
, afGroups
[pSrcLogger
->cGroups
]) + RTLOGGERINTERNAL_R0_SIZE
));
1385 rc
= VERR_INVALID_PARAMETER
;
1386 cGroups
= pDstInt
->cMaxGroups
;
1388 memcpy(&pDstLogger
->afGroups
[0], &pSrcLogger
->afGroups
[0], cGroups
* sizeof(pDstLogger
->afGroups
[0]));
1389 pDstLogger
->cGroups
= cGroups
;
1393 RT_EXPORT_SYMBOL(RTLogCopyGroupsAndFlagsForR0
);
1396 RTDECL(int) RTLogSetCustomPrefixCallbackForR0(PRTLOGGER pLogger
, RTR0PTR pLoggerR0Ptr
,
1397 RTR0PTR pfnCallbackR0Ptr
, RTR0PTR pvUserR0Ptr
)
1399 AssertPtrReturn(pLogger
, VERR_INVALID_POINTER
);
1400 AssertReturn(pLogger
->u32Magic
== RTLOGGER_MAGIC
, VERR_INVALID_MAGIC
);
1405 PRTLOGGERINTERNAL pInt
= (PRTLOGGERINTERNAL
)((uintptr_t)pLogger
->pInt
- pLoggerR0Ptr
+ (uintptr_t)pLogger
);
1406 AssertReturn(pInt
->uRevision
== RTLOGGERINTERNAL_REV
, VERR_LOG_REVISION_MISMATCH
);
1407 pInt
->pvPrefixUserArg
= (void *)pvUserR0Ptr
;
1408 pInt
->pfnPrefix
= (PFNRTLOGPREFIX
)pfnCallbackR0Ptr
;
1410 return VINF_SUCCESS
;
1412 RT_EXPORT_SYMBOL(RTLogSetCustomPrefixCallbackForR0
);
1414 RTDECL(void) RTLogFlushR0(PRTLOGGER pLogger
, PRTLOGGER pLoggerR0
)
1421 pLogger
= RTLogDefaultInstance();
1424 /* flushing to "/dev/null". */
1425 if (pLoggerR0
->offScratch
)
1426 pLoggerR0
->offScratch
= 0;
1432 * Anything to flush?
1434 if ( pLoggerR0
->offScratch
1435 || pLogger
->offScratch
)
1438 * Acquire logger semaphores.
1440 int rc
= rtlogLock(pLogger
);
1446 * Write whatever the GC instance contains to the HC one, and then
1447 * flush the HC instance.
1449 if (pLoggerR0
->offScratch
)
1451 rtLogOutput(pLogger
, pLoggerR0
->achScratch
, pLoggerR0
->offScratch
);
1452 rtLogOutput(pLogger
, NULL
, 0);
1453 pLoggerR0
->offScratch
= 0;
1456 rtlogUnlock(pLogger
);
1459 RT_EXPORT_SYMBOL(RTLogFlushR0
);
1461 # endif /* IN_RING3 */
1465 * Flushes the buffer in one logger instance onto another logger.
1467 * @returns iprt status code.
1469 * @param pSrcLogger The logger instance to flush.
1470 * @param pDstLogger The logger instance to flush onto.
1471 * If NULL the default logger will be used.
1473 RTDECL(void) RTLogFlushToLogger(PRTLOGGER pSrcLogger
, PRTLOGGER pDstLogger
)
1480 pDstLogger
= RTLogDefaultInstance();
1483 /* flushing to "/dev/null". */
1484 if (pSrcLogger
->offScratch
)
1486 int rc
= rtlogLock(pSrcLogger
);
1489 pSrcLogger
->offScratch
= 0;
1490 rtlogUnlock(pSrcLogger
);
1498 * Any thing to flush?
1500 if ( pSrcLogger
->offScratch
1501 || pDstLogger
->offScratch
)
1504 * Acquire logger semaphores.
1506 int rc
= rtlogLock(pDstLogger
);
1509 rc
= rtlogLock(pSrcLogger
);
1513 * Write whatever the GC instance contains to the HC one, and then
1514 * flush the HC instance.
1516 if (pSrcLogger
->offScratch
)
1518 rtLogOutput(pDstLogger
, pSrcLogger
->achScratch
, pSrcLogger
->offScratch
);
1519 rtLogOutput(pDstLogger
, NULL
, 0);
1520 pSrcLogger
->offScratch
= 0;
1524 * Release the semaphores.
1526 rtlogUnlock(pSrcLogger
);
1528 rtlogUnlock(pDstLogger
);
1531 RT_EXPORT_SYMBOL(RTLogFlushToLogger
);
1535 * Sets the custom prefix callback.
1537 * @returns IPRT status code.
1538 * @param pLogger The logger instance.
1539 * @param pfnCallback The callback.
1540 * @param pvUser The user argument for the callback.
1542 RTDECL(int) RTLogSetCustomPrefixCallback(PRTLOGGER pLogger
, PFNRTLOGPREFIX pfnCallback
, void *pvUser
)
1549 pLogger
= RTLogDefaultInstance();
1551 return VINF_SUCCESS
;
1553 AssertReturn(pLogger
->u32Magic
== RTLOGGER_MAGIC
, VERR_INVALID_MAGIC
);
1559 pLogger
->pInt
->pvPrefixUserArg
= pvUser
;
1560 pLogger
->pInt
->pfnPrefix
= pfnCallback
;
1561 rtlogUnlock(pLogger
);
1563 return VINF_SUCCESS
;
1565 RT_EXPORT_SYMBOL(RTLogSetCustomPrefixCallback
);
1569 * Matches a group name with a pattern mask in an case insensitive manner (ASCII).
1571 * @returns true if matching and *ppachMask set to the end of the pattern.
1572 * @returns false if no match.
1573 * @param pszGrp The group name.
1574 * @param ppachMask Pointer to the pointer to the mask. Only wildcard supported is '*'.
1575 * @param cchMask The length of the mask, including modifiers. The modifiers is why
1576 * we update *ppachMask on match.
1578 static bool rtlogIsGroupMatching(const char *pszGrp
, const char **ppachMask
, size_t cchMask
)
1580 const char *pachMask
;
1582 if (!pszGrp
|| !*pszGrp
)
1584 pachMask
= *ppachMask
;
1587 if (RT_C_TO_LOWER(*pszGrp
) != RT_C_TO_LOWER(*pachMask
))
1592 * Check for wildcard and do a minimal match if found.
1594 if (*pachMask
!= '*')
1599 while (--cchMask
&& *pachMask
== '*');
1601 /* is there more to match? */
1604 || *pachMask
== '=')
1605 break; /* we're good */
1607 /* do extremely minimal matching (fixme) */
1608 pszTmp
= strchr(pszGrp
, RT_C_TO_LOWER(*pachMask
));
1610 pszTmp
= strchr(pszGrp
, RT_C_TO_UPPER(*pachMask
));
1620 /* trailing wildcard is ok. */
1625 } while (cchMask
&& *pachMask
== '*');
1628 || *pachMask
== '=')
1629 break; /* we're good */
1639 *ppachMask
= pachMask
;
1645 * Updates the group settings for the logger instance using the specified
1646 * specification string.
1648 * @returns iprt status code.
1649 * Failures can safely be ignored.
1650 * @param pLogger Logger instance.
1651 * @param pszValue Value to parse.
1653 RTDECL(int) RTLogGroupSettings(PRTLOGGER pLogger
, const char *pszValue
)
1660 pLogger
= RTLogDefaultInstance();
1662 return VINF_SUCCESS
;
1666 * Iterate the string.
1671 * Skip prefixes (blanks, ;, + and -).
1673 bool fEnabled
= true;
1675 const char *pszStart
;
1679 while ((ch
= *pszValue
) == '+' || ch
== '-' || ch
== ' ' || ch
== '\t' || ch
== '\n' || ch
== ';')
1681 if (ch
== '+' || ch
== '-' || ch
== ';')
1682 fEnabled
= ch
!= '-';
1691 pszStart
= pszValue
;
1692 while ((ch
= *pszValue
) != '\0' && ch
!= '+' && ch
!= '-' && ch
!= ' ' && ch
!= '\t')
1696 * Find the group (ascii case insensitive search).
1697 * Special group 'all'.
1699 cch
= pszValue
- pszStart
;
1701 && (pszStart
[0] == 'a' || pszStart
[0] == 'A')
1702 && (pszStart
[1] == 'l' || pszStart
[1] == 'L')
1703 && (pszStart
[2] == 'l' || pszStart
[2] == 'L')
1704 && (cch
== 3 || pszStart
[3] == '.' || pszStart
[3] == '='))
1709 unsigned fFlags
= cch
== 3
1710 ? RTLOGGRPFLAGS_ENABLED
| RTLOGGRPFLAGS_LEVEL_1
1711 : rtlogGroupFlags(&pszStart
[3]);
1712 for (i
= 0; i
< pLogger
->cGroups
; i
++)
1715 pLogger
->afGroups
[i
] |= fFlags
;
1717 pLogger
->afGroups
[i
] &= ~fFlags
;
1723 * Specific group(s).
1725 for (i
= 0; i
< pLogger
->cGroups
; i
++)
1727 const char *psz2
= (const char*)pszStart
;
1728 if (rtlogIsGroupMatching(pLogger
->pInt
->papszGroups
[i
], &psz2
, cch
))
1730 unsigned fFlags
= RTLOGGRPFLAGS_ENABLED
| RTLOGGRPFLAGS_LEVEL_1
;
1731 if (*psz2
== '.' || *psz2
== '=')
1732 fFlags
= rtlogGroupFlags(psz2
);
1734 pLogger
->afGroups
[i
] |= fFlags
;
1736 pLogger
->afGroups
[i
] &= ~fFlags
;
1738 } /* for each group */
1741 } /* parse specification */
1743 return VINF_SUCCESS
;
1745 RT_EXPORT_SYMBOL(RTLogGroupSettings
);
1749 * Interprets the group flags suffix.
1751 * @returns Flags specified. (0 is possible!)
1752 * @param psz Start of Suffix. (Either dot or equal sign.)
1754 static unsigned rtlogGroupFlags(const char *psz
)
1756 unsigned fFlags
= 0;
1765 const char *pszFlag
; /* lowercase!! */
1769 { "eo", RTLOGGRPFLAGS_ENABLED
},
1770 { "enabledonly",RTLOGGRPFLAGS_ENABLED
},
1771 { "e", RTLOGGRPFLAGS_ENABLED
| RTLOGGRPFLAGS_LEVEL_1
| RTLOGGRPFLAGS_WARN
},
1772 { "enabled", RTLOGGRPFLAGS_ENABLED
| RTLOGGRPFLAGS_LEVEL_1
| RTLOGGRPFLAGS_WARN
},
1773 { "l1", RTLOGGRPFLAGS_LEVEL_1
},
1774 { "level1", RTLOGGRPFLAGS_LEVEL_1
},
1775 { "l", RTLOGGRPFLAGS_LEVEL_2
},
1776 { "l2", RTLOGGRPFLAGS_LEVEL_2
},
1777 { "level2", RTLOGGRPFLAGS_LEVEL_2
},
1778 { "l3", RTLOGGRPFLAGS_LEVEL_3
},
1779 { "level3", RTLOGGRPFLAGS_LEVEL_3
},
1780 { "l4", RTLOGGRPFLAGS_LEVEL_4
},
1781 { "level4", RTLOGGRPFLAGS_LEVEL_4
},
1782 { "l5", RTLOGGRPFLAGS_LEVEL_5
},
1783 { "level5", RTLOGGRPFLAGS_LEVEL_5
},
1784 { "l6", RTLOGGRPFLAGS_LEVEL_6
},
1785 { "level6", RTLOGGRPFLAGS_LEVEL_6
},
1786 { "l7", RTLOGGRPFLAGS_LEVEL_7
},
1787 { "level7", RTLOGGRPFLAGS_LEVEL_7
},
1788 { "l8", RTLOGGRPFLAGS_LEVEL_8
},
1789 { "level8", RTLOGGRPFLAGS_LEVEL_8
},
1790 { "l9", RTLOGGRPFLAGS_LEVEL_9
},
1791 { "level9", RTLOGGRPFLAGS_LEVEL_9
},
1792 { "l10", RTLOGGRPFLAGS_LEVEL_10
},
1793 { "level10", RTLOGGRPFLAGS_LEVEL_10
},
1794 { "l11", RTLOGGRPFLAGS_LEVEL_11
},
1795 { "level11", RTLOGGRPFLAGS_LEVEL_11
},
1796 { "l12", RTLOGGRPFLAGS_LEVEL_12
},
1797 { "level12", RTLOGGRPFLAGS_LEVEL_12
},
1798 { "f", RTLOGGRPFLAGS_FLOW
},
1799 { "flow", RTLOGGRPFLAGS_FLOW
},
1800 { "w", RTLOGGRPFLAGS_WARN
},
1801 { "warn", RTLOGGRPFLAGS_WARN
},
1802 { "warning", RTLOGGRPFLAGS_WARN
},
1803 { "restrict", RTLOGGRPFLAGS_RESTRICT
},
1807 bool fFound
= false;
1809 for (i
= 0; i
< RT_ELEMENTS(aFlags
) && !fFound
; i
++)
1811 const char *psz1
= aFlags
[i
].pszFlag
;
1812 const char *psz2
= psz
;
1813 while (*psz1
== RT_C_TO_LOWER(*psz2
))
1819 if ( (*psz2
>= 'a' && *psz2
<= 'z')
1820 || (*psz2
>= 'A' && *psz2
<= 'Z')
1821 || (*psz2
>= '0' && *psz2
<= '9') )
1823 fFlags
|= aFlags
[i
].fFlag
;
1829 } /* for each flags */
1830 AssertMsg(fFound
, ("%.15s...", psz
));
1840 fFlags
= ~RTStrToInt32(psz
+ 1);
1842 fFlags
= RTStrToInt32(psz
);
1849 * Helper for RTLogGetGroupSettings.
1851 static int rtLogGetGroupSettingsAddOne(const char *pszName
, uint32_t fGroup
, char **ppszBuf
, size_t *pcchBuf
, bool *pfNotFirst
)
1853 # define APPEND_PSZ(psz,cch) do { memcpy(*ppszBuf, (psz), (cch)); *ppszBuf += (cch); *pcchBuf -= (cch); } while (0)
1854 # define APPEND_SZ(sz) APPEND_PSZ(sz, sizeof(sz) - 1)
1855 # define APPEND_CH(ch) do { **ppszBuf = (ch); *ppszBuf += 1; *pcchBuf -= 1; } while (0)
1860 size_t cchName
= strlen(pszName
);
1861 if (cchName
+ 1 + *pfNotFirst
> *pcchBuf
)
1862 return VERR_BUFFER_OVERFLOW
;
1867 APPEND_PSZ(pszName
, cchName
);
1870 * Only generate mnemonics for the simple+common bits.
1872 if (fGroup
== (RTLOGGRPFLAGS_ENABLED
| RTLOGGRPFLAGS_LEVEL_1
))
1874 else if ( fGroup
== (RTLOGGRPFLAGS_ENABLED
| RTLOGGRPFLAGS_LEVEL_1
| RTLOGGRPFLAGS_LEVEL_2
| RTLOGGRPFLAGS_FLOW
)
1875 && *pcchBuf
>= sizeof(".e.l.f"))
1876 APPEND_SZ(".e.l.f");
1877 else if ( fGroup
== (RTLOGGRPFLAGS_ENABLED
| RTLOGGRPFLAGS_LEVEL_1
| RTLOGGRPFLAGS_FLOW
)
1878 && *pcchBuf
>= sizeof(".e.f"))
1880 else if (*pcchBuf
>= 1 + 10 + 1)
1884 cch
= RTStrFormatNumber(*ppszBuf
, fGroup
, 16, 0, 0, RTSTR_F_SPECIAL
| RTSTR_F_32BIT
);
1889 return VERR_BUFFER_OVERFLOW
;
1894 return VINF_SUCCESS
;
1899 * Get the current log group settings as a string.
1901 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
1902 * @param pLogger Logger instance (NULL for default logger).
1903 * @param pszBuf The output buffer.
1904 * @param cchBuf The size of the output buffer. Must be greater
1907 RTDECL(int) RTLogGetGroupSettings(PRTLOGGER pLogger
, char *pszBuf
, size_t cchBuf
)
1909 bool fNotFirst
= false;
1910 int rc
= VINF_SUCCESS
;
1922 pLogger
= RTLogDefaultInstance();
1926 return VINF_SUCCESS
;
1930 cGroups
= pLogger
->cGroups
;
1933 * Check if all are the same.
1935 fGroup
= pLogger
->afGroups
[0];
1936 for (i
= 1; i
< cGroups
; i
++)
1937 if (pLogger
->afGroups
[i
] != fGroup
)
1940 rc
= rtLogGetGroupSettingsAddOne("all", fGroup
, &pszBuf
, &cchBuf
, &fNotFirst
);
1945 * Iterate all the groups and print all that are enabled.
1947 for (i
= 0; i
< cGroups
; i
++)
1949 fGroup
= pLogger
->afGroups
[i
];
1952 const char *pszName
= pLogger
->pInt
->papszGroups
[i
];
1955 rc
= rtLogGetGroupSettingsAddOne(pszName
, fGroup
, &pszBuf
, &cchBuf
, &fNotFirst
);
1966 RT_EXPORT_SYMBOL(RTLogGetGroupSettings
);
1971 * Updates the flags for the logger instance using the specified
1972 * specification string.
1974 * @returns iprt status code.
1975 * Failures can safely be ignored.
1976 * @param pLogger Logger instance (NULL for default logger).
1977 * @param pszValue Value to parse.
1979 RTDECL(int) RTLogFlags(PRTLOGGER pLogger
, const char *pszValue
)
1981 int rc
= VINF_SUCCESS
;
1988 pLogger
= RTLogDefaultInstance();
1990 return VINF_SUCCESS
;
1994 * Iterate the string.
1998 /* check no prefix. */
2004 while (RT_C_IS_SPACE(*pszValue
))
2009 while ((ch
= *pszValue
) != '\0')
2011 if (ch
== 'n' && pszValue
[1] == 'o')
2021 else if (ch
== '-' || ch
== '!' || ch
== '~')
2031 for (i
= 0; i
< RT_ELEMENTS(g_aLogFlags
); i
++)
2033 if (!strncmp(pszValue
, g_aLogFlags
[i
].pszInstr
, g_aLogFlags
[i
].cchInstr
))
2035 if (fNo
== g_aLogFlags
[i
].fInverted
)
2036 pLogger
->fFlags
|= g_aLogFlags
[i
].fFlag
;
2038 pLogger
->fFlags
&= ~g_aLogFlags
[i
].fFlag
;
2039 pszValue
+= g_aLogFlags
[i
].cchInstr
;
2044 /* unknown instruction? */
2045 if (i
>= RT_ELEMENTS(g_aLogFlags
))
2047 AssertMsgFailed(("Invalid flags! unknown instruction %.20s\n", pszValue
));
2051 /* skip blanks and delimiters. */
2052 while (RT_C_IS_SPACE(*pszValue
) || *pszValue
== ';')
2054 } /* while more environment variable value left */
2058 RT_EXPORT_SYMBOL(RTLogFlags
);
2062 * Changes the buffering setting of the specified logger.
2064 * This can be used for optimizing longish logging sequences.
2066 * @returns The old state.
2067 * @param pLogger The logger instance (NULL is an alias for the
2069 * @param fBuffered The new state.
2071 RTDECL(bool) RTLogSetBuffering(PRTLOGGER pLogger
, bool fBuffered
)
2076 * Resolve the logger instance.
2080 pLogger
= RTLogDefaultInstance();
2086 fOld
= !!(pLogger
->fFlags
& RTLOGFLAGS_BUFFERED
);
2088 pLogger
->fFlags
|= RTLOGFLAGS_BUFFERED
;
2090 pLogger
->fFlags
&= ~RTLOGFLAGS_BUFFERED
;
2091 rtlogUnlock(pLogger
);
2095 RT_EXPORT_SYMBOL(RTLogSetBuffering
);
2099 RTDECL(uint32_t) RTLogSetGroupLimit(PRTLOGGER pLogger
, uint32_t cMaxEntriesPerGroup
)
2102 * Resolve the logger instance.
2106 pLogger
= RTLogDefaultInstance();
2112 uint32_t cOld
= pLogger
->pInt
->cMaxEntriesPerGroup
;
2113 pLogger
->pInt
->cMaxEntriesPerGroup
= cMaxEntriesPerGroup
;
2114 rtlogUnlock(pLogger
);
2123 * Get the current log flags as a string.
2125 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
2126 * @param pLogger Logger instance (NULL for default logger).
2127 * @param pszBuf The output buffer.
2128 * @param cchBuf The size of the output buffer. Must be greater
2131 RTDECL(int) RTLogGetFlags(PRTLOGGER pLogger
, char *pszBuf
, size_t cchBuf
)
2133 bool fNotFirst
= false;
2134 int rc
= VINF_SUCCESS
;
2145 pLogger
= RTLogDefaultInstance();
2149 return VINF_SUCCESS
;
2154 * Add the flags in the list.
2156 fFlags
= pLogger
->fFlags
;
2157 for (i
= 0; i
< RT_ELEMENTS(g_aLogFlags
); i
++)
2158 if ( !g_aLogFlags
[i
].fInverted
2159 ? (g_aLogFlags
[i
].fFlag
& fFlags
)
2160 : !(g_aLogFlags
[i
].fFlag
& fFlags
))
2162 size_t cchInstr
= g_aLogFlags
[i
].cchInstr
;
2163 if (cchInstr
+ fNotFirst
+ 1 > cchBuf
)
2165 rc
= VERR_BUFFER_OVERFLOW
;
2173 memcpy(pszBuf
, g_aLogFlags
[i
].pszInstr
, cchInstr
);
2181 RT_EXPORT_SYMBOL(RTLogGetFlags
);
2185 * Finds the end of a destination value.
2187 * The value ends when we counter a ';' or a free standing word (space on both
2188 * from the g_aLogDst table. (If this is problematic for someone, we could
2189 * always do quoting and escaping.)
2191 * @returns Value length in chars.
2192 * @param pszValue The first char after '=' or ':'.
2194 static size_t rtLogDestFindValueLength(const char *pszValue
)
2198 while ((ch
= pszValue
[off
]) != '\0' && ch
!= ';')
2200 if (!RT_C_IS_SPACE(ch
))
2205 size_t cchThusFar
= off
;
2208 while ((ch
= pszValue
[off
]) != '\0' && RT_C_IS_SPACE(ch
));
2212 if (ch
== 'n' && pszValue
[off
+ 1] == 'o')
2214 for (i
= 0; i
< RT_ELEMENTS(g_aLogDst
); i
++)
2215 if (!strncmp(&pszValue
[off
], g_aLogDst
[i
].pszInstr
, g_aLogDst
[i
].cchInstr
))
2217 ch
= pszValue
[off
+ g_aLogDst
[i
].cchInstr
];
2218 if (ch
== '\0' || RT_C_IS_SPACE(ch
) || ch
== '=' || ch
== ':' || ch
== ';')
2228 * Updates the logger destination using the specified string.
2230 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
2231 * @param pLogger Logger instance (NULL for default logger).
2232 * @param pszValue The value to parse.
2234 RTDECL(int) RTLogDestinations(PRTLOGGER pLogger
, char const *pszValue
)
2241 pLogger
= RTLogDefaultInstance();
2243 return VINF_SUCCESS
;
2255 while (RT_C_IS_SPACE(*pszValue
))
2260 /* check no prefix. */
2262 if ( pszValue
[0] == 'n'
2263 && pszValue
[1] == 'o'
2264 && ( pszValue
[2] != 'd'
2265 || pszValue
[3] != 'e'
2266 || pszValue
[4] != 'n'
2267 || pszValue
[5] != 'y'))
2274 for (i
= 0; i
< RT_ELEMENTS(g_aLogDst
); i
++)
2276 size_t cchInstr
= strlen(g_aLogDst
[i
].pszInstr
);
2277 if (!strncmp(pszValue
, g_aLogDst
[i
].pszInstr
, cchInstr
))
2280 pLogger
->fDestFlags
|= g_aLogDst
[i
].fFlag
;
2282 pLogger
->fDestFlags
&= ~g_aLogDst
[i
].fFlag
;
2283 pszValue
+= cchInstr
;
2285 /* check for value. */
2286 while (RT_C_IS_SPACE(*pszValue
))
2288 if (*pszValue
== '=' || *pszValue
== ':')
2291 size_t cch
= rtLogDestFindValueLength(pszValue
);
2292 const char *pszEnd
= pszValue
+ cch
;
2295 char szTmp
[sizeof(pLogger
->pInt
->szFilename
)];
2304 else if (i
== 0 /* file */ && !fNo
)
2306 AssertReturn(cch
< sizeof(pLogger
->pInt
->szFilename
), VERR_OUT_OF_RANGE
);
2307 memcpy(pLogger
->pInt
->szFilename
, pszValue
, cch
);
2308 pLogger
->pInt
->szFilename
[cch
] = '\0';
2309 /** @todo reopen log file if pLogger->pInt->fCreated is true ... */
2312 else if (i
== 1 /* dir */ && !fNo
)
2314 const char *pszFile
= RTPathFilename(pLogger
->pInt
->szFilename
);
2315 size_t cchFile
= pszFile
? strlen(pszFile
) : 0;
2316 AssertReturn(cchFile
+ cch
+ 1 < sizeof(pLogger
->pInt
->szFilename
), VERR_OUT_OF_RANGE
);
2317 memcpy(szTmp
, cchFile
? pszFile
: "", cchFile
+ 1);
2319 memcpy(pLogger
->pInt
->szFilename
, pszValue
, cch
);
2320 pLogger
->pInt
->szFilename
[cch
] = '\0';
2321 RTPathStripTrailingSlash(pLogger
->pInt
->szFilename
);
2323 cch
= strlen(pLogger
->pInt
->szFilename
);
2324 pLogger
->pInt
->szFilename
[cch
++] = '/';
2325 memcpy(&pLogger
->pInt
->szFilename
[cch
], szTmp
, cchFile
);
2326 pLogger
->pInt
->szFilename
[cch
+ cchFile
] = '\0';
2327 /** @todo reopen log file if pLogger->pInt->fCreated is true ... */
2329 else if (i
== 2 /* history */)
2333 uint32_t cHistory
= 0;
2334 int rc
= RTStrCopyEx(szTmp
, sizeof(szTmp
), pszValue
, cch
);
2336 rc
= RTStrToUInt32Full(szTmp
, 0, &cHistory
);
2337 AssertMsgReturn(RT_SUCCESS(rc
) && cHistory
< _1M
, ("Invalid history value %s (%Rrc)!\n", szTmp
, rc
), rc
);
2338 pLogger
->pInt
->cHistory
= cHistory
;
2341 pLogger
->pInt
->cHistory
= 0;
2343 else if (i
== 3 /* histsize */)
2347 int rc
= RTStrCopyEx(szTmp
, sizeof(szTmp
), pszValue
, cch
);
2349 rc
= RTStrToUInt64Full(szTmp
, 0, &pLogger
->pInt
->cbHistoryFileMax
);
2350 AssertMsgRCReturn(rc
, ("Invalid history file size value %s (%Rrc)!\n", szTmp
, rc
), rc
);
2351 if (pLogger
->pInt
->cbHistoryFileMax
== 0)
2352 pLogger
->pInt
->cbHistoryFileMax
= UINT64_MAX
;
2355 pLogger
->pInt
->cbHistoryFileMax
= UINT64_MAX
;
2357 else if (i
== 4 /* histtime */)
2361 int rc
= RTStrCopyEx(szTmp
, sizeof(szTmp
), pszValue
, cch
);
2363 rc
= RTStrToUInt32Full(szTmp
, 0, &pLogger
->pInt
->cSecsHistoryTimeSlot
);
2364 AssertMsgRCReturn(rc
, ("Invalid history time slot value %s (%Rrc)!\n", szTmp
, rc
), rc
);
2365 if (pLogger
->pInt
->cSecsHistoryTimeSlot
== 0)
2366 pLogger
->pInt
->cSecsHistoryTimeSlot
= UINT32_MAX
;
2369 pLogger
->pInt
->cSecsHistoryTimeSlot
= UINT32_MAX
;
2371 # endif /* IN_RING3 */
2372 else if (i
== 5 /* ringbuf */ && !fNo
)
2374 int rc
= RTStrCopyEx(szTmp
, sizeof(szTmp
), pszValue
, cch
);
2375 uint32_t cbRingBuf
= 0;
2377 rc
= RTStrToUInt32Full(szTmp
, 0, &cbRingBuf
);
2378 AssertMsgRCReturn(rc
, ("Invalid ring buffer size value '%s' (%Rrc)!\n", szTmp
, rc
), rc
);
2381 cbRingBuf
= RTLOG_RINGBUF_DEFAULT_SIZE
;
2382 else if (cbRingBuf
< RTLOG_RINGBUF_MIN_SIZE
)
2383 cbRingBuf
= RTLOG_RINGBUF_MIN_SIZE
;
2384 else if (cbRingBuf
> RTLOG_RINGBUF_MAX_SIZE
)
2385 cbRingBuf
= RTLOG_RINGBUF_MAX_SIZE
;
2387 cbRingBuf
= RT_ALIGN_32(cbRingBuf
, 64);
2388 rc
= rtLogRingBufAdjust(pLogger
, cbRingBuf
, false /*fForce*/);
2393 AssertMsgFailedReturn(("Invalid destination value! %s%s doesn't take a value!\n",
2394 fNo
? "no" : "", g_aLogDst
[i
].pszInstr
),
2395 VERR_INVALID_PARAMETER
);
2397 pszValue
= pszEnd
+ (*pszEnd
!= '\0');
2399 else if (i
== 5 /* ringbuf */ && !fNo
&& !pLogger
->pInt
->pszRingBuf
)
2401 int rc
= rtLogRingBufAdjust(pLogger
, pLogger
->pInt
->cbRingBuf
, false /*fForce*/);
2409 /* assert known instruction */
2410 AssertMsgReturn(i
< RT_ELEMENTS(g_aLogDst
),
2411 ("Invalid destination value! unknown instruction %.20s\n", pszValue
),
2412 VERR_INVALID_PARAMETER
);
2414 /* skip blanks and delimiters. */
2415 while (RT_C_IS_SPACE(*pszValue
) || *pszValue
== ';')
2417 } /* while more environment variable value left */
2419 return VINF_SUCCESS
;
2421 RT_EXPORT_SYMBOL(RTLogDestinations
);
2425 * Clear the file delay flag if set, opening the destination and flushing.
2427 * @returns IPRT status code.
2428 * @param pLogger Logger instance (NULL for default logger).
2429 * @param pszValue The value to parse.
2430 * @param pErrInfo Where to return extended error info. Optional.
2432 RTDECL(int) RTLogClearFileDelayFlag(PRTLOGGER pLogger
, PRTERRINFO pErrInfo
)
2439 pLogger
= RTLogDefaultInstance();
2441 return VINF_SUCCESS
;
2447 int rc
= rtlogLock(pLogger
);
2450 if (pLogger
->fDestFlags
& RTLOGDEST_F_DELAY_FILE
)
2452 pLogger
->fDestFlags
&= ~RTLOGDEST_F_DELAY_FILE
;
2454 if ( pLogger
->fDestFlags
& RTLOGDEST_FILE
2455 && pLogger
->pInt
->hFile
== NIL_RTFILE
)
2457 rc
= rtR3LogOpenFileDestination(pLogger
, pErrInfo
);
2459 rtlogFlush(pLogger
, false /*fNeedSpace*/);
2462 RT_NOREF(pErrInfo
); /** @todo fix create API to use RTErrInfo */
2464 rtlogUnlock(pLogger
);
2466 return VINF_SUCCESS
;
2468 RT_EXPORT_SYMBOL(RTLogClearFileDelayFlag
);
2472 * Get the current log destinations as a string.
2474 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
2475 * @param pLogger Logger instance (NULL for default logger).
2476 * @param pszBuf The output buffer.
2477 * @param cchBuf The size of the output buffer. Must be greater
2480 RTDECL(int) RTLogGetDestinations(PRTLOGGER pLogger
, char *pszBuf
, size_t cchBuf
)
2482 bool fNotFirst
= false;
2483 int rc
= VINF_SUCCESS
;
2484 uint32_t fDestFlags
;
2487 AssertReturn(cchBuf
, VERR_INVALID_PARAMETER
);
2495 pLogger
= RTLogDefaultInstance();
2497 return VINF_SUCCESS
;
2501 * Add the flags in the list.
2503 fDestFlags
= pLogger
->fDestFlags
;
2504 for (i
= 6; i
< RT_ELEMENTS(g_aLogDst
); i
++)
2505 if (g_aLogDst
[i
].fFlag
& fDestFlags
)
2509 rc
= RTStrCopyP(&pszBuf
, &cchBuf
, " ");
2513 rc
= RTStrCopyP(&pszBuf
, &cchBuf
, g_aLogDst
[i
].pszInstr
);
2525 if (fDestFlags
& RTLOGDEST_FILE
)
2527 rc
= RTStrCopyP(&pszBuf
, &cchBuf
, fNotFirst
? " file=" : "file=");
2530 rc
= RTStrCopyP(&pszBuf
, &cchBuf
, pLogger
->pInt
->szFilename
);
2535 if (pLogger
->pInt
->cHistory
)
2537 RTStrPrintf(szNum
, sizeof(szNum
), fNotFirst
? " history=%u" : "history=%u", pLogger
->pInt
->cHistory
);
2538 rc
= RTStrCopyP(&pszBuf
, &cchBuf
, szNum
);
2543 if (pLogger
->pInt
->cbHistoryFileMax
!= UINT64_MAX
)
2545 RTStrPrintf(szNum
, sizeof(szNum
), fNotFirst
? " histsize=%llu" : "histsize=%llu", pLogger
->pInt
->cbHistoryFileMax
);
2546 rc
= RTStrCopyP(&pszBuf
, &cchBuf
, szNum
);
2551 if (pLogger
->pInt
->cSecsHistoryTimeSlot
!= UINT32_MAX
)
2553 RTStrPrintf(szNum
, sizeof(szNum
), fNotFirst
? " histtime=%llu" : "histtime=%llu", pLogger
->pInt
->cSecsHistoryTimeSlot
);
2554 rc
= RTStrCopyP(&pszBuf
, &cchBuf
, szNum
);
2560 # endif /* IN_RING3 */
2563 * Add the ring buffer.
2565 if (fDestFlags
& RTLOGDEST_RINGBUF
)
2567 if (pLogger
->pInt
->cbRingBuf
== RTLOG_RINGBUF_DEFAULT_SIZE
)
2568 rc
= RTStrCopyP(&pszBuf
, &cchBuf
, fNotFirst
? " ringbuf" : "ringbuf");
2571 RTStrPrintf(szNum
, sizeof(szNum
), fNotFirst
? " ringbuf=%#x" : "ringbuf=%#x", pLogger
->pInt
->cbRingBuf
);
2572 rc
= RTStrCopyP(&pszBuf
, &cchBuf
, szNum
);
2579 return VINF_SUCCESS
;
2581 RT_EXPORT_SYMBOL(RTLogGetDestinations
);
2586 * Flushes the specified logger.
2588 * @param pLogger The logger instance to flush.
2589 * If NULL the default instance is used. The default instance
2590 * will not be initialized by this call.
2592 RTDECL(void) RTLogFlush(PRTLOGGER pLogger
)
2600 pLogger
= &g_Logger
;
2602 pLogger
= g_pLogger
;
2609 * Any thing to flush?
2611 if ( pLogger
->offScratch
2613 || (pLogger
->fDestFlags
& RTLOGDEST_RINGBUF
)
2619 * Acquire logger instance sem.
2621 int rc
= rtlogLock(pLogger
);
2628 rtlogFlush(pLogger
, false /*fNeedSpace*/);
2632 * Since this is an explicit flush call, the ring buffer content should
2633 * be flushed to the other destinations if active.
2635 if ( (pLogger
->fDestFlags
& RTLOGDEST_RINGBUF
)
2636 && pLogger
->pInt
->pszRingBuf
/* paranoia */)
2637 rtLogRingBufFlush(pLogger
);
2640 * Release the semaphore.
2642 rtlogUnlock(pLogger
);
2646 RT_EXPORT_SYMBOL(RTLogFlush
);
2650 * Common worker for RTLogDefaultInstance and RTLogDefaultInstanceEx.
2652 DECL_FORCE_INLINE(PRTLOGGER
) rtLogDefaultInstanceCommon(void)
2660 * Check per thread loggers first.
2662 if (g_cPerThreadLoggers
)
2664 const RTNATIVETHREAD Self
= RTThreadNativeSelf();
2665 int32_t i
= RT_ELEMENTS(g_aPerThreadLoggers
);
2667 if (g_aPerThreadLoggers
[i
].NativeThread
== Self
)
2668 return g_aPerThreadLoggers
[i
].pLogger
;
2670 # endif /* IN_RING0 */
2673 * If no per thread logger, use the default one.
2676 g_pLogger
= RTLogDefaultInit();
2682 RTDECL(PRTLOGGER
) RTLogDefaultInstance(void)
2684 return rtLogDefaultInstanceCommon();
2686 RT_EXPORT_SYMBOL(RTLogDefaultInstance
);
2689 RTDECL(PRTLOGGER
) RTLogDefaultInstanceEx(uint32_t fFlagsAndGroup
)
2691 PRTLOGGER pLogger
= rtLogDefaultInstanceCommon();
2694 if (pLogger
->fFlags
& RTLOGFLAGS_DISABLED
)
2698 uint16_t const fFlags
= RT_LO_U16(fFlagsAndGroup
);
2699 uint16_t const iGroup
= RT_HI_U16(fFlagsAndGroup
);
2700 if ( iGroup
!= UINT16_MAX
2701 && ( (pLogger
->afGroups
[iGroup
< pLogger
->cGroups
? iGroup
: 0] & (fFlags
| (uint32_t)RTLOGGRPFLAGS_ENABLED
))
2702 != (fFlags
| (uint32_t)RTLOGGRPFLAGS_ENABLED
)))
2708 RT_EXPORT_SYMBOL(RTLogDefaultInstanceEx
);
2712 * Common worker for RTLogGetDefaultInstance and RTLogGetDefaultInstanceEx.
2714 DECL_FORCE_INLINE(PRTLOGGER
) rtLogGetDefaultInstanceCommon(void)
2721 * Check per thread loggers first.
2723 if (g_cPerThreadLoggers
)
2725 const RTNATIVETHREAD Self
= RTThreadNativeSelf();
2726 int32_t i
= RT_ELEMENTS(g_aPerThreadLoggers
);
2728 if (g_aPerThreadLoggers
[i
].NativeThread
== Self
)
2729 return g_aPerThreadLoggers
[i
].pLogger
;
2731 # endif /* IN_RING0 */
2738 RTDECL(PRTLOGGER
) RTLogGetDefaultInstance(void)
2740 return rtLogGetDefaultInstanceCommon();
2742 RT_EXPORT_SYMBOL(RTLogGetDefaultInstance
);
2745 RTDECL(PRTLOGGER
) RTLogGetDefaultInstanceEx(uint32_t fFlagsAndGroup
)
2747 PRTLOGGER pLogger
= rtLogGetDefaultInstanceCommon();
2750 if (pLogger
->fFlags
& RTLOGFLAGS_DISABLED
)
2754 uint32_t const fFlags
= RT_LO_U16(fFlagsAndGroup
);
2755 uint16_t const iGroup
= RT_HI_U16(fFlagsAndGroup
);
2756 if ( iGroup
!= UINT16_MAX
2757 && ( (pLogger
->afGroups
[iGroup
< pLogger
->cGroups
? iGroup
: 0] & (fFlags
| RTLOGGRPFLAGS_ENABLED
))
2758 != (fFlags
| RTLOGGRPFLAGS_ENABLED
)))
2764 RT_EXPORT_SYMBOL(RTLogGetDefaultInstanceEx
);
2769 * Sets the default logger instance.
2771 * @returns iprt status code.
2772 * @param pLogger The new default logger instance.
2774 RTDECL(PRTLOGGER
) RTLogSetDefaultInstance(PRTLOGGER pLogger
)
2776 return ASMAtomicXchgPtrT(&g_pLogger
, pLogger
, PRTLOGGER
);
2778 RT_EXPORT_SYMBOL(RTLogSetDefaultInstance
);
2784 * Changes the default logger instance for the current thread.
2786 * @returns IPRT status code.
2787 * @param pLogger The logger instance. Pass NULL for deregistration.
2788 * @param uKey Associated key for cleanup purposes. If pLogger is NULL,
2789 * all instances with this key will be deregistered. So in
2790 * order to only deregister the instance associated with the
2791 * current thread use 0.
2793 RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger
, uintptr_t uKey
)
2796 RTNATIVETHREAD Self
= RTThreadNativeSelf();
2802 AssertReturn(pLogger
->u32Magic
== RTLOGGER_MAGIC
, VERR_INVALID_MAGIC
);
2805 * Iterate the table to see if there is already an entry for this thread.
2807 i
= RT_ELEMENTS(g_aPerThreadLoggers
);
2809 if (g_aPerThreadLoggers
[i
].NativeThread
== Self
)
2811 ASMAtomicWritePtr((void * volatile *)&g_aPerThreadLoggers
[i
].uKey
, (void *)uKey
);
2812 g_aPerThreadLoggers
[i
].pLogger
= pLogger
;
2813 return VINF_SUCCESS
;
2817 * Allocate a new table entry.
2819 i
= ASMAtomicIncS32(&g_cPerThreadLoggers
);
2820 if (i
> (int32_t)RT_ELEMENTS(g_aPerThreadLoggers
))
2822 ASMAtomicDecS32(&g_cPerThreadLoggers
);
2823 return VERR_BUFFER_OVERFLOW
; /* horrible error code! */
2826 for (j
= 0; j
< 10; j
++)
2828 i
= RT_ELEMENTS(g_aPerThreadLoggers
);
2831 AssertCompile(sizeof(RTNATIVETHREAD
) == sizeof(void*));
2832 if ( g_aPerThreadLoggers
[i
].NativeThread
== NIL_RTNATIVETHREAD
2833 && ASMAtomicCmpXchgPtr((void * volatile *)&g_aPerThreadLoggers
[i
].NativeThread
, (void *)Self
, (void *)NIL_RTNATIVETHREAD
))
2835 ASMAtomicWritePtr((void * volatile *)&g_aPerThreadLoggers
[i
].uKey
, (void *)uKey
);
2836 ASMAtomicWritePtr(&g_aPerThreadLoggers
[i
].pLogger
, pLogger
);
2837 return VINF_SUCCESS
;
2842 ASMAtomicDecS32(&g_cPerThreadLoggers
);
2843 rc
= VERR_INTERNAL_ERROR
;
2848 * Search the array for the current thread.
2850 int32_t i
= RT_ELEMENTS(g_aPerThreadLoggers
);
2852 if ( g_aPerThreadLoggers
[i
].NativeThread
== Self
2853 || g_aPerThreadLoggers
[i
].uKey
== uKey
)
2855 ASMAtomicWriteNullPtr((void * volatile *)&g_aPerThreadLoggers
[i
].uKey
);
2856 ASMAtomicWriteNullPtr(&g_aPerThreadLoggers
[i
].pLogger
);
2857 ASMAtomicWriteHandle(&g_aPerThreadLoggers
[i
].NativeThread
, NIL_RTNATIVETHREAD
);
2858 ASMAtomicDecS32(&g_cPerThreadLoggers
);
2865 RT_EXPORT_SYMBOL(RTLogSetDefaultInstanceThread
);
2866 #endif /* IN_RING0 */
2870 * Write to a logger instance.
2872 * @param pLogger Pointer to logger instance.
2873 * @param pszFormat Format string.
2874 * @param args Format arguments.
2876 RTDECL(void) RTLogLoggerV(PRTLOGGER pLogger
, const char *pszFormat
, va_list args
)
2878 RTLogLoggerExV(pLogger
, 0, ~0U, pszFormat
, args
);
2880 RT_EXPORT_SYMBOL(RTLogLoggerV
);
2884 * Write to a logger instance.
2886 * This function will check whether the instance, group and flags makes up a
2887 * logging kind which is currently enabled before writing anything to the log.
2889 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.
2890 * @param fFlags The logging flags.
2891 * @param iGroup The group.
2892 * The value ~0U is reserved for compatibility with RTLogLogger[V] and is
2893 * only for internal usage!
2894 * @param pszFormat Format string.
2895 * @param args Format arguments.
2897 RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger
, unsigned fFlags
, unsigned iGroup
, const char *pszFormat
, va_list args
)
2902 * A NULL logger means default instance.
2906 pLogger
= RTLogDefaultInstance();
2912 * Validate and correct iGroup.
2914 if (iGroup
!= ~0U && iGroup
>= pLogger
->cGroups
)
2918 * If no output, then just skip it.
2920 if ( (pLogger
->fFlags
& RTLOGFLAGS_DISABLED
)
2922 || !pLogger
->fDestFlags
2924 || !pszFormat
|| !*pszFormat
)
2927 && (pLogger
->afGroups
[iGroup
] & (fFlags
| RTLOGGRPFLAGS_ENABLED
)) != (fFlags
| RTLOGGRPFLAGS_ENABLED
))
2931 * Acquire logger instance sem.
2933 rc
= rtlogLock(pLogger
);
2937 if (pLogger
->fDestFlags
& ~RTLOGDEST_FILE
)
2938 rtR0LogLoggerExFallback(pLogger
->fDestFlags
, pLogger
->fFlags
, pLogger
->pInt
, pszFormat
, args
);
2944 * Check restrictions and call worker.
2947 if (RT_UNLIKELY( (pLogger
->fFlags
& RTLOGFLAGS_RESTRICT_GROUPS
)
2948 && iGroup
< pLogger
->cGroups
2949 && (pLogger
->afGroups
[iGroup
] & RTLOGGRPFLAGS_RESTRICT
)
2950 && ++pLogger
->pInt
->pacEntriesPerGroup
[iGroup
] >= pLogger
->pInt
->cMaxEntriesPerGroup
))
2952 uint32_t cEntries
= pLogger
->pInt
->pacEntriesPerGroup
[iGroup
];
2953 if (cEntries
> pLogger
->pInt
->cMaxEntriesPerGroup
)
2954 pLogger
->pInt
->pacEntriesPerGroup
[iGroup
] = cEntries
- 1;
2957 rtlogLoggerExVLocked(pLogger
, fFlags
, iGroup
, pszFormat
, args
);
2958 if ( pLogger
->pInt
->papszGroups
2959 && pLogger
->pInt
->papszGroups
[iGroup
])
2960 rtlogLoggerExFLocked(pLogger
, fFlags
, iGroup
, "%u messages from group %s (#%u), muting it.\n",
2961 cEntries
, pLogger
->pInt
->papszGroups
[iGroup
], iGroup
);
2963 rtlogLoggerExFLocked(pLogger
, fFlags
, iGroup
, "%u messages from group #%u, muting it.\n",
2969 rtlogLoggerExVLocked(pLogger
, fFlags
, iGroup
, pszFormat
, args
);
2972 * Release the semaphore.
2974 rtlogUnlock(pLogger
);
2976 RT_EXPORT_SYMBOL(RTLogLoggerExV
);
2981 * For rtR0LogLoggerExFallbackOutput and rtR0LogLoggerExFallbackFlush.
2983 typedef struct RTR0LOGLOGGERFALLBACK
2985 /** The current scratch buffer offset. */
2986 uint32_t offScratch
;
2987 /** The destination flags. */
2988 uint32_t fDestFlags
;
2989 /** For ring buffer output. */
2990 PRTLOGGERINTERNAL pInt
;
2991 /** The scratch buffer. */
2992 char achScratch
[80];
2993 } RTR0LOGLOGGERFALLBACK
;
2994 /** Pointer to RTR0LOGLOGGERFALLBACK which is used by
2995 * rtR0LogLoggerExFallbackOutput. */
2996 typedef RTR0LOGLOGGERFALLBACK
*PRTR0LOGLOGGERFALLBACK
;
3000 * Flushes the fallback buffer.
3002 * @param pThis The scratch buffer.
3004 static void rtR0LogLoggerExFallbackFlush(PRTR0LOGLOGGERFALLBACK pThis
)
3006 if (!pThis
->offScratch
)
3009 if ( (pThis
->fDestFlags
& RTLOGDEST_RINGBUF
)
3011 && pThis
->pInt
->pszRingBuf
/* paranoia */)
3012 rtLogRingBufWrite(pThis
->pInt
, pThis
->achScratch
, pThis
->offScratch
);
3015 if (pThis
->fDestFlags
& RTLOGDEST_USER
)
3016 RTLogWriteUser(pThis
->achScratch
, pThis
->offScratch
);
3018 if (pThis
->fDestFlags
& RTLOGDEST_DEBUGGER
)
3019 RTLogWriteDebugger(pThis
->achScratch
, pThis
->offScratch
);
3021 if (pThis
->fDestFlags
& RTLOGDEST_STDOUT
)
3022 RTLogWriteStdOut(pThis
->achScratch
, pThis
->offScratch
);
3024 if (pThis
->fDestFlags
& RTLOGDEST_STDERR
)
3025 RTLogWriteStdErr(pThis
->achScratch
, pThis
->offScratch
);
3028 if (pThis
->fDestFlags
& RTLOGDEST_COM
)
3029 RTLogWriteCom(pThis
->achScratch
, pThis
->offScratch
);
3033 /* empty the buffer. */
3034 pThis
->offScratch
= 0;
3039 * Callback for RTLogFormatV used by rtR0LogLoggerExFallback.
3040 * See PFNLOGOUTPUT() for details.
3042 static DECLCALLBACK(size_t) rtR0LogLoggerExFallbackOutput(void *pv
, const char *pachChars
, size_t cbChars
)
3044 PRTR0LOGLOGGERFALLBACK pThis
= (PRTR0LOGLOGGERFALLBACK
)pv
;
3051 uint32_t cb
= sizeof(pThis
->achScratch
) - pThis
->offScratch
- 1; /* minus 1 - for the string terminator. */
3053 cb
= (uint32_t)cbChars
;
3056 memcpy(&pThis
->achScratch
[pThis
->offScratch
], pachChars
, cb
);
3059 pThis
->offScratch
+= cb
;
3070 pThis
->achScratch
[pThis
->offScratch
] = '\0';
3071 rtR0LogLoggerExFallbackFlush(pThis
);
3074 /* won't ever get here! */
3079 * Termination call, flush the log.
3081 pThis
->achScratch
[pThis
->offScratch
] = '\0';
3082 rtR0LogLoggerExFallbackFlush(pThis
);
3089 * Ring-0 fallback for cases where we're unable to grab the lock.
3091 * This will happen when we're at a too high IRQL on Windows for instance and
3092 * needs to be dealt with or we'll drop a lot of log output. This fallback will
3093 * only output to some of the log destinations as a few of them may be doing
3094 * dangerous things. We won't be doing any prefixing here either, at least not
3095 * for the present, because it's too much hassle.
3097 * @param fDestFlags The destination flags.
3098 * @param fFlags The logger flags.
3099 * @param pInt The internal logger data, for ring buffer output.
3100 * @param pszFormat The format string.
3101 * @param va The format arguments.
3103 static void rtR0LogLoggerExFallback(uint32_t fDestFlags
, uint32_t fFlags
, PRTLOGGERINTERNAL pInt
,
3104 const char *pszFormat
, va_list va
)
3106 RTR0LOGLOGGERFALLBACK This
;
3107 This
.fDestFlags
= fDestFlags
;
3110 /* fallback indicator. */
3111 This
.offScratch
= 2;
3112 This
.achScratch
[0] = '[';
3113 This
.achScratch
[1] = 'F';
3115 /* selected prefixes */
3116 if (fFlags
& RTLOGFLAGS_PREFIX_PID
)
3118 RTPROCESS Process
= RTProcSelf();
3119 This
.achScratch
[This
.offScratch
++] = ' ';
3120 This
.offScratch
+= RTStrFormatNumber(&This
.achScratch
[This
.offScratch
], Process
, 16, sizeof(RTPROCESS
) * 2, 0, RTSTR_F_ZEROPAD
);
3122 if (fFlags
& RTLOGFLAGS_PREFIX_TID
)
3124 RTNATIVETHREAD Thread
= RTThreadNativeSelf();
3125 This
.achScratch
[This
.offScratch
++] = ' ';
3126 This
.offScratch
+= RTStrFormatNumber(&This
.achScratch
[This
.offScratch
], Thread
, 16, sizeof(RTNATIVETHREAD
) * 2, 0, RTSTR_F_ZEROPAD
);
3129 This
.achScratch
[This
.offScratch
++] = ']';
3130 This
.achScratch
[This
.offScratch
++] = ' ';
3132 RTLogFormatV(rtR0LogLoggerExFallbackOutput
, &This
, pszFormat
, va
);
3134 #endif /* IN_RING0 */
3138 * vprintf like function for writing to the default log.
3140 * @param pszFormat Printf like format string.
3141 * @param va Optional arguments as specified in pszFormat.
3143 * @remark The API doesn't support formatting of floating point numbers at the moment.
3145 RTDECL(void) RTLogPrintfV(const char *pszFormat
, va_list va
)
3147 RTLogLoggerV(NULL
, pszFormat
, va
);
3149 RT_EXPORT_SYMBOL(RTLogPrintfV
);
3153 * Dumper vprintf-like function outputting to a logger.
3155 * @param pvUser Pointer to the logger instance to use, NULL for
3157 * @param pszFormat Format string.
3158 * @param va Format arguments.
3160 RTDECL(void) RTLogDumpPrintfV(void *pvUser
, const char *pszFormat
, va_list va
)
3162 RTLogLoggerV((PRTLOGGER
)pvUser
, pszFormat
, va
);
3164 RT_EXPORT_SYMBOL(RTLogDumpPrintfV
);
3170 * Opens/creates the log file.
3172 * @param pLogger The logger instance to update. NULL is not allowed!
3173 * @param pErrInfo Where to return extended error information.
3176 static int rtlogFileOpen(PRTLOGGER pLogger
, PRTERRINFO pErrInfo
)
3178 uint32_t fOpen
= RTFILE_O_WRITE
| RTFILE_O_DENY_NONE
;
3179 if (pLogger
->fFlags
& RTLOGFLAGS_APPEND
)
3180 fOpen
|= RTFILE_O_OPEN_CREATE
| RTFILE_O_APPEND
;
3182 fOpen
|= RTFILE_O_CREATE_REPLACE
;
3183 if (pLogger
->fFlags
& RTLOGFLAGS_WRITE_THROUGH
)
3184 fOpen
|= RTFILE_O_WRITE_THROUGH
;
3185 if (pLogger
->fDestFlags
& RTLOGDEST_F_NO_DENY
)
3186 fOpen
= (fOpen
& ~RTFILE_O_DENY_NONE
) | RTFILE_O_DENY_NOT_DELETE
;
3188 unsigned cBackoff
= 0;
3189 int rc
= RTFileOpen(&pLogger
->pInt
->hFile
, pLogger
->pInt
->szFilename
, fOpen
);
3190 while ( rc
== VERR_SHARING_VIOLATION
3191 && cBackoff
< RT_ELEMENTS(g_acMsLogBackoff
))
3193 RTThreadSleep(g_acMsLogBackoff
[cBackoff
++]);
3194 rc
= RTFileOpen(&pLogger
->pInt
->hFile
, pLogger
->pInt
->szFilename
, fOpen
);
3198 rc
= RTFileGetSize(pLogger
->pInt
->hFile
, &pLogger
->pInt
->cbHistoryFileWritten
);
3201 /* Don't complain if this fails, assume the file is empty. */
3202 pLogger
->pInt
->cbHistoryFileWritten
= 0;
3208 pLogger
->pInt
->hFile
= NIL_RTFILE
;
3209 RTErrInfoSetF(pErrInfo
, rc
, N_("could not open file '%s' (fOpen=%#x)"), pLogger
->pInt
->szFilename
, fOpen
);
3216 * Closes, rotates and opens the log files if necessary.
3218 * Used by the rtlogFlush() function as well as RTLogCreateExV.
3220 * @param pLogger The logger instance to update. NULL is not allowed!
3221 * @param uTimeSlot Current time slot (for tikme based rotation).
3222 * @param fFirst Flag whether this is the beginning of logging, i.e.
3223 * called from RTLogCreateExV. Prevents pfnPhase from
3225 * @param pErrInfo Where to return extended error information. Optional.
3227 static void rtlogRotate(PRTLOGGER pLogger
, uint32_t uTimeSlot
, bool fFirst
, PRTERRINFO pErrInfo
)
3229 /* Suppress rotating empty log files simply because the time elapsed. */
3230 if (RT_UNLIKELY(!pLogger
->pInt
->cbHistoryFileWritten
))
3231 pLogger
->pInt
->uHistoryTimeSlotStart
= uTimeSlot
;
3233 /* Check rotation condition: file still small enough and not too old? */
3234 if (RT_LIKELY( pLogger
->pInt
->cbHistoryFileWritten
< pLogger
->pInt
->cbHistoryFileMax
3235 && uTimeSlot
== pLogger
->pInt
->uHistoryTimeSlotStart
))
3239 * Save "disabled" log flag and make sure logging is disabled.
3240 * The logging in the functions called during log file history
3241 * rotation would cause severe trouble otherwise.
3243 uint32_t const fSavedFlags
= pLogger
->fFlags
;
3244 pLogger
->fFlags
|= RTLOGFLAGS_DISABLED
;
3247 * Disable log rotation temporarily, otherwise with extreme settings and
3248 * chatty phase logging we could run into endless rotation.
3250 uint32_t const cSavedHistory
= pLogger
->pInt
->cHistory
;
3251 pLogger
->pInt
->cHistory
= 0;
3254 * Close the old log file.
3256 if (pLogger
->pInt
->hFile
!= NIL_RTFILE
)
3258 /* Use the callback to generate some final log contents, but only if
3259 * this is a rotation with a fully set up logger. Leave the other case
3260 * to the RTLogCreateExV function. */
3261 if (pLogger
->pInt
->pfnPhase
&& !fFirst
)
3263 uint32_t fODestFlags
= pLogger
->fDestFlags
;
3264 pLogger
->fDestFlags
&= RTLOGDEST_FILE
;
3265 pLogger
->pInt
->pfnPhase(pLogger
, RTLOGPHASE_PREROTATE
, rtlogPhaseMsgLocked
);
3266 pLogger
->fDestFlags
= fODestFlags
;
3268 RTFileClose(pLogger
->pInt
->hFile
);
3269 pLogger
->pInt
->hFile
= NIL_RTFILE
;
3275 * Rotate the log files.
3277 for (uint32_t i
= cSavedHistory
- 1; i
+ 1 > 0; i
--)
3279 char szOldName
[sizeof(pLogger
->pInt
->szFilename
) + 32];
3281 RTStrPrintf(szOldName
, sizeof(szOldName
), "%s.%u", pLogger
->pInt
->szFilename
, i
);
3283 RTStrCopy(szOldName
, sizeof(szOldName
), pLogger
->pInt
->szFilename
);
3285 char szNewName
[sizeof(pLogger
->pInt
->szFilename
) + 32];
3286 RTStrPrintf(szNewName
, sizeof(szNewName
), "%s.%u", pLogger
->pInt
->szFilename
, i
+ 1);
3288 unsigned cBackoff
= 0;
3289 int rc
= RTFileRename(szOldName
, szNewName
, RTFILEMOVE_FLAGS_REPLACE
);
3290 while ( rc
== VERR_SHARING_VIOLATION
3291 && cBackoff
< RT_ELEMENTS(g_acMsLogBackoff
))
3293 RTThreadSleep(g_acMsLogBackoff
[cBackoff
++]);
3294 rc
= RTFileRename(szOldName
, szNewName
, RTFILEMOVE_FLAGS_REPLACE
);
3297 if (rc
== VERR_FILE_NOT_FOUND
)
3298 RTFileDelete(szNewName
);
3302 * Delete excess log files.
3304 for (uint32_t i
= cSavedHistory
+ 1; ; i
++)
3306 char szExcessName
[sizeof(pLogger
->pInt
->szFilename
) + 32];
3307 RTStrPrintf(szExcessName
, sizeof(szExcessName
), "%s.%u", pLogger
->pInt
->szFilename
, i
);
3308 int rc
= RTFileDelete(szExcessName
);
3315 * Update logger state and create new log file.
3317 pLogger
->pInt
->cbHistoryFileWritten
= 0;
3318 pLogger
->pInt
->uHistoryTimeSlotStart
= uTimeSlot
;
3319 rtlogFileOpen(pLogger
, pErrInfo
);
3322 * Use the callback to generate some initial log contents, but only if this
3323 * is a rotation with a fully set up logger. Leave the other case to the
3324 * RTLogCreateExV function.
3326 if (pLogger
->pInt
->pfnPhase
&& !fFirst
)
3328 uint32_t const fSavedDestFlags
= pLogger
->fDestFlags
;
3329 pLogger
->fDestFlags
&= RTLOGDEST_FILE
;
3330 pLogger
->pInt
->pfnPhase(pLogger
, RTLOGPHASE_POSTROTATE
, rtlogPhaseMsgLocked
);
3331 pLogger
->fDestFlags
= fSavedDestFlags
;
3334 /* Restore saved values. */
3335 pLogger
->pInt
->cHistory
= cSavedHistory
;
3336 pLogger
->fFlags
= fSavedFlags
;
3341 * Worker for RTLogCreateExV and RTLogClearFileDelayFlag.
3343 * This will later be used to reopen the file by RTLogDestinations.
3345 * @returns IPRT status code.
3346 * @param pLogger The logger.
3347 * @param pErrInfo Where to return extended error information.
3350 static int rtR3LogOpenFileDestination(PRTLOGGER pLogger
, PRTERRINFO pErrInfo
)
3353 if (pLogger
->fFlags
& RTLOGFLAGS_APPEND
)
3355 rc
= rtlogFileOpen(pLogger
, pErrInfo
);
3357 /* Rotate in case of appending to a too big log file,
3358 otherwise this simply doesn't do anything. */
3359 rtlogRotate(pLogger
, 0, true /* fFirst */, pErrInfo
);
3363 /* Force rotation if it is configured. */
3364 pLogger
->pInt
->cbHistoryFileWritten
= UINT64_MAX
;
3365 rtlogRotate(pLogger
, 0, true /* fFirst */, pErrInfo
);
3367 /* If the file is not open then rotation is not set up. */
3368 if (pLogger
->pInt
->hFile
== NIL_RTFILE
)
3370 pLogger
->pInt
->cbHistoryFileWritten
= 0;
3371 rc
= rtlogFileOpen(pLogger
, pErrInfo
);
3379 #endif /* IN_RING3 */
3383 * Writes the buffer to the given log device without checking for buffered
3386 * Used by the RTLogFlush() function.
3388 * @param pLogger The logger instance to write to. NULL is not allowed!
3389 * @param fNeedSpace Set if the caller assumes space will be made available.
3391 static void rtlogFlush(PRTLOGGER pLogger
, bool fNeedSpace
)
3393 uint32_t const cchScratch
= pLogger
->offScratch
;
3394 if (cchScratch
== 0)
3395 return; /* nothing to flush. */
3400 * If the ring buffer is active, the other destinations are only written
3401 * to when the ring buffer is flushed by RTLogFlush().
3403 if ( (pLogger
->fDestFlags
& RTLOGDEST_RINGBUF
)
3405 && pLogger
->pInt
->pszRingBuf
/* paraoia */)
3407 rtLogRingBufWrite(pLogger
->pInt
, pLogger
->achScratch
, pLogger
->offScratch
);
3408 pLogger
->offScratch
= 0; /* empty the buffer. */
3411 * In file delay mode, we ignore flush requests except when we're full
3412 * and the caller really needs some scratch space to get work done.
3416 if (!(pLogger
->fDestFlags
& RTLOGDEST_F_DELAY_FILE
))
3420 /* Make sure the string is terminated. On Windows, RTLogWriteDebugger
3421 will get upset if it isn't. */
3422 if (RT_LIKELY(cchScratch
< sizeof(pLogger
->achScratch
)))
3423 pLogger
->achScratch
[cchScratch
] = '\0';
3428 if (pLogger
->fDestFlags
& RTLOGDEST_USER
)
3429 RTLogWriteUser(pLogger
->achScratch
, cchScratch
);
3431 if (pLogger
->fDestFlags
& RTLOGDEST_DEBUGGER
)
3432 RTLogWriteDebugger(pLogger
->achScratch
, cchScratch
);
3435 if ((pLogger
->fDestFlags
& (RTLOGDEST_FILE
| RTLOGDEST_RINGBUF
)) == RTLOGDEST_FILE
)
3437 if (pLogger
->pInt
->hFile
!= NIL_RTFILE
)
3439 RTFileWrite(pLogger
->pInt
->hFile
, pLogger
->achScratch
, cchScratch
, NULL
);
3440 if (pLogger
->fFlags
& RTLOGFLAGS_FLUSH
)
3441 RTFileFlush(pLogger
->pInt
->hFile
);
3443 if (pLogger
->pInt
->cHistory
)
3444 pLogger
->pInt
->cbHistoryFileWritten
+= cchScratch
;
3448 if (pLogger
->fDestFlags
& RTLOGDEST_STDOUT
)
3449 RTLogWriteStdOut(pLogger
->achScratch
, cchScratch
);
3451 if (pLogger
->fDestFlags
& RTLOGDEST_STDERR
)
3452 RTLogWriteStdErr(pLogger
->achScratch
, cchScratch
);
3454 # if (defined(IN_RING0) || defined(IN_RC)) && !defined(LOG_NO_COM)
3455 if (pLogger
->fDestFlags
& RTLOGDEST_COM
)
3456 RTLogWriteCom(pLogger
->achScratch
, cchScratch
);
3461 if (pLogger
->pfnFlush
)
3462 pLogger
->pfnFlush(pLogger
);
3464 if (pLogger
->pInt
->pfnFlush
)
3465 pLogger
->pInt
->pfnFlush(pLogger
);
3468 /* empty the buffer. */
3469 pLogger
->offScratch
= 0;
3473 * Rotate the log file if configured. Must be done after everything is
3474 * flushed, since this will also use logging/flushing to write the header
3475 * and footer messages.
3477 if ( (pLogger
->fDestFlags
& RTLOGDEST_FILE
)
3478 && pLogger
->pInt
->cHistory
)
3479 rtlogRotate(pLogger
, RTTimeProgramSecTS() / pLogger
->pInt
->cSecsHistoryTimeSlot
, false /*fFirst*/, NULL
/*pErrInfo*/);
3486 * Delay file open but the caller really need some space. So, give him half a
3487 * buffer and insert a message indicating that we've dropped output.
3489 uint32_t offHalf
= sizeof(pLogger
->achScratch
) / 2;
3490 if (cchScratch
> offHalf
)
3492 if (pLogger
->fFlags
& RTLOGFLAGS_USECRLF
)
3493 pLogger
->achScratch
[offHalf
++] = '\r';
3494 static const char s_szDropMsg
[] = "\n[DROP DROP DROP]";
3495 memcpy(&pLogger
->achScratch
[offHalf
], RT_STR_TUPLE(s_szDropMsg
));
3496 offHalf
+= sizeof(s_szDropMsg
) - 1;
3497 if (pLogger
->fFlags
& RTLOGFLAGS_USECRLF
)
3498 pLogger
->achScratch
[offHalf
++] = '\r';
3499 pLogger
->achScratch
[offHalf
++] = '\n';
3501 pLogger
->offScratch
= offHalf
;
3509 * Callback for RTLogFormatV which writes to the com port.
3510 * See PFNLOGOUTPUT() for details.
3512 static DECLCALLBACK(size_t) rtLogOutput(void *pv
, const char *pachChars
, size_t cbChars
)
3514 PRTLOGGER pLogger
= (PRTLOGGER
)pv
;
3520 #if defined(DEBUG) && defined(IN_RING3)
3522 if (pLogger
->offScratch
>= sizeof(pLogger
->achScratch
))
3524 fprintf(stderr
, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
3525 pLogger
->offScratch
, (unsigned)sizeof(pLogger
->achScratch
));
3526 AssertBreakpoint(); AssertBreakpoint();
3531 size_t cb
= sizeof(pLogger
->achScratch
) - pLogger
->offScratch
- 1;
3536 memcpy(&pLogger
->achScratch
[pLogger
->offScratch
], pachChars
, cb
);
3539 pLogger
->offScratch
+= (uint32_t)cb
;
3550 rtlogFlush(pLogger
, true /*fNeedSpace*/);
3553 /* won't ever get here! */
3559 * There's always space for a terminator, and it's not counted.
3561 pLogger
->achScratch
[pLogger
->offScratch
] = '\0';
3568 * stpncpy implementation for use in rtLogOutputPrefixed w/ padding.
3570 * @returns Pointer to the destination buffer byte following the copied string.
3571 * @param pszDst The destination buffer.
3572 * @param pszSrc The source string.
3573 * @param cchSrcMax The maximum number of characters to copy from
3575 * @param cchMinWidth The minimum field with, padd with spaces to
3578 DECLINLINE(char *) rtLogStPNCpyPad(char *pszDst
, const char *pszSrc
, size_t cchSrcMax
, size_t cchMinWidth
)
3583 cchSrc
= strlen(pszSrc
);
3584 if (cchSrc
> cchSrcMax
)
3587 memcpy(pszDst
, pszSrc
, cchSrc
);
3592 while (cchSrc
++ < cchMinWidth
);
3600 * Callback for RTLogFormatV which writes to the logger instance.
3601 * This version supports prefixes.
3603 * See PFNLOGOUTPUT() for details.
3605 static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv
, const char *pachChars
, size_t cbChars
)
3607 PRTLOGOUTPUTPREFIXEDARGS pArgs
= (PRTLOGOUTPUTPREFIXEDARGS
)pv
;
3608 PRTLOGGER pLogger
= pArgs
->pLogger
;
3614 uint32_t offScratch
= pLogger
->offScratch
;
3615 size_t cb
= sizeof(pLogger
->achScratch
) - offScratch
- 1;
3616 const char *pszNewLine
;
3619 bool *pfPendingPrefix
= &pLogger
->fPendingPrefix
;
3621 bool *pfPendingPrefix
= &pLogger
->pInt
->fPendingPrefix
;
3627 if (*pfPendingPrefix
)
3629 *pfPendingPrefix
= false;
3631 #if defined(DEBUG) && defined(IN_RING3)
3633 if (offScratch
>= sizeof(pLogger
->achScratch
))
3635 fprintf(stderr
, "offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
3636 offScratch
, (unsigned)sizeof(pLogger
->achScratch
));
3637 AssertBreakpoint(); AssertBreakpoint();
3642 * Flush the buffer if there isn't enough room for the maximum prefix config.
3643 * Max is 256, add a couple of extra bytes. See CCH_PREFIX check way below.
3647 rtlogFlush(pLogger
, true /*fNeedSpace*/);
3648 offScratch
= pLogger
->offScratch
;
3649 cb
= sizeof(pLogger
->achScratch
) - offScratch
- 1;
3653 * Write the prefixes.
3654 * psz is pointing to the current position.
3656 psz
= &pLogger
->achScratch
[offScratch
];
3657 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_TS
)
3659 uint64_t u64
= RTTimeNanoTS();
3661 unsigned int fFlags
= RTSTR_F_ZEROPAD
;
3662 if (pLogger
->fFlags
& RTLOGFLAGS_DECIMAL_TS
)
3667 if (pLogger
->fFlags
& RTLOGFLAGS_REL_TS
)
3669 static volatile uint64_t s_u64LastTs
;
3670 uint64_t u64DiffTs
= u64
- s_u64LastTs
;
3672 /* We could have been preempted just before reading of s_u64LastTs by
3673 * another thread which wrote s_u64LastTs. In that case the difference
3674 * is negative which we simply ignore. */
3675 u64
= (int64_t)u64DiffTs
< 0 ? 0 : u64DiffTs
;
3677 /* 1E15 nanoseconds = 11 days */
3678 psz
+= RTStrFormatNumber(psz
, u64
, iBase
, 16, 0, fFlags
);
3681 #define CCH_PREFIX_01 0 + 17
3683 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_TSC
)
3685 #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
3686 uint64_t u64
= ASMReadTSC();
3688 uint64_t u64
= RTTimeNanoTS();
3691 unsigned int fFlags
= RTSTR_F_ZEROPAD
;
3692 if (pLogger
->fFlags
& RTLOGFLAGS_DECIMAL_TS
)
3697 if (pLogger
->fFlags
& RTLOGFLAGS_REL_TS
)
3699 static volatile uint64_t s_u64LastTsc
;
3700 int64_t i64DiffTsc
= u64
- s_u64LastTsc
;
3702 /* We could have been preempted just before reading of s_u64LastTsc by
3703 * another thread which wrote s_u64LastTsc. In that case the difference
3704 * is negative which we simply ignore. */
3705 u64
= i64DiffTsc
< 0 ? 0 : i64DiffTsc
;
3707 /* 1E15 ticks at 4GHz = 69 hours */
3708 psz
+= RTStrFormatNumber(psz
, u64
, iBase
, 16, 0, fFlags
);
3711 #define CCH_PREFIX_02 CCH_PREFIX_01 + 17
3713 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_MS_PROG
)
3715 #if defined(IN_RING3) || defined(IN_RC)
3716 uint64_t u64
= RTTimeProgramMilliTS();
3720 /* 1E8 milliseconds = 27 hours */
3721 psz
+= RTStrFormatNumber(psz
, u64
, 10, 9, 0, RTSTR_F_ZEROPAD
);
3724 #define CCH_PREFIX_03 CCH_PREFIX_02 + 21
3726 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_TIME
)
3728 #if defined(IN_RING3) || defined(IN_RING0)
3729 RTTIMESPEC TimeSpec
;
3731 RTTimeExplode(&Time
, RTTimeNow(&TimeSpec
));
3732 psz
+= RTStrFormatNumber(psz
, Time
.u8Hour
, 10, 2, 0, RTSTR_F_ZEROPAD
);
3734 psz
+= RTStrFormatNumber(psz
, Time
.u8Minute
, 10, 2, 0, RTSTR_F_ZEROPAD
);
3736 psz
+= RTStrFormatNumber(psz
, Time
.u8Second
, 10, 2, 0, RTSTR_F_ZEROPAD
);
3738 psz
+= RTStrFormatNumber(psz
, Time
.u32Nanosecond
/ 1000, 10, 6, 0, RTSTR_F_ZEROPAD
);
3741 memset(psz
, ' ', 16);
3745 #define CCH_PREFIX_04 CCH_PREFIX_03 + (3+1+3+1+3+1+7+1)
3747 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_TIME_PROG
)
3750 #if defined(IN_RING3) || defined(IN_RC)
3751 uint64_t u64
= RTTimeProgramMicroTS();
3752 psz
+= RTStrFormatNumber(psz
, (uint32_t)(u64
/ RT_US_1HOUR
), 10, 2, 0, RTSTR_F_ZEROPAD
);
3754 uint32_t u32
= (uint32_t)(u64
% RT_US_1HOUR
);
3755 psz
+= RTStrFormatNumber(psz
, u32
/ RT_US_1MIN
, 10, 2, 0, RTSTR_F_ZEROPAD
);
3759 psz
+= RTStrFormatNumber(psz
, u32
/ RT_US_1SEC
, 10, 2, 0, RTSTR_F_ZEROPAD
);
3761 psz
+= RTStrFormatNumber(psz
, u32
% RT_US_1SEC
, 10, 6, 0, RTSTR_F_ZEROPAD
);
3764 memset(psz
, ' ', 16);
3768 #define CCH_PREFIX_05 CCH_PREFIX_04 + (9+1+2+1+2+1+6+1)
3771 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_DATETIME
)
3775 RTTimeSpecToString(RTTimeNow(&Time
), szDate
, sizeof(szDate
));
3776 size_t cch
= strlen(szDate
);
3777 memcpy(psz
, szDate
, cch
);
3781 # define CCH_PREFIX_06 CCH_PREFIX_05 + 32
3783 # define CCH_PREFIX_06 CCH_PREFIX_05 + 0
3786 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_PID
)
3789 RTPROCESS Process
= RTProcSelf();
3791 RTPROCESS Process
= NIL_RTPROCESS
;
3793 psz
+= RTStrFormatNumber(psz
, Process
, 16, sizeof(RTPROCESS
) * 2, 0, RTSTR_F_ZEROPAD
);
3796 #define CCH_PREFIX_07 CCH_PREFIX_06 + 9
3798 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_TID
)
3801 RTNATIVETHREAD Thread
= RTThreadNativeSelf();
3803 RTNATIVETHREAD Thread
= NIL_RTNATIVETHREAD
;
3805 psz
+= RTStrFormatNumber(psz
, Thread
, 16, sizeof(RTNATIVETHREAD
) * 2, 0, RTSTR_F_ZEROPAD
);
3808 #define CCH_PREFIX_08 CCH_PREFIX_07 + 17
3810 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_THREAD
)
3813 const char *pszName
= RTThreadSelfName();
3815 const char *pszName
= "EMT-RC";
3817 const char *pszName
= "R0";
3819 psz
= rtLogStPNCpyPad(psz
, pszName
, 16, 8);
3821 #define CCH_PREFIX_09 CCH_PREFIX_08 + 17
3823 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_CPUID
)
3825 #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
3826 const uint8_t idCpu
= ASMGetApicId();
3828 const RTCPUID idCpu
= RTMpCpuId();
3830 psz
+= RTStrFormatNumber(psz
, idCpu
, 16, sizeof(idCpu
) * 2, 0, RTSTR_F_ZEROPAD
);
3833 #define CCH_PREFIX_10 CCH_PREFIX_09 + 17
3836 if ( (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_CUSTOM
)
3837 && pLogger
->pInt
->pfnPrefix
)
3839 psz
+= pLogger
->pInt
->pfnPrefix(pLogger
, psz
, 31, pLogger
->pInt
->pvPrefixUserArg
);
3840 *psz
++ = ' '; /* +32 */
3843 #define CCH_PREFIX_11 CCH_PREFIX_10 + 32
3845 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_LOCK_COUNTS
)
3847 #ifdef IN_RING3 /** @todo implement these counters in ring-0 too? */
3848 RTTHREAD Thread
= RTThreadSelf();
3849 if (Thread
!= NIL_RTTHREAD
)
3851 uint32_t cReadLocks
= RTLockValidatorReadLockGetCount(Thread
);
3852 uint32_t cWriteLocks
= RTLockValidatorWriteLockGetCount(Thread
) - g_cLoggerLockCount
;
3853 cReadLocks
= RT_MIN(0xfff, cReadLocks
);
3854 cWriteLocks
= RT_MIN(0xfff, cWriteLocks
);
3855 psz
+= RTStrFormatNumber(psz
, cReadLocks
, 16, 1, 0, RTSTR_F_ZEROPAD
);
3857 psz
+= RTStrFormatNumber(psz
, cWriteLocks
, 16, 1, 0, RTSTR_F_ZEROPAD
);
3868 #define CCH_PREFIX_12 CCH_PREFIX_11 + 8
3870 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_FLAG_NO
)
3872 psz
+= RTStrFormatNumber(psz
, pArgs
->fFlags
, 16, 8, 0, RTSTR_F_ZEROPAD
);
3875 #define CCH_PREFIX_13 CCH_PREFIX_12 + 9
3877 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_FLAG
)
3880 const char *pszGroup
= pArgs
->iGroup
!= ~0U ? pLogger
->pInt
->papszGroups
[pArgs
->iGroup
] : NULL
;
3882 const char *pszGroup
= NULL
;
3884 psz
= rtLogStPNCpyPad(psz
, pszGroup
, 16, 8);
3886 #define CCH_PREFIX_14 CCH_PREFIX_13 + 17
3888 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_GROUP_NO
)
3890 if (pArgs
->iGroup
!= ~0U)
3892 psz
+= RTStrFormatNumber(psz
, pArgs
->iGroup
, 16, 3, 0, RTSTR_F_ZEROPAD
);
3897 memcpy(psz
, "-1 ", sizeof("-1 ") - 1);
3898 psz
+= sizeof("-1 ") - 1;
3901 #define CCH_PREFIX_15 CCH_PREFIX_14 + 9
3903 if (pLogger
->fFlags
& RTLOGFLAGS_PREFIX_GROUP
)
3905 const unsigned fGrp
= pLogger
->afGroups
[pArgs
->iGroup
!= ~0U ? pArgs
->iGroup
: 0];
3906 const char *pszGroup
;
3908 switch (pArgs
->fFlags
& fGrp
)
3910 case 0: pszGroup
= "--------"; cch
= sizeof("--------") - 1; break;
3911 case RTLOGGRPFLAGS_ENABLED
: pszGroup
= "enabled" ; cch
= sizeof("enabled" ) - 1; break;
3912 case RTLOGGRPFLAGS_LEVEL_1
: pszGroup
= "level 1" ; cch
= sizeof("level 1" ) - 1; break;
3913 case RTLOGGRPFLAGS_LEVEL_2
: pszGroup
= "level 2" ; cch
= sizeof("level 2" ) - 1; break;
3914 case RTLOGGRPFLAGS_LEVEL_3
: pszGroup
= "level 3" ; cch
= sizeof("level 3" ) - 1; break;
3915 case RTLOGGRPFLAGS_LEVEL_4
: pszGroup
= "level 4" ; cch
= sizeof("level 4" ) - 1; break;
3916 case RTLOGGRPFLAGS_LEVEL_5
: pszGroup
= "level 5" ; cch
= sizeof("level 5" ) - 1; break;
3917 case RTLOGGRPFLAGS_LEVEL_6
: pszGroup
= "level 6" ; cch
= sizeof("level 6" ) - 1; break;
3918 case RTLOGGRPFLAGS_LEVEL_7
: pszGroup
= "level 7" ; cch
= sizeof("level 7" ) - 1; break;
3919 case RTLOGGRPFLAGS_LEVEL_8
: pszGroup
= "level 8" ; cch
= sizeof("level 8" ) - 1; break;
3920 case RTLOGGRPFLAGS_LEVEL_9
: pszGroup
= "level 9" ; cch
= sizeof("level 9" ) - 1; break;
3921 case RTLOGGRPFLAGS_LEVEL_10
: pszGroup
= "level 10"; cch
= sizeof("level 10") - 1; break;
3922 case RTLOGGRPFLAGS_LEVEL_11
: pszGroup
= "level 11"; cch
= sizeof("level 11") - 1; break;
3923 case RTLOGGRPFLAGS_LEVEL_12
: pszGroup
= "level 12"; cch
= sizeof("level 12") - 1; break;
3924 case RTLOGGRPFLAGS_FLOW
: pszGroup
= "flow" ; cch
= sizeof("flow" ) - 1; break;
3925 case RTLOGGRPFLAGS_WARN
: pszGroup
= "warn" ; cch
= sizeof("warn" ) - 1; break;
3926 default: pszGroup
= "????????"; cch
= sizeof("????????") - 1; break;
3928 psz
= rtLogStPNCpyPad(psz
, pszGroup
, 16, 8);
3930 #define CCH_PREFIX_16 CCH_PREFIX_15 + 17
3932 #define CCH_PREFIX ( CCH_PREFIX_16 )
3933 { AssertCompile(CCH_PREFIX
< 256); }
3936 * Done, figure what we've used and advance the buffer and free size.
3938 cb
= psz
- &pLogger
->achScratch
[offScratch
];
3939 AssertMsg(cb
<= 223, ("%#zx (%zd) - fFlags=%#x\n", cb
, cb
, pLogger
->fFlags
));
3940 pLogger
->offScratch
= offScratch
+= (uint32_t)cb
;
3941 cb
= sizeof(pLogger
->achScratch
) - offScratch
- 1;
3945 rtlogFlush(pLogger
, true /*fNeedSpace*/);
3946 offScratch
= pLogger
->offScratch
;
3947 cb
= sizeof(pLogger
->achScratch
) - offScratch
- 1;
3950 #if defined(DEBUG) && defined(IN_RING3)
3952 if (offScratch
>= sizeof(pLogger
->achScratch
))
3954 fprintf(stderr
, "offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
3955 offScratch
, (unsigned)sizeof(pLogger
->achScratch
));
3956 AssertBreakpoint(); AssertBreakpoint();
3965 pszNewLine
= (const char *)memchr(pachChars
, '\n', cb
);
3968 if (pLogger
->fFlags
& RTLOGFLAGS_USECRLF
)
3969 cb
= pszNewLine
- pachChars
;
3972 cb
= pszNewLine
- pachChars
+ 1;
3973 *pfPendingPrefix
= true;
3978 memcpy(&pLogger
->achScratch
[offScratch
], pachChars
, cb
);
3981 pLogger
->offScratch
= offScratch
+= (uint32_t)cb
;
3986 && (pLogger
->fFlags
& RTLOGFLAGS_USECRLF
)
3987 && offScratch
+ 2 < sizeof(pLogger
->achScratch
))
3989 memcpy(&pLogger
->achScratch
[offScratch
], "\r\n", 2);
3990 pLogger
->offScratch
= offScratch
+= 2;
3994 *pfPendingPrefix
= true;
4003 /* won't ever get here! */
4009 * There's always space for a terminator, and it's not counted.
4011 pLogger
->achScratch
[pLogger
->offScratch
] = '\0';
4018 * Write to a logger instance (worker function).
4020 * This function will check whether the instance, group and flags makes up a
4021 * logging kind which is currently enabled before writing anything to the log.
4023 * @param pLogger Pointer to logger instance. Must be non-NULL.
4024 * @param fFlags The logging flags.
4025 * @param iGroup The group.
4026 * The value ~0U is reserved for compatibility with RTLogLogger[V] and is
4027 * only for internal usage!
4028 * @param pszFormat Format string.
4029 * @param args Format arguments.
4031 static void rtlogLoggerExVLocked(PRTLOGGER pLogger
, unsigned fFlags
, unsigned iGroup
, const char *pszFormat
, va_list args
)
4034 * Format the message and perhaps flush it.
4036 if (pLogger
->fFlags
& (RTLOGFLAGS_PREFIX_MASK
| RTLOGFLAGS_USECRLF
))
4038 RTLOGOUTPUTPREFIXEDARGS OutputArgs
;
4039 OutputArgs
.pLogger
= pLogger
;
4040 OutputArgs
.iGroup
= iGroup
;
4041 OutputArgs
.fFlags
= fFlags
;
4042 RTLogFormatV(rtLogOutputPrefixed
, &OutputArgs
, pszFormat
, args
);
4045 RTLogFormatV(rtLogOutput
, pLogger
, pszFormat
, args
);
4046 if ( !(pLogger
->fFlags
& RTLOGFLAGS_BUFFERED
)
4047 && pLogger
->offScratch
)
4048 rtlogFlush(pLogger
, false /*fNeedSpace*/);
4054 * For calling rtlogLoggerExVLocked.
4056 * @param pLogger The logger.
4057 * @param fFlags The logging flags.
4058 * @param iGroup The group.
4059 * The value ~0U is reserved for compatibility with RTLogLogger[V] and is
4060 * only for internal usage!
4061 * @param pszFormat Format string.
4062 * @param ... Format arguments.
4064 static void rtlogLoggerExFLocked(PRTLOGGER pLogger
, unsigned fFlags
, unsigned iGroup
, const char *pszFormat
, ...)
4067 va_start(va
, pszFormat
);
4068 rtlogLoggerExVLocked(pLogger
, fFlags
, iGroup
, pszFormat
, va
);