]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - ubuntu/vbox/vboxguest/common/log/log.c
fb44c9d3b9ea9ba73aea1128f64b940deeaec587
[mirror_ubuntu-bionic-kernel.git] / ubuntu / vbox / vboxguest / common / log / log.c
1 /* $Id: log.cpp $ */
2 /** @file
3 * Runtime VBox - Logger.
4 */
5
6 /*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28 /*********************************************************************************************************************************
29 * Header Files *
30 *********************************************************************************************************************************/
31 #include <iprt/log.h>
32 #include "internal/iprt.h"
33
34 #ifndef IN_RC
35 # include <iprt/alloc.h>
36 # include <iprt/process.h>
37 # include <iprt/semaphore.h>
38 # include <iprt/thread.h>
39 # include <iprt/mp.h>
40 #endif
41 #ifdef IN_RING3
42 # include <iprt/env.h>
43 # include <iprt/file.h>
44 # include <iprt/lockvalidator.h>
45 # include <iprt/path.h>
46 #endif
47 #include <iprt/time.h>
48 #include <iprt/asm.h>
49 #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
50 # include <iprt/asm-amd64-x86.h>
51 #endif
52 #include <iprt/assert.h>
53 #include <iprt/err.h>
54 #include <iprt/param.h>
55
56 #include <iprt/stdarg.h>
57 #include <iprt/string.h>
58 #include <iprt/ctype.h>
59 #ifdef IN_RING3
60 # include <iprt/alloca.h>
61 # include <stdio.h>
62 #endif
63
64
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. */
74 #ifdef IN_RING0
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
82 #endif
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);
90
91
92 /*********************************************************************************************************************************
93 * Structures and Typedefs *
94 *********************************************************************************************************************************/
95 /**
96 * Arguments passed to the output function.
97 */
98 typedef struct RTLOGOUTPUTPREFIXEDARGS
99 {
100 /** The logger instance. */
101 PRTLOGGER pLogger;
102 /** The flags. (used for prefixing.) */
103 unsigned fFlags;
104 /** The group. (used for prefixing.) */
105 unsigned iGroup;
106 } RTLOGOUTPUTPREFIXEDARGS, *PRTLOGOUTPUTPREFIXEDARGS;
107
108 #ifndef IN_RC
109
110 /**
111 * Internal logger data.
112 *
113 * @remarks Don't make casual changes to this structure.
114 */
115 typedef struct RTLOGGERINTERNAL
116 {
117 /** The structure revision (RTLOGGERINTERNAL_REV). */
118 uint32_t uRevision;
119 /** The size of the internal logger structure. */
120 uint32_t cbSelf;
121
122 /** Spinning mutex semaphore. Can be NIL. */
123 RTSEMSPINMUTEX hSpinMtx;
124 /** Pointer to the flush function. */
125 PFNRTLOGFLUSH pfnFlush;
126
127 /** Custom prefix callback. */
128 PFNRTLOGPREFIX pfnPrefix;
129 /** Prefix callback argument. */
130 void *pvPrefixUserArg;
131 /** This is set if a prefix is pending. */
132 bool fPendingPrefix;
133 /** Alignment padding. */
134 bool afPadding1[2];
135 /** Set if fully created. Used to avoid confusing in a few functions used to
136 * parse logger settings from environment variables. */
137 bool fCreated;
138
139 /** The max number of groups that there is room for in afGroups and papszGroups.
140 * Used by RTLogCopyGroupAndFlags(). */
141 uint32_t cMaxGroups;
142 /** Pointer to the group name array.
143 * (The data is readonly and provided by the user.) */
144 const char * const *papszGroups;
145
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;
151
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.
156 *
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.
161 *
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.
165 *
166 * @{ */
167 /** Ring buffer size (including both eye catchers). */
168 uint32_t cbRingBuf;
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). */
173 char *pszRingBuf;
174 /** Current ring buffer position (where to write the next char). */
175 char * volatile pchRingBufCur;
176 /** @} */
177
178 # ifdef IN_RING3 /* Note! Must be at the end! */
179 /** @name File logging bits for the logger.
180 * @{ */
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;
185
186 /** Handle to log file (if open). */
187 RTFILE hFile;
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. */
198 uint32_t cHistory;
199 /** Pointer to filename. */
200 char szFilename[RTPATH_MAX];
201 /** @} */
202 # endif /* IN_RING3 */
203 } RTLOGGERINTERNAL;
204
205 /** The revision of the internal logger structure. */
206 # define RTLOGGERINTERNAL_REV UINT32_C(10)
207
208 # ifdef IN_RING3
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));
213 # endif
214 AssertCompileMemberAlignment(RTLOGGERINTERNAL, cbRingBufUnflushed, sizeof(uint64_t));
215
216 #endif /* !IN_RC */
217
218
219 /*********************************************************************************************************************************
220 * Internal Functions *
221 *********************************************************************************************************************************/
222 #ifndef IN_RC
223 static unsigned rtlogGroupFlags(const char *psz);
224 #endif
225 #ifdef IN_RING0
226 static void rtR0LogLoggerExFallback(uint32_t fDestFlags, uint32_t fFlags, PRTLOGGERINTERNAL pInt,
227 const char *pszFormat, va_list va);
228 #endif
229 #ifdef IN_RING3
230 static int rtR3LogOpenFileDestination(PRTLOGGER pLogger, PRTERRINFO pErrInfo);
231 #endif
232 #ifndef IN_RC
233 static void rtLogRingBufFlush(PRTLOGGER pLogger);
234 #endif
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);
239 #ifndef IN_RC
240 static void rtlogLoggerExFLocked(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...);
241 #endif
242
243
244 /*********************************************************************************************************************************
245 * Global Variables *
246 *********************************************************************************************************************************/
247 #ifdef IN_RC
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'') */
252 # ifdef RT_OS_DARWIN
253 extern "C" DECLIMPORT(RTLOGGERRC) g_Logger;
254 # else
255 extern "C" DECLWEAK(DECLIMPORT(RTLOGGERRC)) g_Logger;
256 # endif
257 #else /* !IN_RC */
258 /** Default logger instance. */
259 static PRTLOGGER g_pLogger;
260 #endif /* !IN_RC */
261 #ifdef IN_RING3
262 /** The RTThreadGetWriteLockCount() change caused by the logger mutex semaphore. */
263 static uint32_t volatile g_cLoggerLockCount;
264 #endif
265
266 #ifdef IN_RING0
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
273 {
274 /** The thread. */
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] =
281 {
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}
290 };
291 #endif /* IN_RING0 */
292
293 /**
294 * Logger flags instructions.
295 */
296 static struct
297 {
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[] =
303 {
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. */
335 };
336
337 /**
338 * Logger destination instructions.
339 */
340 static struct
341 {
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[] =
346 {
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 },
359 };
360
361 #ifdef IN_RING3
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.
365 * 10 seconds total.
366 */
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 };
369 #endif
370
371
372 /**
373 * Locks the logger instance.
374 *
375 * @returns See RTSemSpinMutexRequest().
376 * @param pLogger The logger instance.
377 */
378 DECLINLINE(int) rtlogLock(PRTLOGGER pLogger)
379 {
380 #ifndef IN_RC
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)
387 {
388 int rc = RTSemSpinMutexRequest(pInt->hSpinMtx);
389 if (RT_FAILURE(rc))
390 return rc;
391 }
392 #else
393 NOREF(pLogger);
394 #endif
395 return VINF_SUCCESS;
396 }
397
398
399 /**
400 * Unlocks the logger instance.
401 * @param pLogger The logger instance.
402 */
403 DECLINLINE(void) rtlogUnlock(PRTLOGGER pLogger)
404 {
405 #ifndef IN_RC
406 if (pLogger->pInt->hSpinMtx != NIL_RTSEMSPINMUTEX)
407 RTSemSpinMutexRelease(pLogger->pInt->hSpinMtx);
408 #else
409 NOREF(pLogger);
410 #endif
411 return;
412 }
413
414 #ifndef IN_RC
415 # ifdef IN_RING3
416
417 # ifdef SOME_UNUSED_FUNCTION
418 /**
419 * Logging to file, output callback.
420 *
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.
424 */
425 static DECLCALLBACK(size_t) rtlogPhaseWrite(void *pvArg, const char *pachChars, size_t cbChars)
426 {
427 PRTLOGGER pLogger = (PRTLOGGER)pvArg;
428 RTFileWrite(pLogger->pInt->hFile, pachChars, cbChars, NULL);
429 return cbChars;
430 }
431
432
433 /**
434 * Callback to format VBox formatting extentions.
435 * See @ref pg_rt_str_format for a reference on the format types.
436 *
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'.
448 */
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)
452 {
453 char ch = *(*ppszFormat)++;
454
455 AssertMsgFailed(("Invalid logger phase format type '%%%c%.10s'!\n", ch, *ppszFormat)); NOREF(ch);
456
457 return 0;
458 }
459
460 # endif /* SOME_UNUSED_FUNCTION */
461
462
463 /**
464 * Log phase callback function, assumes the lock is already held
465 *
466 * @param pLogger The logger instance.
467 * @param pszFormat Format string.
468 * @param ... Optional arguments as specified in the format string.
469 */
470 static DECLCALLBACK(void) rtlogPhaseMsgLocked(PRTLOGGER pLogger, const char *pszFormat, ...)
471 {
472 va_list args;
473 AssertPtrReturnVoid(pLogger);
474 AssertPtrReturnVoid(pLogger->pInt);
475 Assert(pLogger->pInt->hSpinMtx != NIL_RTSEMSPINMUTEX);
476
477 va_start(args, pszFormat);
478 rtlogLoggerExVLocked(pLogger, 0, ~0U, pszFormat, args);
479 va_end(args);
480 }
481
482
483 /**
484 * Log phase callback function, assumes the lock is not held.
485 *
486 * @param pLogger The logger instance.
487 * @param pszFormat Format string.
488 * @param ... Optional arguments as specified in the format string.
489 */
490 static DECLCALLBACK(void) rtlogPhaseMsgNormal(PRTLOGGER pLogger, const char *pszFormat, ...)
491 {
492 va_list args;
493 AssertPtrReturnVoid(pLogger);
494 AssertPtrReturnVoid(pLogger->pInt);
495 Assert(pLogger->pInt->hSpinMtx != NIL_RTSEMSPINMUTEX);
496
497 va_start(args, pszFormat);
498 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
499 va_end(args);
500 }
501
502 # endif /* IN_RING3 */
503
504 /**
505 * Adjusts the ring buffer.
506 *
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).
512 */
513 static int rtLogRingBufAdjust(PRTLOGGER pLogger, uint32_t cbNewSize, bool fForce)
514 {
515 /*
516 * If this is early logger init, don't do anything.
517 */
518 if (!pLogger->pInt->fCreated && !fForce)
519 return VINF_SUCCESS;
520
521 /*
522 * Lock the logger and make the necessary changes.
523 */
524 int rc = rtlogLock(pLogger);
525 if (RT_SUCCESS(rc))
526 {
527 if (cbNewSize == 0)
528 cbNewSize = RTLOG_RINGBUF_DEFAULT_SIZE;
529 if ( pLogger->pInt->cbRingBuf != cbNewSize
530 || !pLogger->pInt->pchRingBufCur)
531 {
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)
536 {
537 memmove(pLogger->pInt->pszRingBuf, &pLogger->pInt->pszRingBuf[offOld - cbNewSize], cbNewSize);
538 offOld = sizeof(RTLOG_RINGBUF_EYE_CATCHER);
539 }
540
541 void *pvNew = RTMemRealloc(pLogger->pInt->pchRingBufCur, cbNewSize);
542 if (pvNew)
543 {
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));
550 rc = VINF_SUCCESS;
551 }
552 else
553 rc = VERR_NO_MEMORY;
554 }
555 rtlogUnlock(pLogger);
556 }
557
558 return rc;
559 }
560
561
562 /**
563 * Writes text to the ring buffer.
564 *
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.
568 */
569 static void rtLogRingBufWrite(PRTLOGGERINTERNAL pInt, const char *pachText, size_t cchText)
570 {
571 /*
572 * Get the ring buffer data, adjusting it to only describe the writable
573 * part of the buffer.
574 */
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;
581 else
582 {
583 /* May happen in ring-0 where a thread or two went ahead without getting the lock. */
584 pchCur = pchStart;
585 cchLeft = cchBuf;
586 }
587 Assert(cchBuf < pInt->cbRingBuf);
588
589 if (cchText < cchLeft)
590 {
591 /*
592 * The text fits in the remaining space.
593 */
594 memcpy(pchCur, pachText, cchText);
595 pchCur[cchText] = '\0';
596 pInt->pchRingBufCur = &pchCur[cchText];
597 pInt->cbRingBufUnflushed += cchText;
598 }
599 else
600 {
601 /*
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.
605 */
606 /* Fill to the end of the buffer. */
607 memcpy(pchCur, pachText, cchLeft);
608 pachText += cchLeft;
609 cchText -= cchLeft;
610 pInt->cbRingBufUnflushed += cchLeft;
611 pInt->pchRingBufCur = pchStart;
612
613 /* Ring buffer overflows (the plainly inefficient bit). */
614 while (cchText >= cchBuf)
615 {
616 memcpy(pchStart, pachText, cchBuf);
617 pachText += cchBuf;
618 cchText -= cchBuf;
619 pInt->cbRingBufUnflushed += cchBuf;
620 }
621
622 /* The final bit, if any. */
623 if (cchText > 0)
624 {
625 memcpy(pchStart, pachText, cchText);
626 pInt->cbRingBufUnflushed += cchText;
627 }
628 pchStart[cchText] = '\0';
629 pInt->pchRingBufCur = &pchStart[cchText];
630 }
631 }
632
633
634 /**
635 * Flushes the ring buffer to all the other log destinations.
636 *
637 * @param pLogger The logger instance which ring buffer should be flushed.
638 */
639 static void rtLogRingBufFlush(PRTLOGGER pLogger)
640 {
641 const char *pszPreamble;
642 size_t cchPreamble;
643 const char *pszFirst;
644 size_t cchFirst;
645 const char *pszSecond;
646 size_t cchSecond;
647
648 /*
649 * Get the ring buffer data, adjusting it to only describe the writable
650 * part of the buffer.
651 */
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;
656 size_t cchAfter;
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. */
660 {
661 offCur = 0;
662 cchAfter = cchBuf;
663 }
664
665 pLogger->pInt->cbRingBufUnflushed = 0;
666
667 /*
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.)
671 */
672 if (cchUnflushed == 0)
673 return;
674 pszBuf[offCur] = '\0';
675 if (cchUnflushed >= cchBuf)
676 {
677 pszFirst = &pszBuf[offCur + 1];
678 cchFirst = cchAfter ? cchAfter - 1 : 0;
679 pszSecond = pszBuf;
680 cchSecond = offCur;
681 pszPreamble = "\n*FLUSH RING BUF*\n";
682 cchPreamble = sizeof("\n*FLUSH RING BUF*\n") - 1;
683 }
684 else if ((size_t)cchUnflushed <= offCur)
685 {
686 cchFirst = (size_t)cchUnflushed;
687 pszFirst = &pszBuf[offCur - cchFirst];
688 pszSecond = "";
689 cchSecond = 0;
690 pszPreamble = "";
691 cchPreamble = 0;
692 }
693 else
694 {
695 cchFirst = (size_t)cchUnflushed - offCur;
696 pszFirst = &pszBuf[cchBuf - cchFirst];
697 pszSecond = pszBuf;
698 cchSecond = offCur;
699 pszPreamble = "";
700 cchPreamble = 0;
701 }
702
703 /*
704 * Write the ring buffer to all other destiations.
705 */
706 if (pLogger->fDestFlags & RTLOGDEST_USER)
707 {
708 if (cchPreamble)
709 RTLogWriteUser(pszPreamble, cchPreamble);
710 if (cchFirst)
711 RTLogWriteUser(pszFirst, cchFirst);
712 if (cchSecond)
713 RTLogWriteUser(pszSecond, cchSecond);
714 }
715
716 if (pLogger->fDestFlags & RTLOGDEST_DEBUGGER)
717 {
718 if (cchPreamble)
719 RTLogWriteDebugger(pszPreamble, cchPreamble);
720 if (cchFirst)
721 RTLogWriteDebugger(pszFirst, cchFirst);
722 if (cchSecond)
723 RTLogWriteDebugger(pszSecond, cchSecond);
724 }
725
726 # ifdef IN_RING3
727 if (pLogger->fDestFlags & RTLOGDEST_FILE)
728 {
729 if (pLogger->pInt->hFile != NIL_RTFILE)
730 {
731 if (cchPreamble)
732 RTFileWrite(pLogger->pInt->hFile, pszPreamble, cchPreamble, NULL);
733 if (cchFirst)
734 RTFileWrite(pLogger->pInt->hFile, pszFirst, cchFirst, NULL);
735 if (cchSecond)
736 RTFileWrite(pLogger->pInt->hFile, pszSecond, cchSecond, NULL);
737 if (pLogger->fFlags & RTLOGFLAGS_FLUSH)
738 RTFileFlush(pLogger->pInt->hFile);
739 }
740 if (pLogger->pInt->cHistory)
741 pLogger->pInt->cbHistoryFileWritten += cchFirst + cchSecond;
742 }
743 # endif
744
745 if (pLogger->fDestFlags & RTLOGDEST_STDOUT)
746 {
747 if (cchPreamble)
748 RTLogWriteStdOut(pszPreamble, cchPreamble);
749 if (cchFirst)
750 RTLogWriteStdOut(pszFirst, cchFirst);
751 if (cchSecond)
752 RTLogWriteStdOut(pszSecond, cchSecond);
753 }
754
755 if (pLogger->fDestFlags & RTLOGDEST_STDERR)
756 {
757 if (cchPreamble)
758 RTLogWriteStdErr(pszPreamble, cchPreamble);
759 if (cchFirst)
760 RTLogWriteStdErr(pszFirst, cchFirst);
761 if (cchSecond)
762 RTLogWriteStdErr(pszSecond, cchSecond);
763 }
764
765 # if defined(IN_RING0) && !defined(LOG_NO_COM)
766 if (pLogger->fDestFlags & RTLOGDEST_COM)
767 {
768 if (cchPreamble)
769 RTLogWriteCom(pszPreamble, cchPreamble);
770 if (cchFirst)
771 RTLogWriteCom(pszFirst, cchFirst);
772 if (cchSecond)
773 RTLogWriteCom(pszSecond, cchSecond);
774 }
775 # endif
776 }
777
778
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)
784 {
785 int rc;
786 size_t offInternal;
787 size_t cbLogger;
788 PRTLOGGER pLogger;
789
790 /*
791 * Validate input.
792 */
793 if ( (cGroups && !papszGroups)
794 || !VALID_PTR(ppLogger) )
795 {
796 AssertMsgFailed(("Invalid parameters!\n"));
797 return VERR_INVALID_PARAMETER;
798 }
799 *ppLogger = NULL;
800
801 AssertMsgReturn(cHistory < _1M, ("%#x", cHistory), VERR_OUT_OF_RANGE);
802
803 /*
804 * Allocate a logger instance.
805 */
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);
812 if (pLogger)
813 {
814 # if defined(RT_ARCH_X86) && (!defined(LOG_USE_C99) || !defined(RT_WITHOUT_EXEC_ALLOC))
815 uint8_t *pu8Code;
816 # endif
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);
835 else
836 pLogger->pInt->pacEntriesPerGroup = NULL;
837 pLogger->pInt->cMaxEntriesPerGroup = UINT32_MAX;
838 # ifdef IN_RING3
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;
844 else
845 pLogger->pInt->cbHistoryFileMax = cbHistoryFileMax;
846 if (cSecsHistoryTimeSlot == 0)
847 pLogger->pInt->cSecsHistoryTimeSlot = UINT32_MAX;
848 else
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);
855
856 # if defined(RT_ARCH_X86) && (!defined(LOG_USE_C99) || !defined(RT_WITHOUT_EXEC_ALLOC))
857 /*
858 * Emit wrapper code.
859 */
860 pu8Code = (uint8_t *)RTMemExecAlloc(64);
861 if (pu8Code)
862 {
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] */
871 *pu8Code++ = 0x64;
872 *pu8Code++ = 0x24;
873 *pu8Code++ = 0x04;
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));
877 rc = VINF_SUCCESS;
878 }
879 else
880 {
881 rc = VERR_NO_MEMORY;
882 # ifdef RT_OS_LINUX
883 /* Most probably SELinux causing trouble since the larger RTMemAlloc succeeded. */
884 RTErrInfoSet(pErrInfo, rc, N_("mmap(PROT_WRITE | PROT_EXEC) failed -- SELinux?"));
885 # endif
886 }
887 if (RT_SUCCESS(rc))
888 # endif /* X86 wrapper code*/
889 {
890 # ifdef IN_RING3 /* files and env.vars. are only accessible when in R3 at the present time. */
891 /*
892 * Format the filename.
893 */
894 if (pszFilenameFmt)
895 {
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;
900 }
901
902 /*
903 * Parse the environment variables.
904 */
905 if (pszEnvVarBase)
906 {
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);
911
912 /*
913 * Destination.
914 */
915 strcpy(pszEnvVar + cchEnvVarBase, "_DEST");
916 const char *pszValue = RTEnvGet(pszEnvVar);
917 if (pszValue)
918 RTLogDestinations(pLogger, pszValue);
919
920 /*
921 * The flags.
922 */
923 strcpy(pszEnvVar + cchEnvVarBase, "_FLAGS");
924 pszValue = RTEnvGet(pszEnvVar);
925 if (pszValue)
926 RTLogFlags(pLogger, pszValue);
927
928 /*
929 * The group settings.
930 */
931 pszEnvVar[cchEnvVarBase] = '\0';
932 pszValue = RTEnvGet(pszEnvVar);
933 if (pszValue)
934 RTLogGroupSettings(pLogger, pszValue);
935 }
936 # else /* !IN_RING3 */
937 RT_NOREF_PV(pszEnvVarBase); RT_NOREF_PV(pszFilenameFmt); RT_NOREF_PV(args);
938 # endif /* !IN_RING3 */
939
940 /*
941 * Open the destination(s).
942 */
943 rc = VINF_SUCCESS;
944 if ((pLogger->fDestFlags & (RTLOGDEST_F_DELAY_FILE | RTLOGDEST_FILE)) == RTLOGDEST_F_DELAY_FILE)
945 pLogger->fDestFlags &= ~RTLOGDEST_F_DELAY_FILE;
946 # ifdef IN_RING3
947 if ((pLogger->fDestFlags & (RTLOGDEST_FILE | RTLOGDEST_F_DELAY_FILE)) == RTLOGDEST_FILE)
948 rc = rtR3LogOpenFileDestination(pLogger, pErrInfo);
949 # endif
950
951 if ((pLogger->fDestFlags & RTLOGDEST_RINGBUF) && RT_SUCCESS(rc))
952 rc = rtLogRingBufAdjust(pLogger, pLogger->pInt->cbRingBuf, true /*fForce*/);
953
954 /*
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.
957 */
958 if (RT_SUCCESS(rc))
959 {
960 rc = RTSemSpinMutexCreate(&pLogger->pInt->hSpinMtx, RTSEMSPINMUTEX_FLAGS_IRQ_SAFE);
961 if (RT_SUCCESS(rc))
962 {
963 # ifdef IN_RING3 /** @todo do counters in ring-0 too? */
964 RTTHREAD Thread = RTThreadSelf();
965 if (Thread != NIL_RTTHREAD)
966 {
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);
972 }
973
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);
978 # endif
979 pLogger->pInt->fCreated = true;
980 *ppLogger = pLogger;
981 return VINF_SUCCESS;
982 }
983
984 RTErrInfoSet(pErrInfo, rc, N_("failed to create semaphore"));
985 }
986 # ifdef IN_RING3
987 RTFileClose(pLogger->pInt->hFile);
988 # endif
989 # if defined(LOG_USE_C99) && defined(RT_WITHOUT_EXEC_ALLOC)
990 RTMemFree(*(void **)&pLogger->pfnLogger);
991 # else
992 RTMemExecFree(*(void **)&pLogger->pfnLogger, 64);
993 # endif
994 }
995 RTMemFree(pLogger);
996 }
997 else
998 rc = VERR_NO_MEMORY;
999
1000 return rc;
1001 }
1002 RT_EXPORT_SYMBOL(RTLogCreateExV);
1003
1004
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, ...)
1008 {
1009 va_list args;
1010 int rc;
1011
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);
1016 va_end(args);
1017 return rc;
1018 }
1019 RT_EXPORT_SYMBOL(RTLogCreate);
1020
1021
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, ...)
1027 {
1028 va_list args;
1029 int rc;
1030
1031 va_start(args, pszFilenameFmt);
1032 rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups,
1033 fDestFlags, pfnPhase, cHistory, cbHistoryFileMax, cSecsHistoryTimeSlot,
1034 pErrInfo, pszFilenameFmt, args);
1035 va_end(args);
1036 return rc;
1037 }
1038 RT_EXPORT_SYMBOL(RTLogCreateEx);
1039
1040
1041 /**
1042 * Destroys a logger instance.
1043 *
1044 * The instance is flushed and all output destinations closed (where applicable).
1045 *
1046 * @returns iprt status code.
1047 * @param pLogger The logger instance which close destroyed. NULL is fine.
1048 */
1049 RTDECL(int) RTLogDestroy(PRTLOGGER pLogger)
1050 {
1051 int rc;
1052 uint32_t iGroup;
1053 RTSEMSPINMUTEX hSpinMtx;
1054
1055 /*
1056 * Validate input.
1057 */
1058 if (!pLogger)
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);
1063
1064 /*
1065 * Acquire logger instance sem and disable all logging. (paranoia)
1066 */
1067 rc = rtlogLock(pLogger);
1068 AssertMsgRCReturn(rc, ("%Rrc\n", rc), rc);
1069
1070 pLogger->fFlags |= RTLOGFLAGS_DISABLED;
1071 iGroup = pLogger->cGroups;
1072 while (iGroup-- > 0)
1073 pLogger->afGroups[iGroup] = 0;
1074
1075 /*
1076 * Flush it.
1077 */
1078 rtlogFlush(pLogger, false /*fNeedSpace*/);
1079
1080 # ifdef IN_RING3
1081 /*
1082 * Add end of logging message.
1083 */
1084 if ( (pLogger->fDestFlags & RTLOGDEST_FILE)
1085 && pLogger->pInt->hFile != NIL_RTFILE)
1086 pLogger->pInt->pfnPhase(pLogger, RTLOGPHASE_END, rtlogPhaseMsgLocked);
1087
1088 /*
1089 * Close output stuffs.
1090 */
1091 if (pLogger->pInt->hFile != NIL_RTFILE)
1092 {
1093 int rc2 = RTFileClose(pLogger->pInt->hFile);
1094 AssertRC(rc2);
1095 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1096 rc = rc2;
1097 pLogger->pInt->hFile = NIL_RTFILE;
1098 }
1099 # endif
1100
1101 /*
1102 * Free the mutex, the wrapper and the instance memory.
1103 */
1104 hSpinMtx = pLogger->pInt->hSpinMtx;
1105 pLogger->pInt->hSpinMtx = NIL_RTSEMSPINMUTEX;
1106 if (hSpinMtx != NIL_RTSEMSPINMUTEX)
1107 {
1108 int rc2;
1109 RTSemSpinMutexRelease(hSpinMtx);
1110 rc2 = RTSemSpinMutexDestroy(hSpinMtx);
1111 AssertRC(rc2);
1112 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1113 rc = rc2;
1114 }
1115
1116 if (pLogger->pfnLogger)
1117 {
1118 # if defined(LOG_USE_C99) && defined(RT_WITHOUT_EXEC_ALLOC)
1119 RTMemFree(*(void **)&pLogger->pfnLogger);
1120 # else
1121 RTMemExecFree(*(void **)&pLogger->pfnLogger, 64);
1122 # endif
1123 pLogger->pfnLogger = NULL;
1124 }
1125 RTMemFree(pLogger);
1126
1127 return rc;
1128 }
1129 RT_EXPORT_SYMBOL(RTLogDestroy);
1130
1131
1132 /**
1133 * Create a logger instance clone for RC usage.
1134 *
1135 * @returns iprt status code.
1136 *
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
1140 * instance clone.
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.
1145 */
1146 RTDECL(int) RTLogCloneRC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerRC, size_t cbLoggerRC,
1147 RTRCPTR pfnLoggerRCPtr, RTRCPTR pfnFlushRCPtr, uint32_t fFlags)
1148 {
1149 /*
1150 * Validate input.
1151 */
1152 if ( !pLoggerRC
1153 || !pfnFlushRCPtr
1154 || !pfnLoggerRCPtr)
1155 {
1156 AssertMsgFailed(("Invalid parameters!\n"));
1157 return VERR_INVALID_PARAMETER;
1158 }
1159 if (cbLoggerRC < sizeof(*pLoggerRC))
1160 {
1161 AssertMsgFailed(("%d min=%d\n", cbLoggerRC, sizeof(*pLoggerRC)));
1162 return VERR_INVALID_PARAMETER;
1163 }
1164
1165 /*
1166 * Initialize GC instance.
1167 */
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;
1176
1177 /*
1178 * Resolve defaults.
1179 */
1180 if (!pLogger)
1181 {
1182 pLogger = RTLogDefaultInstance();
1183 if (!pLogger)
1184 return VINF_SUCCESS;
1185 }
1186
1187 /*
1188 * Check if there's enough space for the groups.
1189 */
1190 if (cbLoggerRC < (size_t)RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]))
1191 {
1192 AssertMsgFailed(("%d req=%d cGroups=%d\n", cbLoggerRC, RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]), pLogger->cGroups));
1193 return VERR_BUFFER_OVERFLOW;
1194 }
1195 memcpy(&pLoggerRC->afGroups[0], &pLogger->afGroups[0], pLogger->cGroups * sizeof(pLoggerRC->afGroups[0]));
1196 pLoggerRC->cGroups = pLogger->cGroups;
1197
1198 /*
1199 * Copy bits from the HC instance.
1200 */
1201 pLoggerRC->fPendingPrefix = pLogger->pInt->fPendingPrefix;
1202 pLoggerRC->fFlags |= pLogger->fFlags;
1203
1204 /*
1205 * Check if we can remove the disabled flag.
1206 */
1207 if ( pLogger->fDestFlags
1208 && !((pLogger->fFlags | fFlags) & RTLOGFLAGS_DISABLED))
1209 pLoggerRC->fFlags &= ~RTLOGFLAGS_DISABLED;
1210
1211 return VINF_SUCCESS;
1212 }
1213 RT_EXPORT_SYMBOL(RTLogCloneRC);
1214
1215
1216 /**
1217 * Flushes a RC logger instance to a R3 logger.
1218 *
1219 *
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.
1224 */
1225 RTDECL(void) RTLogFlushRC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerRC)
1226 {
1227 /*
1228 * Resolve defaults.
1229 */
1230 if (!pLogger)
1231 {
1232 pLogger = RTLogDefaultInstance();
1233 if (!pLogger)
1234 {
1235 pLoggerRC->offScratch = 0;
1236 return;
1237 }
1238 }
1239
1240 /*
1241 * Any thing to flush?
1242 */
1243 if ( pLogger->offScratch
1244 || pLoggerRC->offScratch)
1245 {
1246 /*
1247 * Acquire logger instance sem.
1248 */
1249 int rc = rtlogLock(pLogger);
1250 if (RT_FAILURE(rc))
1251 return;
1252
1253 /*
1254 * Write whatever the GC instance contains to the HC one, and then
1255 * flush the HC instance.
1256 */
1257 if (pLoggerRC->offScratch)
1258 {
1259 rtLogOutput(pLogger, pLoggerRC->achScratch, pLoggerRC->offScratch);
1260 rtLogOutput(pLogger, NULL, 0);
1261 pLoggerRC->offScratch = 0;
1262 }
1263
1264 /*
1265 * Release the semaphore.
1266 */
1267 rtlogUnlock(pLogger);
1268 }
1269 }
1270 RT_EXPORT_SYMBOL(RTLogFlushRC);
1271
1272 # ifdef IN_RING3
1273
1274 RTDECL(int) RTLogCreateForR0(PRTLOGGER pLogger, size_t cbLogger,
1275 RTR0PTR pLoggerR0Ptr, RTR0PTR pfnLoggerR0Ptr, RTR0PTR pfnFlushR0Ptr,
1276 uint32_t fFlags, uint32_t fDestFlags)
1277 {
1278 /*
1279 * Validate input.
1280 */
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);
1286
1287 /*
1288 * Initialize the ring-0 instance.
1289 */
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;
1298
1299 uint32_t cMaxGroups = (uint32_t)((cbLogger - cbRequired) / sizeof(pLogger->afGroups[0]));
1300 if (fFlags & RTLOGFLAGS_RESTRICT_GROUPS)
1301 cMaxGroups /= 2;
1302 PRTLOGGERINTERNAL pInt;
1303 for (;;)
1304 {
1305 AssertReturn(cMaxGroups > 0, VERR_BUFFER_OVERFLOW);
1306 pInt = (PRTLOGGERINTERNAL)&pLogger->afGroups[cMaxGroups];
1307 if (!((uintptr_t)pInt & (sizeof(uint64_t) - 1)))
1308 break;
1309 cMaxGroups--;
1310 }
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)
1323 {
1324 memset(pInt + 1, 0, sizeof(uint32_t) * cMaxGroups);
1325 pInt->pacEntriesPerGroup= (uint32_t *)(pLogger->pInt + 1);
1326 }
1327 else
1328 pInt->pacEntriesPerGroup= NULL;
1329
1330 pInt->fCreated = true;
1331 pLogger->u32Magic = RTLOGGER_MAGIC;
1332 return VINF_SUCCESS;
1333 }
1334 RT_EXPORT_SYMBOL(RTLogCreateForR0);
1335
1336
1337 RTDECL(size_t) RTLogCalcSizeForR0(uint32_t cGroups, uint32_t fFlags)
1338 {
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;
1344 return cb;
1345 }
1346 RT_EXPORT_SYMBOL(RTLogCalcSizeForR0);
1347
1348
1349 RTDECL(int) RTLogCopyGroupsAndFlagsForR0(PRTLOGGER pDstLogger, RTR0PTR pDstLoggerR0Ptr,
1350 PCRTLOGGER pSrcLogger, uint32_t fFlagsOr, uint32_t fFlagsAnd)
1351 {
1352 /*
1353 * Validate input.
1354 */
1355 AssertPtrReturn(pDstLogger, VERR_INVALID_PARAMETER);
1356 AssertPtrNullReturn(pSrcLogger, VERR_INVALID_PARAMETER);
1357
1358 /*
1359 * Resolve defaults.
1360 */
1361 if (!pSrcLogger)
1362 {
1363 pSrcLogger = RTLogDefaultInstance();
1364 if (!pSrcLogger)
1365 {
1366 pDstLogger->fFlags |= RTLOGFLAGS_DISABLED | fFlagsOr;
1367 pDstLogger->cGroups = 1;
1368 pDstLogger->afGroups[0] = 0;
1369 return VINF_SUCCESS;
1370 }
1371 }
1372
1373 /*
1374 * Copy flags and group settings.
1375 */
1376 pDstLogger->fFlags = (pSrcLogger->fFlags & fFlagsAnd & ~RTLOGFLAGS_RESTRICT_GROUPS) | fFlagsOr;
1377
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)
1382 {
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;
1387 }
1388 memcpy(&pDstLogger->afGroups[0], &pSrcLogger->afGroups[0], cGroups * sizeof(pDstLogger->afGroups[0]));
1389 pDstLogger->cGroups = cGroups;
1390
1391 return rc;
1392 }
1393 RT_EXPORT_SYMBOL(RTLogCopyGroupsAndFlagsForR0);
1394
1395
1396 RTDECL(int) RTLogSetCustomPrefixCallbackForR0(PRTLOGGER pLogger, RTR0PTR pLoggerR0Ptr,
1397 RTR0PTR pfnCallbackR0Ptr, RTR0PTR pvUserR0Ptr)
1398 {
1399 AssertPtrReturn(pLogger, VERR_INVALID_POINTER);
1400 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
1401
1402 /*
1403 * Do the work.
1404 */
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;
1409
1410 return VINF_SUCCESS;
1411 }
1412 RT_EXPORT_SYMBOL(RTLogSetCustomPrefixCallbackForR0);
1413
1414 RTDECL(void) RTLogFlushR0(PRTLOGGER pLogger, PRTLOGGER pLoggerR0)
1415 {
1416 /*
1417 * Resolve defaults.
1418 */
1419 if (!pLogger)
1420 {
1421 pLogger = RTLogDefaultInstance();
1422 if (!pLogger)
1423 {
1424 /* flushing to "/dev/null". */
1425 if (pLoggerR0->offScratch)
1426 pLoggerR0->offScratch = 0;
1427 return;
1428 }
1429 }
1430
1431 /*
1432 * Anything to flush?
1433 */
1434 if ( pLoggerR0->offScratch
1435 || pLogger->offScratch)
1436 {
1437 /*
1438 * Acquire logger semaphores.
1439 */
1440 int rc = rtlogLock(pLogger);
1441 if (RT_FAILURE(rc))
1442 return;
1443 if (RT_SUCCESS(rc))
1444 {
1445 /*
1446 * Write whatever the GC instance contains to the HC one, and then
1447 * flush the HC instance.
1448 */
1449 if (pLoggerR0->offScratch)
1450 {
1451 rtLogOutput(pLogger, pLoggerR0->achScratch, pLoggerR0->offScratch);
1452 rtLogOutput(pLogger, NULL, 0);
1453 pLoggerR0->offScratch = 0;
1454 }
1455 }
1456 rtlogUnlock(pLogger);
1457 }
1458 }
1459 RT_EXPORT_SYMBOL(RTLogFlushR0);
1460
1461 # endif /* IN_RING3 */
1462
1463
1464 /**
1465 * Flushes the buffer in one logger instance onto another logger.
1466 *
1467 * @returns iprt status code.
1468 *
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.
1472 */
1473 RTDECL(void) RTLogFlushToLogger(PRTLOGGER pSrcLogger, PRTLOGGER pDstLogger)
1474 {
1475 /*
1476 * Resolve defaults.
1477 */
1478 if (!pDstLogger)
1479 {
1480 pDstLogger = RTLogDefaultInstance();
1481 if (!pDstLogger)
1482 {
1483 /* flushing to "/dev/null". */
1484 if (pSrcLogger->offScratch)
1485 {
1486 int rc = rtlogLock(pSrcLogger);
1487 if (RT_SUCCESS(rc))
1488 {
1489 pSrcLogger->offScratch = 0;
1490 rtlogUnlock(pSrcLogger);
1491 }
1492 }
1493 return;
1494 }
1495 }
1496
1497 /*
1498 * Any thing to flush?
1499 */
1500 if ( pSrcLogger->offScratch
1501 || pDstLogger->offScratch)
1502 {
1503 /*
1504 * Acquire logger semaphores.
1505 */
1506 int rc = rtlogLock(pDstLogger);
1507 if (RT_FAILURE(rc))
1508 return;
1509 rc = rtlogLock(pSrcLogger);
1510 if (RT_SUCCESS(rc))
1511 {
1512 /*
1513 * Write whatever the GC instance contains to the HC one, and then
1514 * flush the HC instance.
1515 */
1516 if (pSrcLogger->offScratch)
1517 {
1518 rtLogOutput(pDstLogger, pSrcLogger->achScratch, pSrcLogger->offScratch);
1519 rtLogOutput(pDstLogger, NULL, 0);
1520 pSrcLogger->offScratch = 0;
1521 }
1522
1523 /*
1524 * Release the semaphores.
1525 */
1526 rtlogUnlock(pSrcLogger);
1527 }
1528 rtlogUnlock(pDstLogger);
1529 }
1530 }
1531 RT_EXPORT_SYMBOL(RTLogFlushToLogger);
1532
1533
1534 /**
1535 * Sets the custom prefix callback.
1536 *
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.
1541 * */
1542 RTDECL(int) RTLogSetCustomPrefixCallback(PRTLOGGER pLogger, PFNRTLOGPREFIX pfnCallback, void *pvUser)
1543 {
1544 /*
1545 * Resolve defaults.
1546 */
1547 if (!pLogger)
1548 {
1549 pLogger = RTLogDefaultInstance();
1550 if (!pLogger)
1551 return VINF_SUCCESS;
1552 }
1553 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
1554
1555 /*
1556 * Do the work.
1557 */
1558 rtlogLock(pLogger);
1559 pLogger->pInt->pvPrefixUserArg = pvUser;
1560 pLogger->pInt->pfnPrefix = pfnCallback;
1561 rtlogUnlock(pLogger);
1562
1563 return VINF_SUCCESS;
1564 }
1565 RT_EXPORT_SYMBOL(RTLogSetCustomPrefixCallback);
1566
1567
1568 /**
1569 * Matches a group name with a pattern mask in an case insensitive manner (ASCII).
1570 *
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.
1577 */
1578 static bool rtlogIsGroupMatching(const char *pszGrp, const char **ppachMask, size_t cchMask)
1579 {
1580 const char *pachMask;
1581
1582 if (!pszGrp || !*pszGrp)
1583 return false;
1584 pachMask = *ppachMask;
1585 for (;;)
1586 {
1587 if (RT_C_TO_LOWER(*pszGrp) != RT_C_TO_LOWER(*pachMask))
1588 {
1589 const char *pszTmp;
1590
1591 /*
1592 * Check for wildcard and do a minimal match if found.
1593 */
1594 if (*pachMask != '*')
1595 return false;
1596
1597 /* eat '*'s. */
1598 do pachMask++;
1599 while (--cchMask && *pachMask == '*');
1600
1601 /* is there more to match? */
1602 if ( !cchMask
1603 || *pachMask == '.'
1604 || *pachMask == '=')
1605 break; /* we're good */
1606
1607 /* do extremely minimal matching (fixme) */
1608 pszTmp = strchr(pszGrp, RT_C_TO_LOWER(*pachMask));
1609 if (!pszTmp)
1610 pszTmp = strchr(pszGrp, RT_C_TO_UPPER(*pachMask));
1611 if (!pszTmp)
1612 return false;
1613 pszGrp = pszTmp;
1614 continue;
1615 }
1616
1617 /* done? */
1618 if (!*++pszGrp)
1619 {
1620 /* trailing wildcard is ok. */
1621 do
1622 {
1623 pachMask++;
1624 cchMask--;
1625 } while (cchMask && *pachMask == '*');
1626 if ( !cchMask
1627 || *pachMask == '.'
1628 || *pachMask == '=')
1629 break; /* we're good */
1630 return false;
1631 }
1632
1633 if (!--cchMask)
1634 return false;
1635 pachMask++;
1636 }
1637
1638 /* match */
1639 *ppachMask = pachMask;
1640 return true;
1641 }
1642
1643
1644 /**
1645 * Updates the group settings for the logger instance using the specified
1646 * specification string.
1647 *
1648 * @returns iprt status code.
1649 * Failures can safely be ignored.
1650 * @param pLogger Logger instance.
1651 * @param pszValue Value to parse.
1652 */
1653 RTDECL(int) RTLogGroupSettings(PRTLOGGER pLogger, const char *pszValue)
1654 {
1655 /*
1656 * Resolve defaults.
1657 */
1658 if (!pLogger)
1659 {
1660 pLogger = RTLogDefaultInstance();
1661 if (!pLogger)
1662 return VINF_SUCCESS;
1663 }
1664
1665 /*
1666 * Iterate the string.
1667 */
1668 while (*pszValue)
1669 {
1670 /*
1671 * Skip prefixes (blanks, ;, + and -).
1672 */
1673 bool fEnabled = true;
1674 char ch;
1675 const char *pszStart;
1676 unsigned i;
1677 size_t cch;
1678
1679 while ((ch = *pszValue) == '+' || ch == '-' || ch == ' ' || ch == '\t' || ch == '\n' || ch == ';')
1680 {
1681 if (ch == '+' || ch == '-' || ch == ';')
1682 fEnabled = ch != '-';
1683 pszValue++;
1684 }
1685 if (!*pszValue)
1686 break;
1687
1688 /*
1689 * Find end.
1690 */
1691 pszStart = pszValue;
1692 while ((ch = *pszValue) != '\0' && ch != '+' && ch != '-' && ch != ' ' && ch != '\t')
1693 pszValue++;
1694
1695 /*
1696 * Find the group (ascii case insensitive search).
1697 * Special group 'all'.
1698 */
1699 cch = pszValue - pszStart;
1700 if ( cch >= 3
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] == '='))
1705 {
1706 /*
1707 * All.
1708 */
1709 unsigned fFlags = cch == 3
1710 ? RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1
1711 : rtlogGroupFlags(&pszStart[3]);
1712 for (i = 0; i < pLogger->cGroups; i++)
1713 {
1714 if (fEnabled)
1715 pLogger->afGroups[i] |= fFlags;
1716 else
1717 pLogger->afGroups[i] &= ~fFlags;
1718 }
1719 }
1720 else
1721 {
1722 /*
1723 * Specific group(s).
1724 */
1725 for (i = 0; i < pLogger->cGroups; i++)
1726 {
1727 const char *psz2 = (const char*)pszStart;
1728 if (rtlogIsGroupMatching(pLogger->pInt->papszGroups[i], &psz2, cch))
1729 {
1730 unsigned fFlags = RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1;
1731 if (*psz2 == '.' || *psz2 == '=')
1732 fFlags = rtlogGroupFlags(psz2);
1733 if (fEnabled)
1734 pLogger->afGroups[i] |= fFlags;
1735 else
1736 pLogger->afGroups[i] &= ~fFlags;
1737 }
1738 } /* for each group */
1739 }
1740
1741 } /* parse specification */
1742
1743 return VINF_SUCCESS;
1744 }
1745 RT_EXPORT_SYMBOL(RTLogGroupSettings);
1746
1747
1748 /**
1749 * Interprets the group flags suffix.
1750 *
1751 * @returns Flags specified. (0 is possible!)
1752 * @param psz Start of Suffix. (Either dot or equal sign.)
1753 */
1754 static unsigned rtlogGroupFlags(const char *psz)
1755 {
1756 unsigned fFlags = 0;
1757
1758 /*
1759 * Literal flags.
1760 */
1761 while (*psz == '.')
1762 {
1763 static struct
1764 {
1765 const char *pszFlag; /* lowercase!! */
1766 unsigned fFlag;
1767 } aFlags[] =
1768 {
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 },
1804
1805 };
1806 unsigned i;
1807 bool fFound = false;
1808 psz++;
1809 for (i = 0; i < RT_ELEMENTS(aFlags) && !fFound; i++)
1810 {
1811 const char *psz1 = aFlags[i].pszFlag;
1812 const char *psz2 = psz;
1813 while (*psz1 == RT_C_TO_LOWER(*psz2))
1814 {
1815 psz1++;
1816 psz2++;
1817 if (!*psz1)
1818 {
1819 if ( (*psz2 >= 'a' && *psz2 <= 'z')
1820 || (*psz2 >= 'A' && *psz2 <= 'Z')
1821 || (*psz2 >= '0' && *psz2 <= '9') )
1822 break;
1823 fFlags |= aFlags[i].fFlag;
1824 fFound = true;
1825 psz = psz2;
1826 break;
1827 }
1828 } /* strincmp */
1829 } /* for each flags */
1830 AssertMsg(fFound, ("%.15s...", psz));
1831 }
1832
1833 /*
1834 * Flag value.
1835 */
1836 if (*psz == '=')
1837 {
1838 psz++;
1839 if (*psz == '~')
1840 fFlags = ~RTStrToInt32(psz + 1);
1841 else
1842 fFlags = RTStrToInt32(psz);
1843 }
1844
1845 return fFlags;
1846 }
1847
1848 /**
1849 * Helper for RTLogGetGroupSettings.
1850 */
1851 static int rtLogGetGroupSettingsAddOne(const char *pszName, uint32_t fGroup, char **ppszBuf, size_t *pcchBuf, bool *pfNotFirst)
1852 {
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)
1856
1857 /*
1858 * Add the name.
1859 */
1860 size_t cchName = strlen(pszName);
1861 if (cchName + 1 + *pfNotFirst > *pcchBuf)
1862 return VERR_BUFFER_OVERFLOW;
1863 if (*pfNotFirst)
1864 APPEND_CH(' ');
1865 else
1866 *pfNotFirst = true;
1867 APPEND_PSZ(pszName, cchName);
1868
1869 /*
1870 * Only generate mnemonics for the simple+common bits.
1871 */
1872 if (fGroup == (RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1))
1873 /* nothing */;
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"))
1879 APPEND_SZ(".e.f");
1880 else if (*pcchBuf >= 1 + 10 + 1)
1881 {
1882 size_t cch;
1883 APPEND_CH('=');
1884 cch = RTStrFormatNumber(*ppszBuf, fGroup, 16, 0, 0, RTSTR_F_SPECIAL | RTSTR_F_32BIT);
1885 *ppszBuf += cch;
1886 *pcchBuf -= cch;
1887 }
1888 else
1889 return VERR_BUFFER_OVERFLOW;
1890
1891 # undef APPEND_PSZ
1892 # undef APPEND_SZ
1893 # undef APPEND_CH
1894 return VINF_SUCCESS;
1895 }
1896
1897
1898 /**
1899 * Get the current log group settings as a string.
1900 *
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
1905 * than zero.
1906 */
1907 RTDECL(int) RTLogGetGroupSettings(PRTLOGGER pLogger, char *pszBuf, size_t cchBuf)
1908 {
1909 bool fNotFirst = false;
1910 int rc = VINF_SUCCESS;
1911 uint32_t cGroups;
1912 uint32_t fGroup;
1913 uint32_t i;
1914
1915 Assert(cchBuf);
1916
1917 /*
1918 * Resolve defaults.
1919 */
1920 if (!pLogger)
1921 {
1922 pLogger = RTLogDefaultInstance();
1923 if (!pLogger)
1924 {
1925 *pszBuf = '\0';
1926 return VINF_SUCCESS;
1927 }
1928 }
1929
1930 cGroups = pLogger->cGroups;
1931
1932 /*
1933 * Check if all are the same.
1934 */
1935 fGroup = pLogger->afGroups[0];
1936 for (i = 1; i < cGroups; i++)
1937 if (pLogger->afGroups[i] != fGroup)
1938 break;
1939 if (i >= cGroups)
1940 rc = rtLogGetGroupSettingsAddOne("all", fGroup, &pszBuf, &cchBuf, &fNotFirst);
1941 else
1942 {
1943
1944 /*
1945 * Iterate all the groups and print all that are enabled.
1946 */
1947 for (i = 0; i < cGroups; i++)
1948 {
1949 fGroup = pLogger->afGroups[i];
1950 if (fGroup)
1951 {
1952 const char *pszName = pLogger->pInt->papszGroups[i];
1953 if (pszName)
1954 {
1955 rc = rtLogGetGroupSettingsAddOne(pszName, fGroup, &pszBuf, &cchBuf, &fNotFirst);
1956 if (rc)
1957 break;
1958 }
1959 }
1960 }
1961 }
1962
1963 *pszBuf = '\0';
1964 return rc;
1965 }
1966 RT_EXPORT_SYMBOL(RTLogGetGroupSettings);
1967
1968 #endif /* !IN_RC */
1969
1970 /**
1971 * Updates the flags for the logger instance using the specified
1972 * specification string.
1973 *
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.
1978 */
1979 RTDECL(int) RTLogFlags(PRTLOGGER pLogger, const char *pszValue)
1980 {
1981 int rc = VINF_SUCCESS;
1982
1983 /*
1984 * Resolve defaults.
1985 */
1986 if (!pLogger)
1987 {
1988 pLogger = RTLogDefaultInstance();
1989 if (!pLogger)
1990 return VINF_SUCCESS;
1991 }
1992
1993 /*
1994 * Iterate the string.
1995 */
1996 while (*pszValue)
1997 {
1998 /* check no prefix. */
1999 bool fNo = false;
2000 char ch;
2001 unsigned i;
2002
2003 /* skip blanks. */
2004 while (RT_C_IS_SPACE(*pszValue))
2005 pszValue++;
2006 if (!*pszValue)
2007 return rc;
2008
2009 while ((ch = *pszValue) != '\0')
2010 {
2011 if (ch == 'n' && pszValue[1] == 'o')
2012 {
2013 pszValue += 2;
2014 fNo = !fNo;
2015 }
2016 else if (ch == '+')
2017 {
2018 pszValue++;
2019 fNo = true;
2020 }
2021 else if (ch == '-' || ch == '!' || ch == '~')
2022 {
2023 pszValue++;
2024 fNo = !fNo;
2025 }
2026 else
2027 break;
2028 }
2029
2030 /* instruction. */
2031 for (i = 0; i < RT_ELEMENTS(g_aLogFlags); i++)
2032 {
2033 if (!strncmp(pszValue, g_aLogFlags[i].pszInstr, g_aLogFlags[i].cchInstr))
2034 {
2035 if (fNo == g_aLogFlags[i].fInverted)
2036 pLogger->fFlags |= g_aLogFlags[i].fFlag;
2037 else
2038 pLogger->fFlags &= ~g_aLogFlags[i].fFlag;
2039 pszValue += g_aLogFlags[i].cchInstr;
2040 break;
2041 }
2042 }
2043
2044 /* unknown instruction? */
2045 if (i >= RT_ELEMENTS(g_aLogFlags))
2046 {
2047 AssertMsgFailed(("Invalid flags! unknown instruction %.20s\n", pszValue));
2048 pszValue++;
2049 }
2050
2051 /* skip blanks and delimiters. */
2052 while (RT_C_IS_SPACE(*pszValue) || *pszValue == ';')
2053 pszValue++;
2054 } /* while more environment variable value left */
2055
2056 return rc;
2057 }
2058 RT_EXPORT_SYMBOL(RTLogFlags);
2059
2060
2061 /**
2062 * Changes the buffering setting of the specified logger.
2063 *
2064 * This can be used for optimizing longish logging sequences.
2065 *
2066 * @returns The old state.
2067 * @param pLogger The logger instance (NULL is an alias for the
2068 * default logger).
2069 * @param fBuffered The new state.
2070 */
2071 RTDECL(bool) RTLogSetBuffering(PRTLOGGER pLogger, bool fBuffered)
2072 {
2073 bool fOld;
2074
2075 /*
2076 * Resolve the logger instance.
2077 */
2078 if (!pLogger)
2079 {
2080 pLogger = RTLogDefaultInstance();
2081 if (!pLogger)
2082 return false;
2083 }
2084
2085 rtlogLock(pLogger);
2086 fOld = !!(pLogger->fFlags & RTLOGFLAGS_BUFFERED);
2087 if (fBuffered)
2088 pLogger->fFlags |= RTLOGFLAGS_BUFFERED;
2089 else
2090 pLogger->fFlags &= ~RTLOGFLAGS_BUFFERED;
2091 rtlogUnlock(pLogger);
2092
2093 return fOld;
2094 }
2095 RT_EXPORT_SYMBOL(RTLogSetBuffering);
2096
2097
2098 #ifdef IN_RING3
2099 RTDECL(uint32_t) RTLogSetGroupLimit(PRTLOGGER pLogger, uint32_t cMaxEntriesPerGroup)
2100 {
2101 /*
2102 * Resolve the logger instance.
2103 */
2104 if (!pLogger)
2105 {
2106 pLogger = RTLogDefaultInstance();
2107 if (!pLogger)
2108 return UINT32_MAX;
2109 }
2110
2111 rtlogLock(pLogger);
2112 uint32_t cOld = pLogger->pInt->cMaxEntriesPerGroup;
2113 pLogger->pInt->cMaxEntriesPerGroup = cMaxEntriesPerGroup;
2114 rtlogUnlock(pLogger);
2115
2116 return cOld;
2117 }
2118 #endif
2119
2120 #ifndef IN_RC
2121
2122 /**
2123 * Get the current log flags as a string.
2124 *
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
2129 * than zero.
2130 */
2131 RTDECL(int) RTLogGetFlags(PRTLOGGER pLogger, char *pszBuf, size_t cchBuf)
2132 {
2133 bool fNotFirst = false;
2134 int rc = VINF_SUCCESS;
2135 uint32_t fFlags;
2136 unsigned i;
2137
2138 Assert(cchBuf);
2139
2140 /*
2141 * Resolve defaults.
2142 */
2143 if (!pLogger)
2144 {
2145 pLogger = RTLogDefaultInstance();
2146 if (!pLogger)
2147 {
2148 *pszBuf = '\0';
2149 return VINF_SUCCESS;
2150 }
2151 }
2152
2153 /*
2154 * Add the flags in the list.
2155 */
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))
2161 {
2162 size_t cchInstr = g_aLogFlags[i].cchInstr;
2163 if (cchInstr + fNotFirst + 1 > cchBuf)
2164 {
2165 rc = VERR_BUFFER_OVERFLOW;
2166 break;
2167 }
2168 if (fNotFirst)
2169 {
2170 *pszBuf++ = ' ';
2171 cchBuf--;
2172 }
2173 memcpy(pszBuf, g_aLogFlags[i].pszInstr, cchInstr);
2174 pszBuf += cchInstr;
2175 cchBuf -= cchInstr;
2176 fNotFirst = true;
2177 }
2178 *pszBuf = '\0';
2179 return rc;
2180 }
2181 RT_EXPORT_SYMBOL(RTLogGetFlags);
2182
2183
2184 /**
2185 * Finds the end of a destination value.
2186 *
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.)
2190 *
2191 * @returns Value length in chars.
2192 * @param pszValue The first char after '=' or ':'.
2193 */
2194 static size_t rtLogDestFindValueLength(const char *pszValue)
2195 {
2196 size_t off = 0;
2197 char ch;
2198 while ((ch = pszValue[off]) != '\0' && ch != ';')
2199 {
2200 if (!RT_C_IS_SPACE(ch))
2201 off++;
2202 else
2203 {
2204 unsigned i;
2205 size_t cchThusFar = off;
2206 do
2207 off++;
2208 while ((ch = pszValue[off]) != '\0' && RT_C_IS_SPACE(ch));
2209 if (ch == ';')
2210 return cchThusFar;
2211
2212 if (ch == 'n' && pszValue[off + 1] == 'o')
2213 off += 2;
2214 for (i = 0; i < RT_ELEMENTS(g_aLogDst); i++)
2215 if (!strncmp(&pszValue[off], g_aLogDst[i].pszInstr, g_aLogDst[i].cchInstr))
2216 {
2217 ch = pszValue[off + g_aLogDst[i].cchInstr];
2218 if (ch == '\0' || RT_C_IS_SPACE(ch) || ch == '=' || ch == ':' || ch == ';')
2219 return cchThusFar;
2220 }
2221 }
2222 }
2223 return off;
2224 }
2225
2226
2227 /**
2228 * Updates the logger destination using the specified string.
2229 *
2230 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
2231 * @param pLogger Logger instance (NULL for default logger).
2232 * @param pszValue The value to parse.
2233 */
2234 RTDECL(int) RTLogDestinations(PRTLOGGER pLogger, char const *pszValue)
2235 {
2236 /*
2237 * Resolve defaults.
2238 */
2239 if (!pLogger)
2240 {
2241 pLogger = RTLogDefaultInstance();
2242 if (!pLogger)
2243 return VINF_SUCCESS;
2244 }
2245
2246 /*
2247 * Do the parsing.
2248 */
2249 while (*pszValue)
2250 {
2251 bool fNo;
2252 unsigned i;
2253
2254 /* skip blanks. */
2255 while (RT_C_IS_SPACE(*pszValue))
2256 pszValue++;
2257 if (!*pszValue)
2258 break;
2259
2260 /* check no prefix. */
2261 fNo = false;
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'))
2268 {
2269 fNo = true;
2270 pszValue += 2;
2271 }
2272
2273 /* instruction. */
2274 for (i = 0; i < RT_ELEMENTS(g_aLogDst); i++)
2275 {
2276 size_t cchInstr = strlen(g_aLogDst[i].pszInstr);
2277 if (!strncmp(pszValue, g_aLogDst[i].pszInstr, cchInstr))
2278 {
2279 if (!fNo)
2280 pLogger->fDestFlags |= g_aLogDst[i].fFlag;
2281 else
2282 pLogger->fDestFlags &= ~g_aLogDst[i].fFlag;
2283 pszValue += cchInstr;
2284
2285 /* check for value. */
2286 while (RT_C_IS_SPACE(*pszValue))
2287 pszValue++;
2288 if (*pszValue == '=' || *pszValue == ':')
2289 {
2290 pszValue++;
2291 size_t cch = rtLogDestFindValueLength(pszValue);
2292 const char *pszEnd = pszValue + cch;
2293
2294 # ifdef IN_RING3
2295 char szTmp[sizeof(pLogger->pInt->szFilename)];
2296 # else
2297 char szTmp[32];
2298 # endif
2299 if (0)
2300 { /* nothing */ }
2301 #ifdef IN_RING3
2302
2303 /* log file name */
2304 else if (i == 0 /* file */ && !fNo)
2305 {
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 ... */
2310 }
2311 /* log directory */
2312 else if (i == 1 /* dir */ && !fNo)
2313 {
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);
2318
2319 memcpy(pLogger->pInt->szFilename, pszValue, cch);
2320 pLogger->pInt->szFilename[cch] = '\0';
2321 RTPathStripTrailingSlash(pLogger->pInt->szFilename);
2322
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 ... */
2328 }
2329 else if (i == 2 /* history */)
2330 {
2331 if (!fNo)
2332 {
2333 uint32_t cHistory = 0;
2334 int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszValue, cch);
2335 if (RT_SUCCESS(rc))
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;
2339 }
2340 else
2341 pLogger->pInt->cHistory = 0;
2342 }
2343 else if (i == 3 /* histsize */)
2344 {
2345 if (!fNo)
2346 {
2347 int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszValue, cch);
2348 if (RT_SUCCESS(rc))
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;
2353 }
2354 else
2355 pLogger->pInt->cbHistoryFileMax = UINT64_MAX;
2356 }
2357 else if (i == 4 /* histtime */)
2358 {
2359 if (!fNo)
2360 {
2361 int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszValue, cch);
2362 if (RT_SUCCESS(rc))
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;
2367 }
2368 else
2369 pLogger->pInt->cSecsHistoryTimeSlot = UINT32_MAX;
2370 }
2371 # endif /* IN_RING3 */
2372 else if (i == 5 /* ringbuf */ && !fNo)
2373 {
2374 int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszValue, cch);
2375 uint32_t cbRingBuf = 0;
2376 if (RT_SUCCESS(rc))
2377 rc = RTStrToUInt32Full(szTmp, 0, &cbRingBuf);
2378 AssertMsgRCReturn(rc, ("Invalid ring buffer size value '%s' (%Rrc)!\n", szTmp, rc), rc);
2379
2380 if (cbRingBuf == 0)
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;
2386 else
2387 cbRingBuf = RT_ALIGN_32(cbRingBuf, 64);
2388 rc = rtLogRingBufAdjust(pLogger, cbRingBuf, false /*fForce*/);
2389 if (RT_FAILURE(rc))
2390 return rc;
2391 }
2392 else
2393 AssertMsgFailedReturn(("Invalid destination value! %s%s doesn't take a value!\n",
2394 fNo ? "no" : "", g_aLogDst[i].pszInstr),
2395 VERR_INVALID_PARAMETER);
2396
2397 pszValue = pszEnd + (*pszEnd != '\0');
2398 }
2399 else if (i == 5 /* ringbuf */ && !fNo && !pLogger->pInt->pszRingBuf)
2400 {
2401 int rc = rtLogRingBufAdjust(pLogger, pLogger->pInt->cbRingBuf, false /*fForce*/);
2402 if (RT_FAILURE(rc))
2403 return rc;
2404 }
2405 break;
2406 }
2407 }
2408
2409 /* assert known instruction */
2410 AssertMsgReturn(i < RT_ELEMENTS(g_aLogDst),
2411 ("Invalid destination value! unknown instruction %.20s\n", pszValue),
2412 VERR_INVALID_PARAMETER);
2413
2414 /* skip blanks and delimiters. */
2415 while (RT_C_IS_SPACE(*pszValue) || *pszValue == ';')
2416 pszValue++;
2417 } /* while more environment variable value left */
2418
2419 return VINF_SUCCESS;
2420 }
2421 RT_EXPORT_SYMBOL(RTLogDestinations);
2422
2423
2424 /**
2425 * Clear the file delay flag if set, opening the destination and flushing.
2426 *
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.
2431 */
2432 RTDECL(int) RTLogClearFileDelayFlag(PRTLOGGER pLogger, PRTERRINFO pErrInfo)
2433 {
2434 /*
2435 * Resolve defaults.
2436 */
2437 if (!pLogger)
2438 {
2439 pLogger = RTLogDefaultInstance();
2440 if (!pLogger)
2441 return VINF_SUCCESS;
2442 }
2443
2444 /*
2445 * Do the work.
2446 */
2447 int rc = rtlogLock(pLogger);
2448 if (RT_SUCCESS(rc))
2449 {
2450 if (pLogger->fDestFlags & RTLOGDEST_F_DELAY_FILE)
2451 {
2452 pLogger->fDestFlags &= ~RTLOGDEST_F_DELAY_FILE;
2453 # ifdef IN_RING3
2454 if ( pLogger->fDestFlags & RTLOGDEST_FILE
2455 && pLogger->pInt->hFile == NIL_RTFILE)
2456 {
2457 rc = rtR3LogOpenFileDestination(pLogger, pErrInfo);
2458 if (RT_SUCCESS(rc))
2459 rtlogFlush(pLogger, false /*fNeedSpace*/);
2460 }
2461 # endif
2462 RT_NOREF(pErrInfo); /** @todo fix create API to use RTErrInfo */
2463 }
2464 rtlogUnlock(pLogger);
2465 }
2466 return VINF_SUCCESS;
2467 }
2468 RT_EXPORT_SYMBOL(RTLogClearFileDelayFlag);
2469
2470
2471 /**
2472 * Get the current log destinations as a string.
2473 *
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
2478 * than 0.
2479 */
2480 RTDECL(int) RTLogGetDestinations(PRTLOGGER pLogger, char *pszBuf, size_t cchBuf)
2481 {
2482 bool fNotFirst = false;
2483 int rc = VINF_SUCCESS;
2484 uint32_t fDestFlags;
2485 unsigned i;
2486
2487 AssertReturn(cchBuf, VERR_INVALID_PARAMETER);
2488 *pszBuf = '\0';
2489
2490 /*
2491 * Resolve defaults.
2492 */
2493 if (!pLogger)
2494 {
2495 pLogger = RTLogDefaultInstance();
2496 if (!pLogger)
2497 return VINF_SUCCESS;
2498 }
2499
2500 /*
2501 * Add the flags in the list.
2502 */
2503 fDestFlags = pLogger->fDestFlags;
2504 for (i = 6; i < RT_ELEMENTS(g_aLogDst); i++)
2505 if (g_aLogDst[i].fFlag & fDestFlags)
2506 {
2507 if (fNotFirst)
2508 {
2509 rc = RTStrCopyP(&pszBuf, &cchBuf, " ");
2510 if (RT_FAILURE(rc))
2511 return rc;
2512 }
2513 rc = RTStrCopyP(&pszBuf, &cchBuf, g_aLogDst[i].pszInstr);
2514 if (RT_FAILURE(rc))
2515 return rc;
2516 fNotFirst = true;
2517 }
2518
2519 char szNum[32];
2520
2521 # ifdef IN_RING3
2522 /*
2523 * Add the filename.
2524 */
2525 if (fDestFlags & RTLOGDEST_FILE)
2526 {
2527 rc = RTStrCopyP(&pszBuf, &cchBuf, fNotFirst ? " file=" : "file=");
2528 if (RT_FAILURE(rc))
2529 return rc;
2530 rc = RTStrCopyP(&pszBuf, &cchBuf, pLogger->pInt->szFilename);
2531 if (RT_FAILURE(rc))
2532 return rc;
2533 fNotFirst = true;
2534
2535 if (pLogger->pInt->cHistory)
2536 {
2537 RTStrPrintf(szNum, sizeof(szNum), fNotFirst ? " history=%u" : "history=%u", pLogger->pInt->cHistory);
2538 rc = RTStrCopyP(&pszBuf, &cchBuf, szNum);
2539 if (RT_FAILURE(rc))
2540 return rc;
2541 fNotFirst = true;
2542 }
2543 if (pLogger->pInt->cbHistoryFileMax != UINT64_MAX)
2544 {
2545 RTStrPrintf(szNum, sizeof(szNum), fNotFirst ? " histsize=%llu" : "histsize=%llu", pLogger->pInt->cbHistoryFileMax);
2546 rc = RTStrCopyP(&pszBuf, &cchBuf, szNum);
2547 if (RT_FAILURE(rc))
2548 return rc;
2549 fNotFirst = true;
2550 }
2551 if (pLogger->pInt->cSecsHistoryTimeSlot != UINT32_MAX)
2552 {
2553 RTStrPrintf(szNum, sizeof(szNum), fNotFirst ? " histtime=%llu" : "histtime=%llu", pLogger->pInt->cSecsHistoryTimeSlot);
2554 rc = RTStrCopyP(&pszBuf, &cchBuf, szNum);
2555 if (RT_FAILURE(rc))
2556 return rc;
2557 fNotFirst = true;
2558 }
2559 }
2560 # endif /* IN_RING3 */
2561
2562 /*
2563 * Add the ring buffer.
2564 */
2565 if (fDestFlags & RTLOGDEST_RINGBUF)
2566 {
2567 if (pLogger->pInt->cbRingBuf == RTLOG_RINGBUF_DEFAULT_SIZE)
2568 rc = RTStrCopyP(&pszBuf, &cchBuf, fNotFirst ? " ringbuf" : "ringbuf");
2569 else
2570 {
2571 RTStrPrintf(szNum, sizeof(szNum), fNotFirst ? " ringbuf=%#x" : "ringbuf=%#x", pLogger->pInt->cbRingBuf);
2572 rc = RTStrCopyP(&pszBuf, &cchBuf, szNum);
2573 }
2574 if (RT_FAILURE(rc))
2575 return rc;
2576 fNotFirst = true;
2577 }
2578
2579 return VINF_SUCCESS;
2580 }
2581 RT_EXPORT_SYMBOL(RTLogGetDestinations);
2582
2583 #endif /* !IN_RC */
2584
2585 /**
2586 * Flushes the specified logger.
2587 *
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.
2591 */
2592 RTDECL(void) RTLogFlush(PRTLOGGER pLogger)
2593 {
2594 /*
2595 * Resolve defaults.
2596 */
2597 if (!pLogger)
2598 {
2599 #ifdef IN_RC
2600 pLogger = &g_Logger;
2601 #else
2602 pLogger = g_pLogger;
2603 #endif
2604 if (!pLogger)
2605 return;
2606 }
2607
2608 /*
2609 * Any thing to flush?
2610 */
2611 if ( pLogger->offScratch
2612 #ifndef IN_RC
2613 || (pLogger->fDestFlags & RTLOGDEST_RINGBUF)
2614 #endif
2615 )
2616 {
2617 #ifndef IN_RC
2618 /*
2619 * Acquire logger instance sem.
2620 */
2621 int rc = rtlogLock(pLogger);
2622 if (RT_FAILURE(rc))
2623 return;
2624 #endif
2625 /*
2626 * Call worker.
2627 */
2628 rtlogFlush(pLogger, false /*fNeedSpace*/);
2629
2630 #ifndef IN_RC
2631 /*
2632 * Since this is an explicit flush call, the ring buffer content should
2633 * be flushed to the other destinations if active.
2634 */
2635 if ( (pLogger->fDestFlags & RTLOGDEST_RINGBUF)
2636 && pLogger->pInt->pszRingBuf /* paranoia */)
2637 rtLogRingBufFlush(pLogger);
2638
2639 /*
2640 * Release the semaphore.
2641 */
2642 rtlogUnlock(pLogger);
2643 #endif
2644 }
2645 }
2646 RT_EXPORT_SYMBOL(RTLogFlush);
2647
2648
2649 /**
2650 * Common worker for RTLogDefaultInstance and RTLogDefaultInstanceEx.
2651 */
2652 DECL_FORCE_INLINE(PRTLOGGER) rtLogDefaultInstanceCommon(void)
2653 {
2654 #ifdef IN_RC
2655 return &g_Logger;
2656
2657 #else /* !IN_RC */
2658 # ifdef IN_RING0
2659 /*
2660 * Check per thread loggers first.
2661 */
2662 if (g_cPerThreadLoggers)
2663 {
2664 const RTNATIVETHREAD Self = RTThreadNativeSelf();
2665 int32_t i = RT_ELEMENTS(g_aPerThreadLoggers);
2666 while (i-- > 0)
2667 if (g_aPerThreadLoggers[i].NativeThread == Self)
2668 return g_aPerThreadLoggers[i].pLogger;
2669 }
2670 # endif /* IN_RING0 */
2671
2672 /*
2673 * If no per thread logger, use the default one.
2674 */
2675 if (!g_pLogger)
2676 g_pLogger = RTLogDefaultInit();
2677 return g_pLogger;
2678 #endif /* !IN_RC */
2679 }
2680
2681
2682 RTDECL(PRTLOGGER) RTLogDefaultInstance(void)
2683 {
2684 return rtLogDefaultInstanceCommon();
2685 }
2686 RT_EXPORT_SYMBOL(RTLogDefaultInstance);
2687
2688
2689 RTDECL(PRTLOGGER) RTLogDefaultInstanceEx(uint32_t fFlagsAndGroup)
2690 {
2691 PRTLOGGER pLogger = rtLogDefaultInstanceCommon();
2692 if (pLogger)
2693 {
2694 if (pLogger->fFlags & RTLOGFLAGS_DISABLED)
2695 pLogger = NULL;
2696 else
2697 {
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)))
2703 pLogger = NULL;
2704 }
2705 }
2706 return pLogger;
2707 }
2708 RT_EXPORT_SYMBOL(RTLogDefaultInstanceEx);
2709
2710
2711 /**
2712 * Common worker for RTLogGetDefaultInstance and RTLogGetDefaultInstanceEx.
2713 */
2714 DECL_FORCE_INLINE(PRTLOGGER) rtLogGetDefaultInstanceCommon(void)
2715 {
2716 #ifdef IN_RC
2717 return &g_Logger;
2718 #else
2719 # ifdef IN_RING0
2720 /*
2721 * Check per thread loggers first.
2722 */
2723 if (g_cPerThreadLoggers)
2724 {
2725 const RTNATIVETHREAD Self = RTThreadNativeSelf();
2726 int32_t i = RT_ELEMENTS(g_aPerThreadLoggers);
2727 while (i-- > 0)
2728 if (g_aPerThreadLoggers[i].NativeThread == Self)
2729 return g_aPerThreadLoggers[i].pLogger;
2730 }
2731 # endif /* IN_RING0 */
2732
2733 return g_pLogger;
2734 #endif
2735 }
2736
2737
2738 RTDECL(PRTLOGGER) RTLogGetDefaultInstance(void)
2739 {
2740 return rtLogGetDefaultInstanceCommon();
2741 }
2742 RT_EXPORT_SYMBOL(RTLogGetDefaultInstance);
2743
2744
2745 RTDECL(PRTLOGGER) RTLogGetDefaultInstanceEx(uint32_t fFlagsAndGroup)
2746 {
2747 PRTLOGGER pLogger = rtLogGetDefaultInstanceCommon();
2748 if (pLogger)
2749 {
2750 if (pLogger->fFlags & RTLOGFLAGS_DISABLED)
2751 pLogger = NULL;
2752 else
2753 {
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)))
2759 pLogger = NULL;
2760 }
2761 }
2762 return pLogger;
2763 }
2764 RT_EXPORT_SYMBOL(RTLogGetDefaultInstanceEx);
2765
2766
2767 #ifndef IN_RC
2768 /**
2769 * Sets the default logger instance.
2770 *
2771 * @returns iprt status code.
2772 * @param pLogger The new default logger instance.
2773 */
2774 RTDECL(PRTLOGGER) RTLogSetDefaultInstance(PRTLOGGER pLogger)
2775 {
2776 return ASMAtomicXchgPtrT(&g_pLogger, pLogger, PRTLOGGER);
2777 }
2778 RT_EXPORT_SYMBOL(RTLogSetDefaultInstance);
2779 #endif /* !IN_RC */
2780
2781
2782 #ifdef IN_RING0
2783 /**
2784 * Changes the default logger instance for the current thread.
2785 *
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.
2792 */
2793 RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey)
2794 {
2795 int rc;
2796 RTNATIVETHREAD Self = RTThreadNativeSelf();
2797 if (pLogger)
2798 {
2799 int32_t i;
2800 unsigned j;
2801
2802 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
2803
2804 /*
2805 * Iterate the table to see if there is already an entry for this thread.
2806 */
2807 i = RT_ELEMENTS(g_aPerThreadLoggers);
2808 while (i-- > 0)
2809 if (g_aPerThreadLoggers[i].NativeThread == Self)
2810 {
2811 ASMAtomicWritePtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
2812 g_aPerThreadLoggers[i].pLogger = pLogger;
2813 return VINF_SUCCESS;
2814 }
2815
2816 /*
2817 * Allocate a new table entry.
2818 */
2819 i = ASMAtomicIncS32(&g_cPerThreadLoggers);
2820 if (i > (int32_t)RT_ELEMENTS(g_aPerThreadLoggers))
2821 {
2822 ASMAtomicDecS32(&g_cPerThreadLoggers);
2823 return VERR_BUFFER_OVERFLOW; /* horrible error code! */
2824 }
2825
2826 for (j = 0; j < 10; j++)
2827 {
2828 i = RT_ELEMENTS(g_aPerThreadLoggers);
2829 while (i-- > 0)
2830 {
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))
2834 {
2835 ASMAtomicWritePtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
2836 ASMAtomicWritePtr(&g_aPerThreadLoggers[i].pLogger, pLogger);
2837 return VINF_SUCCESS;
2838 }
2839 }
2840 }
2841
2842 ASMAtomicDecS32(&g_cPerThreadLoggers);
2843 rc = VERR_INTERNAL_ERROR;
2844 }
2845 else
2846 {
2847 /*
2848 * Search the array for the current thread.
2849 */
2850 int32_t i = RT_ELEMENTS(g_aPerThreadLoggers);
2851 while (i-- > 0)
2852 if ( g_aPerThreadLoggers[i].NativeThread == Self
2853 || g_aPerThreadLoggers[i].uKey == uKey)
2854 {
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);
2859 }
2860
2861 rc = VINF_SUCCESS;
2862 }
2863 return rc;
2864 }
2865 RT_EXPORT_SYMBOL(RTLogSetDefaultInstanceThread);
2866 #endif /* IN_RING0 */
2867
2868
2869 /**
2870 * Write to a logger instance.
2871 *
2872 * @param pLogger Pointer to logger instance.
2873 * @param pszFormat Format string.
2874 * @param args Format arguments.
2875 */
2876 RTDECL(void) RTLogLoggerV(PRTLOGGER pLogger, const char *pszFormat, va_list args)
2877 {
2878 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
2879 }
2880 RT_EXPORT_SYMBOL(RTLogLoggerV);
2881
2882
2883 /**
2884 * Write to a logger instance.
2885 *
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.
2888 *
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.
2896 */
2897 RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
2898 {
2899 int rc;
2900
2901 /*
2902 * A NULL logger means default instance.
2903 */
2904 if (!pLogger)
2905 {
2906 pLogger = RTLogDefaultInstance();
2907 if (!pLogger)
2908 return;
2909 }
2910
2911 /*
2912 * Validate and correct iGroup.
2913 */
2914 if (iGroup != ~0U && iGroup >= pLogger->cGroups)
2915 iGroup = 0;
2916
2917 /*
2918 * If no output, then just skip it.
2919 */
2920 if ( (pLogger->fFlags & RTLOGFLAGS_DISABLED)
2921 #ifndef IN_RC
2922 || !pLogger->fDestFlags
2923 #endif
2924 || !pszFormat || !*pszFormat)
2925 return;
2926 if ( iGroup != ~0U
2927 && (pLogger->afGroups[iGroup] & (fFlags | RTLOGGRPFLAGS_ENABLED)) != (fFlags | RTLOGGRPFLAGS_ENABLED))
2928 return;
2929
2930 /*
2931 * Acquire logger instance sem.
2932 */
2933 rc = rtlogLock(pLogger);
2934 if (RT_FAILURE(rc))
2935 {
2936 #ifdef IN_RING0
2937 if (pLogger->fDestFlags & ~RTLOGDEST_FILE)
2938 rtR0LogLoggerExFallback(pLogger->fDestFlags, pLogger->fFlags, pLogger->pInt, pszFormat, args);
2939 #endif
2940 return;
2941 }
2942
2943 /*
2944 * Check restrictions and call worker.
2945 */
2946 #ifndef IN_RC
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 ))
2951 {
2952 uint32_t cEntries = pLogger->pInt->pacEntriesPerGroup[iGroup];
2953 if (cEntries > pLogger->pInt->cMaxEntriesPerGroup)
2954 pLogger->pInt->pacEntriesPerGroup[iGroup] = cEntries - 1;
2955 else
2956 {
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);
2962 else
2963 rtlogLoggerExFLocked(pLogger, fFlags, iGroup, "%u messages from group #%u, muting it.\n",
2964 cEntries, iGroup);
2965 }
2966 }
2967 else
2968 #endif
2969 rtlogLoggerExVLocked(pLogger, fFlags, iGroup, pszFormat, args);
2970
2971 /*
2972 * Release the semaphore.
2973 */
2974 rtlogUnlock(pLogger);
2975 }
2976 RT_EXPORT_SYMBOL(RTLogLoggerExV);
2977
2978
2979 #ifdef IN_RING0
2980 /**
2981 * For rtR0LogLoggerExFallbackOutput and rtR0LogLoggerExFallbackFlush.
2982 */
2983 typedef struct RTR0LOGLOGGERFALLBACK
2984 {
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;
2997
2998
2999 /**
3000 * Flushes the fallback buffer.
3001 *
3002 * @param pThis The scratch buffer.
3003 */
3004 static void rtR0LogLoggerExFallbackFlush(PRTR0LOGLOGGERFALLBACK pThis)
3005 {
3006 if (!pThis->offScratch)
3007 return;
3008
3009 if ( (pThis->fDestFlags & RTLOGDEST_RINGBUF)
3010 && pThis->pInt
3011 && pThis->pInt->pszRingBuf /* paranoia */)
3012 rtLogRingBufWrite(pThis->pInt, pThis->achScratch, pThis->offScratch);
3013 else
3014 {
3015 if (pThis->fDestFlags & RTLOGDEST_USER)
3016 RTLogWriteUser(pThis->achScratch, pThis->offScratch);
3017
3018 if (pThis->fDestFlags & RTLOGDEST_DEBUGGER)
3019 RTLogWriteDebugger(pThis->achScratch, pThis->offScratch);
3020
3021 if (pThis->fDestFlags & RTLOGDEST_STDOUT)
3022 RTLogWriteStdOut(pThis->achScratch, pThis->offScratch);
3023
3024 if (pThis->fDestFlags & RTLOGDEST_STDERR)
3025 RTLogWriteStdErr(pThis->achScratch, pThis->offScratch);
3026
3027 # ifndef LOG_NO_COM
3028 if (pThis->fDestFlags & RTLOGDEST_COM)
3029 RTLogWriteCom(pThis->achScratch, pThis->offScratch);
3030 # endif
3031 }
3032
3033 /* empty the buffer. */
3034 pThis->offScratch = 0;
3035 }
3036
3037
3038 /**
3039 * Callback for RTLogFormatV used by rtR0LogLoggerExFallback.
3040 * See PFNLOGOUTPUT() for details.
3041 */
3042 static DECLCALLBACK(size_t) rtR0LogLoggerExFallbackOutput(void *pv, const char *pachChars, size_t cbChars)
3043 {
3044 PRTR0LOGLOGGERFALLBACK pThis = (PRTR0LOGLOGGERFALLBACK)pv;
3045 if (cbChars)
3046 {
3047 size_t cbRet = 0;
3048 for (;;)
3049 {
3050 /* how much */
3051 uint32_t cb = sizeof(pThis->achScratch) - pThis->offScratch - 1; /* minus 1 - for the string terminator. */
3052 if (cb > cbChars)
3053 cb = (uint32_t)cbChars;
3054
3055 /* copy */
3056 memcpy(&pThis->achScratch[pThis->offScratch], pachChars, cb);
3057
3058 /* advance */
3059 pThis->offScratch += cb;
3060 cbRet += cb;
3061 cbChars -= cb;
3062
3063 /* done? */
3064 if (cbChars <= 0)
3065 return cbRet;
3066
3067 pachChars += cb;
3068
3069 /* flush */
3070 pThis->achScratch[pThis->offScratch] = '\0';
3071 rtR0LogLoggerExFallbackFlush(pThis);
3072 }
3073
3074 /* won't ever get here! */
3075 }
3076 else
3077 {
3078 /*
3079 * Termination call, flush the log.
3080 */
3081 pThis->achScratch[pThis->offScratch] = '\0';
3082 rtR0LogLoggerExFallbackFlush(pThis);
3083 return 0;
3084 }
3085 }
3086
3087
3088 /**
3089 * Ring-0 fallback for cases where we're unable to grab the lock.
3090 *
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.
3096 *
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.
3102 */
3103 static void rtR0LogLoggerExFallback(uint32_t fDestFlags, uint32_t fFlags, PRTLOGGERINTERNAL pInt,
3104 const char *pszFormat, va_list va)
3105 {
3106 RTR0LOGLOGGERFALLBACK This;
3107 This.fDestFlags = fDestFlags;
3108 This.pInt = pInt;
3109
3110 /* fallback indicator. */
3111 This.offScratch = 2;
3112 This.achScratch[0] = '[';
3113 This.achScratch[1] = 'F';
3114
3115 /* selected prefixes */
3116 if (fFlags & RTLOGFLAGS_PREFIX_PID)
3117 {
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);
3121 }
3122 if (fFlags & RTLOGFLAGS_PREFIX_TID)
3123 {
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);
3127 }
3128
3129 This.achScratch[This.offScratch++] = ']';
3130 This.achScratch[This.offScratch++] = ' ';
3131
3132 RTLogFormatV(rtR0LogLoggerExFallbackOutput, &This, pszFormat, va);
3133 }
3134 #endif /* IN_RING0 */
3135
3136
3137 /**
3138 * vprintf like function for writing to the default log.
3139 *
3140 * @param pszFormat Printf like format string.
3141 * @param va Optional arguments as specified in pszFormat.
3142 *
3143 * @remark The API doesn't support formatting of floating point numbers at the moment.
3144 */
3145 RTDECL(void) RTLogPrintfV(const char *pszFormat, va_list va)
3146 {
3147 RTLogLoggerV(NULL, pszFormat, va);
3148 }
3149 RT_EXPORT_SYMBOL(RTLogPrintfV);
3150
3151
3152 /**
3153 * Dumper vprintf-like function outputting to a logger.
3154 *
3155 * @param pvUser Pointer to the logger instance to use, NULL for
3156 * default instance.
3157 * @param pszFormat Format string.
3158 * @param va Format arguments.
3159 */
3160 RTDECL(void) RTLogDumpPrintfV(void *pvUser, const char *pszFormat, va_list va)
3161 {
3162 RTLogLoggerV((PRTLOGGER)pvUser, pszFormat, va);
3163 }
3164 RT_EXPORT_SYMBOL(RTLogDumpPrintfV);
3165
3166
3167 #ifdef IN_RING3
3168
3169 /**
3170 * Opens/creates the log file.
3171 *
3172 * @param pLogger The logger instance to update. NULL is not allowed!
3173 * @param pErrInfo Where to return extended error information.
3174 * Optional.
3175 */
3176 static int rtlogFileOpen(PRTLOGGER pLogger, PRTERRINFO pErrInfo)
3177 {
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;
3181 else
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;
3187
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))
3192 {
3193 RTThreadSleep(g_acMsLogBackoff[cBackoff++]);
3194 rc = RTFileOpen(&pLogger->pInt->hFile, pLogger->pInt->szFilename, fOpen);
3195 }
3196 if (RT_SUCCESS(rc))
3197 {
3198 rc = RTFileGetSize(pLogger->pInt->hFile, &pLogger->pInt->cbHistoryFileWritten);
3199 if (RT_FAILURE(rc))
3200 {
3201 /* Don't complain if this fails, assume the file is empty. */
3202 pLogger->pInt->cbHistoryFileWritten = 0;
3203 rc = VINF_SUCCESS;
3204 }
3205 }
3206 else
3207 {
3208 pLogger->pInt->hFile = NIL_RTFILE;
3209 RTErrInfoSetF(pErrInfo, rc, N_("could not open file '%s' (fOpen=%#x)"), pLogger->pInt->szFilename, fOpen);
3210 }
3211 return rc;
3212 }
3213
3214
3215 /**
3216 * Closes, rotates and opens the log files if necessary.
3217 *
3218 * Used by the rtlogFlush() function as well as RTLogCreateExV.
3219 *
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
3224 * being called.
3225 * @param pErrInfo Where to return extended error information. Optional.
3226 */
3227 static void rtlogRotate(PRTLOGGER pLogger, uint32_t uTimeSlot, bool fFirst, PRTERRINFO pErrInfo)
3228 {
3229 /* Suppress rotating empty log files simply because the time elapsed. */
3230 if (RT_UNLIKELY(!pLogger->pInt->cbHistoryFileWritten))
3231 pLogger->pInt->uHistoryTimeSlotStart = uTimeSlot;
3232
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))
3236 return;
3237
3238 /*
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.
3242 */
3243 uint32_t const fSavedFlags = pLogger->fFlags;
3244 pLogger->fFlags |= RTLOGFLAGS_DISABLED;
3245
3246 /*
3247 * Disable log rotation temporarily, otherwise with extreme settings and
3248 * chatty phase logging we could run into endless rotation.
3249 */
3250 uint32_t const cSavedHistory = pLogger->pInt->cHistory;
3251 pLogger->pInt->cHistory = 0;
3252
3253 /*
3254 * Close the old log file.
3255 */
3256 if (pLogger->pInt->hFile != NIL_RTFILE)
3257 {
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)
3262 {
3263 uint32_t fODestFlags = pLogger->fDestFlags;
3264 pLogger->fDestFlags &= RTLOGDEST_FILE;
3265 pLogger->pInt->pfnPhase(pLogger, RTLOGPHASE_PREROTATE, rtlogPhaseMsgLocked);
3266 pLogger->fDestFlags = fODestFlags;
3267 }
3268 RTFileClose(pLogger->pInt->hFile);
3269 pLogger->pInt->hFile = NIL_RTFILE;
3270 }
3271
3272 if (cSavedHistory)
3273 {
3274 /*
3275 * Rotate the log files.
3276 */
3277 for (uint32_t i = cSavedHistory - 1; i + 1 > 0; i--)
3278 {
3279 char szOldName[sizeof(pLogger->pInt->szFilename) + 32];
3280 if (i > 0)
3281 RTStrPrintf(szOldName, sizeof(szOldName), "%s.%u", pLogger->pInt->szFilename, i);
3282 else
3283 RTStrCopy(szOldName, sizeof(szOldName), pLogger->pInt->szFilename);
3284
3285 char szNewName[sizeof(pLogger->pInt->szFilename) + 32];
3286 RTStrPrintf(szNewName, sizeof(szNewName), "%s.%u", pLogger->pInt->szFilename, i + 1);
3287
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))
3292 {
3293 RTThreadSleep(g_acMsLogBackoff[cBackoff++]);
3294 rc = RTFileRename(szOldName, szNewName, RTFILEMOVE_FLAGS_REPLACE);
3295 }
3296
3297 if (rc == VERR_FILE_NOT_FOUND)
3298 RTFileDelete(szNewName);
3299 }
3300
3301 /*
3302 * Delete excess log files.
3303 */
3304 for (uint32_t i = cSavedHistory + 1; ; i++)
3305 {
3306 char szExcessName[sizeof(pLogger->pInt->szFilename) + 32];
3307 RTStrPrintf(szExcessName, sizeof(szExcessName), "%s.%u", pLogger->pInt->szFilename, i);
3308 int rc = RTFileDelete(szExcessName);
3309 if (RT_FAILURE(rc))
3310 break;
3311 }
3312 }
3313
3314 /*
3315 * Update logger state and create new log file.
3316 */
3317 pLogger->pInt->cbHistoryFileWritten = 0;
3318 pLogger->pInt->uHistoryTimeSlotStart = uTimeSlot;
3319 rtlogFileOpen(pLogger, pErrInfo);
3320
3321 /*
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.
3325 */
3326 if (pLogger->pInt->pfnPhase && !fFirst)
3327 {
3328 uint32_t const fSavedDestFlags = pLogger->fDestFlags;
3329 pLogger->fDestFlags &= RTLOGDEST_FILE;
3330 pLogger->pInt->pfnPhase(pLogger, RTLOGPHASE_POSTROTATE, rtlogPhaseMsgLocked);
3331 pLogger->fDestFlags = fSavedDestFlags;
3332 }
3333
3334 /* Restore saved values. */
3335 pLogger->pInt->cHistory = cSavedHistory;
3336 pLogger->fFlags = fSavedFlags;
3337 }
3338
3339
3340 /**
3341 * Worker for RTLogCreateExV and RTLogClearFileDelayFlag.
3342 *
3343 * This will later be used to reopen the file by RTLogDestinations.
3344 *
3345 * @returns IPRT status code.
3346 * @param pLogger The logger.
3347 * @param pErrInfo Where to return extended error information.
3348 * Optional.
3349 */
3350 static int rtR3LogOpenFileDestination(PRTLOGGER pLogger, PRTERRINFO pErrInfo)
3351 {
3352 int rc;
3353 if (pLogger->fFlags & RTLOGFLAGS_APPEND)
3354 {
3355 rc = rtlogFileOpen(pLogger, pErrInfo);
3356
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);
3360 }
3361 else
3362 {
3363 /* Force rotation if it is configured. */
3364 pLogger->pInt->cbHistoryFileWritten = UINT64_MAX;
3365 rtlogRotate(pLogger, 0, true /* fFirst */, pErrInfo);
3366
3367 /* If the file is not open then rotation is not set up. */
3368 if (pLogger->pInt->hFile == NIL_RTFILE)
3369 {
3370 pLogger->pInt->cbHistoryFileWritten = 0;
3371 rc = rtlogFileOpen(pLogger, pErrInfo);
3372 }
3373 else
3374 rc = VINF_SUCCESS;
3375 }
3376 return rc;
3377 }
3378
3379 #endif /* IN_RING3 */
3380
3381
3382 /**
3383 * Writes the buffer to the given log device without checking for buffered
3384 * data or anything.
3385 *
3386 * Used by the RTLogFlush() function.
3387 *
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.
3390 */
3391 static void rtlogFlush(PRTLOGGER pLogger, bool fNeedSpace)
3392 {
3393 uint32_t const cchScratch = pLogger->offScratch;
3394 if (cchScratch == 0)
3395 return; /* nothing to flush. */
3396 NOREF(fNeedSpace);
3397
3398 #ifndef IN_RC
3399 /*
3400 * If the ring buffer is active, the other destinations are only written
3401 * to when the ring buffer is flushed by RTLogFlush().
3402 */
3403 if ( (pLogger->fDestFlags & RTLOGDEST_RINGBUF)
3404 && pLogger->pInt
3405 && pLogger->pInt->pszRingBuf /* paraoia */)
3406 {
3407 rtLogRingBufWrite(pLogger->pInt, pLogger->achScratch, pLogger->offScratch);
3408 pLogger->offScratch = 0; /* empty the buffer. */
3409 }
3410 /*
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.
3413 */
3414 else
3415 # ifdef IN_RING3
3416 if (!(pLogger->fDestFlags & RTLOGDEST_F_DELAY_FILE))
3417 # endif
3418 #endif
3419 {
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';
3424 else
3425 AssertFailed();
3426
3427 #ifndef IN_RC
3428 if (pLogger->fDestFlags & RTLOGDEST_USER)
3429 RTLogWriteUser(pLogger->achScratch, cchScratch);
3430
3431 if (pLogger->fDestFlags & RTLOGDEST_DEBUGGER)
3432 RTLogWriteDebugger(pLogger->achScratch, cchScratch);
3433
3434 # ifdef IN_RING3
3435 if ((pLogger->fDestFlags & (RTLOGDEST_FILE | RTLOGDEST_RINGBUF)) == RTLOGDEST_FILE)
3436 {
3437 if (pLogger->pInt->hFile != NIL_RTFILE)
3438 {
3439 RTFileWrite(pLogger->pInt->hFile, pLogger->achScratch, cchScratch, NULL);
3440 if (pLogger->fFlags & RTLOGFLAGS_FLUSH)
3441 RTFileFlush(pLogger->pInt->hFile);
3442 }
3443 if (pLogger->pInt->cHistory)
3444 pLogger->pInt->cbHistoryFileWritten += cchScratch;
3445 }
3446 # endif
3447
3448 if (pLogger->fDestFlags & RTLOGDEST_STDOUT)
3449 RTLogWriteStdOut(pLogger->achScratch, cchScratch);
3450
3451 if (pLogger->fDestFlags & RTLOGDEST_STDERR)
3452 RTLogWriteStdErr(pLogger->achScratch, cchScratch);
3453
3454 # if (defined(IN_RING0) || defined(IN_RC)) && !defined(LOG_NO_COM)
3455 if (pLogger->fDestFlags & RTLOGDEST_COM)
3456 RTLogWriteCom(pLogger->achScratch, cchScratch);
3457 # endif
3458 #endif /* !IN_RC */
3459
3460 #ifdef IN_RC
3461 if (pLogger->pfnFlush)
3462 pLogger->pfnFlush(pLogger);
3463 #else
3464 if (pLogger->pInt->pfnFlush)
3465 pLogger->pInt->pfnFlush(pLogger);
3466 #endif
3467
3468 /* empty the buffer. */
3469 pLogger->offScratch = 0;
3470
3471 #ifdef IN_RING3
3472 /*
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.
3476 */
3477 if ( (pLogger->fDestFlags & RTLOGDEST_FILE)
3478 && pLogger->pInt->cHistory)
3479 rtlogRotate(pLogger, RTTimeProgramSecTS() / pLogger->pInt->cSecsHistoryTimeSlot, false /*fFirst*/, NULL /*pErrInfo*/);
3480 #endif
3481 }
3482 #ifdef IN_RING3
3483 else
3484 {
3485 /*
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.
3488 */
3489 uint32_t offHalf = sizeof(pLogger->achScratch) / 2;
3490 if (cchScratch > offHalf)
3491 {
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';
3500
3501 pLogger->offScratch = offHalf;
3502 }
3503 }
3504 #endif
3505 }
3506
3507
3508 /**
3509 * Callback for RTLogFormatV which writes to the com port.
3510 * See PFNLOGOUTPUT() for details.
3511 */
3512 static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars)
3513 {
3514 PRTLOGGER pLogger = (PRTLOGGER)pv;
3515 if (cbChars)
3516 {
3517 size_t cbRet = 0;
3518 for (;;)
3519 {
3520 #if defined(DEBUG) && defined(IN_RING3)
3521 /* sanity */
3522 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
3523 {
3524 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
3525 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
3526 AssertBreakpoint(); AssertBreakpoint();
3527 }
3528 #endif
3529
3530 /* how much */
3531 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
3532 if (cb > cbChars)
3533 cb = cbChars;
3534
3535 /* copy */
3536 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
3537
3538 /* advance */
3539 pLogger->offScratch += (uint32_t)cb;
3540 cbRet += cb;
3541 cbChars -= cb;
3542
3543 /* done? */
3544 if (cbChars <= 0)
3545 return cbRet;
3546
3547 pachChars += cb;
3548
3549 /* flush */
3550 rtlogFlush(pLogger, true /*fNeedSpace*/);
3551 }
3552
3553 /* won't ever get here! */
3554 }
3555 else
3556 {
3557 /*
3558 * Termination call.
3559 * There's always space for a terminator, and it's not counted.
3560 */
3561 pLogger->achScratch[pLogger->offScratch] = '\0';
3562 return 0;
3563 }
3564 }
3565
3566
3567 /**
3568 * stpncpy implementation for use in rtLogOutputPrefixed w/ padding.
3569 *
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
3574 * the string.
3575 * @param cchMinWidth The minimum field with, padd with spaces to
3576 * reach this.
3577 */
3578 DECLINLINE(char *) rtLogStPNCpyPad(char *pszDst, const char *pszSrc, size_t cchSrcMax, size_t cchMinWidth)
3579 {
3580 size_t cchSrc = 0;
3581 if (pszSrc)
3582 {
3583 cchSrc = strlen(pszSrc);
3584 if (cchSrc > cchSrcMax)
3585 cchSrc = cchSrcMax;
3586
3587 memcpy(pszDst, pszSrc, cchSrc);
3588 pszDst += cchSrc;
3589 }
3590 do
3591 *pszDst++ = ' ';
3592 while (cchSrc++ < cchMinWidth);
3593
3594 return pszDst;
3595 }
3596
3597
3598
3599 /**
3600 * Callback for RTLogFormatV which writes to the logger instance.
3601 * This version supports prefixes.
3602 *
3603 * See PFNLOGOUTPUT() for details.
3604 */
3605 static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars)
3606 {
3607 PRTLOGOUTPUTPREFIXEDARGS pArgs = (PRTLOGOUTPUTPREFIXEDARGS)pv;
3608 PRTLOGGER pLogger = pArgs->pLogger;
3609 if (cbChars)
3610 {
3611 size_t cbRet = 0;
3612 for (;;)
3613 {
3614 uint32_t offScratch = pLogger->offScratch;
3615 size_t cb = sizeof(pLogger->achScratch) - offScratch - 1;
3616 const char *pszNewLine;
3617 char *psz;
3618 #ifdef IN_RC
3619 bool *pfPendingPrefix = &pLogger->fPendingPrefix;
3620 #else
3621 bool *pfPendingPrefix = &pLogger->pInt->fPendingPrefix;
3622 #endif
3623
3624 /*
3625 * Pending prefix?
3626 */
3627 if (*pfPendingPrefix)
3628 {
3629 *pfPendingPrefix = false;
3630
3631 #if defined(DEBUG) && defined(IN_RING3)
3632 /* sanity */
3633 if (offScratch >= sizeof(pLogger->achScratch))
3634 {
3635 fprintf(stderr, "offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
3636 offScratch, (unsigned)sizeof(pLogger->achScratch));
3637 AssertBreakpoint(); AssertBreakpoint();
3638 }
3639 #endif
3640
3641 /*
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.
3644 */
3645 if (cb < 256 + 16)
3646 {
3647 rtlogFlush(pLogger, true /*fNeedSpace*/);
3648 offScratch = pLogger->offScratch;
3649 cb = sizeof(pLogger->achScratch) - offScratch - 1;
3650 }
3651
3652 /*
3653 * Write the prefixes.
3654 * psz is pointing to the current position.
3655 */
3656 psz = &pLogger->achScratch[offScratch];
3657 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TS)
3658 {
3659 uint64_t u64 = RTTimeNanoTS();
3660 int iBase = 16;
3661 unsigned int fFlags = RTSTR_F_ZEROPAD;
3662 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
3663 {
3664 iBase = 10;
3665 fFlags = 0;
3666 }
3667 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
3668 {
3669 static volatile uint64_t s_u64LastTs;
3670 uint64_t u64DiffTs = u64 - s_u64LastTs;
3671 s_u64LastTs = u64;
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;
3676 }
3677 /* 1E15 nanoseconds = 11 days */
3678 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags);
3679 *psz++ = ' ';
3680 }
3681 #define CCH_PREFIX_01 0 + 17
3682
3683 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TSC)
3684 {
3685 #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
3686 uint64_t u64 = ASMReadTSC();
3687 #else
3688 uint64_t u64 = RTTimeNanoTS();
3689 #endif
3690 int iBase = 16;
3691 unsigned int fFlags = RTSTR_F_ZEROPAD;
3692 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
3693 {
3694 iBase = 10;
3695 fFlags = 0;
3696 }
3697 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
3698 {
3699 static volatile uint64_t s_u64LastTsc;
3700 int64_t i64DiffTsc = u64 - s_u64LastTsc;
3701 s_u64LastTsc = u64;
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;
3706 }
3707 /* 1E15 ticks at 4GHz = 69 hours */
3708 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags);
3709 *psz++ = ' ';
3710 }
3711 #define CCH_PREFIX_02 CCH_PREFIX_01 + 17
3712
3713 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_MS_PROG)
3714 {
3715 #if defined(IN_RING3) || defined(IN_RC)
3716 uint64_t u64 = RTTimeProgramMilliTS();
3717 #else
3718 uint64_t u64 = 0;
3719 #endif
3720 /* 1E8 milliseconds = 27 hours */
3721 psz += RTStrFormatNumber(psz, u64, 10, 9, 0, RTSTR_F_ZEROPAD);
3722 *psz++ = ' ';
3723 }
3724 #define CCH_PREFIX_03 CCH_PREFIX_02 + 21
3725
3726 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME)
3727 {
3728 #if defined(IN_RING3) || defined(IN_RING0)
3729 RTTIMESPEC TimeSpec;
3730 RTTIME Time;
3731 RTTimeExplode(&Time, RTTimeNow(&TimeSpec));
3732 psz += RTStrFormatNumber(psz, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD);
3733 *psz++ = ':';
3734 psz += RTStrFormatNumber(psz, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD);
3735 *psz++ = ':';
3736 psz += RTStrFormatNumber(psz, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD);
3737 *psz++ = '.';
3738 psz += RTStrFormatNumber(psz, Time.u32Nanosecond / 1000, 10, 6, 0, RTSTR_F_ZEROPAD);
3739 *psz++ = ' ';
3740 #else
3741 memset(psz, ' ', 16);
3742 psz += 16;
3743 #endif
3744 }
3745 #define CCH_PREFIX_04 CCH_PREFIX_03 + (3+1+3+1+3+1+7+1)
3746
3747 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME_PROG)
3748 {
3749
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);
3753 *psz++ = ':';
3754 uint32_t u32 = (uint32_t)(u64 % RT_US_1HOUR);
3755 psz += RTStrFormatNumber(psz, u32 / RT_US_1MIN, 10, 2, 0, RTSTR_F_ZEROPAD);
3756 *psz++ = ':';
3757 u32 %= RT_US_1MIN;
3758
3759 psz += RTStrFormatNumber(psz, u32 / RT_US_1SEC, 10, 2, 0, RTSTR_F_ZEROPAD);
3760 *psz++ = '.';
3761 psz += RTStrFormatNumber(psz, u32 % RT_US_1SEC, 10, 6, 0, RTSTR_F_ZEROPAD);
3762 *psz++ = ' ';
3763 #else
3764 memset(psz, ' ', 16);
3765 psz += 16;
3766 #endif
3767 }
3768 #define CCH_PREFIX_05 CCH_PREFIX_04 + (9+1+2+1+2+1+6+1)
3769
3770 # if 0
3771 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_DATETIME)
3772 {
3773 char szDate[32];
3774 RTTIMESPEC Time;
3775 RTTimeSpecToString(RTTimeNow(&Time), szDate, sizeof(szDate));
3776 size_t cch = strlen(szDate);
3777 memcpy(psz, szDate, cch);
3778 psz += cch;
3779 *psz++ = ' ';
3780 }
3781 # define CCH_PREFIX_06 CCH_PREFIX_05 + 32
3782 # else
3783 # define CCH_PREFIX_06 CCH_PREFIX_05 + 0
3784 # endif
3785
3786 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_PID)
3787 {
3788 #ifndef IN_RC
3789 RTPROCESS Process = RTProcSelf();
3790 #else
3791 RTPROCESS Process = NIL_RTPROCESS;
3792 #endif
3793 psz += RTStrFormatNumber(psz, Process, 16, sizeof(RTPROCESS) * 2, 0, RTSTR_F_ZEROPAD);
3794 *psz++ = ' ';
3795 }
3796 #define CCH_PREFIX_07 CCH_PREFIX_06 + 9
3797
3798 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TID)
3799 {
3800 #ifndef IN_RC
3801 RTNATIVETHREAD Thread = RTThreadNativeSelf();
3802 #else
3803 RTNATIVETHREAD Thread = NIL_RTNATIVETHREAD;
3804 #endif
3805 psz += RTStrFormatNumber(psz, Thread, 16, sizeof(RTNATIVETHREAD) * 2, 0, RTSTR_F_ZEROPAD);
3806 *psz++ = ' ';
3807 }
3808 #define CCH_PREFIX_08 CCH_PREFIX_07 + 17
3809
3810 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_THREAD)
3811 {
3812 #ifdef IN_RING3
3813 const char *pszName = RTThreadSelfName();
3814 #elif defined IN_RC
3815 const char *pszName = "EMT-RC";
3816 #else
3817 const char *pszName = "R0";
3818 #endif
3819 psz = rtLogStPNCpyPad(psz, pszName, 16, 8);
3820 }
3821 #define CCH_PREFIX_09 CCH_PREFIX_08 + 17
3822
3823 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_CPUID)
3824 {
3825 #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
3826 const uint8_t idCpu = ASMGetApicId();
3827 #else
3828 const RTCPUID idCpu = RTMpCpuId();
3829 #endif
3830 psz += RTStrFormatNumber(psz, idCpu, 16, sizeof(idCpu) * 2, 0, RTSTR_F_ZEROPAD);
3831 *psz++ = ' ';
3832 }
3833 #define CCH_PREFIX_10 CCH_PREFIX_09 + 17
3834
3835 #ifndef IN_RC
3836 if ( (pLogger->fFlags & RTLOGFLAGS_PREFIX_CUSTOM)
3837 && pLogger->pInt->pfnPrefix)
3838 {
3839 psz += pLogger->pInt->pfnPrefix(pLogger, psz, 31, pLogger->pInt->pvPrefixUserArg);
3840 *psz++ = ' '; /* +32 */
3841 }
3842 #endif
3843 #define CCH_PREFIX_11 CCH_PREFIX_10 + 32
3844
3845 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_LOCK_COUNTS)
3846 {
3847 #ifdef IN_RING3 /** @todo implement these counters in ring-0 too? */
3848 RTTHREAD Thread = RTThreadSelf();
3849 if (Thread != NIL_RTTHREAD)
3850 {
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);
3856 *psz++ = '/';
3857 psz += RTStrFormatNumber(psz, cWriteLocks, 16, 1, 0, RTSTR_F_ZEROPAD);
3858 }
3859 else
3860 #endif
3861 {
3862 *psz++ = '?';
3863 *psz++ = '/';
3864 *psz++ = '?';
3865 }
3866 *psz++ = ' ';
3867 }
3868 #define CCH_PREFIX_12 CCH_PREFIX_11 + 8
3869
3870 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG_NO)
3871 {
3872 psz += RTStrFormatNumber(psz, pArgs->fFlags, 16, 8, 0, RTSTR_F_ZEROPAD);
3873 *psz++ = ' ';
3874 }
3875 #define CCH_PREFIX_13 CCH_PREFIX_12 + 9
3876
3877 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG)
3878 {
3879 #ifdef IN_RING3
3880 const char *pszGroup = pArgs->iGroup != ~0U ? pLogger->pInt->papszGroups[pArgs->iGroup] : NULL;
3881 #else
3882 const char *pszGroup = NULL;
3883 #endif
3884 psz = rtLogStPNCpyPad(psz, pszGroup, 16, 8);
3885 }
3886 #define CCH_PREFIX_14 CCH_PREFIX_13 + 17
3887
3888 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP_NO)
3889 {
3890 if (pArgs->iGroup != ~0U)
3891 {
3892 psz += RTStrFormatNumber(psz, pArgs->iGroup, 16, 3, 0, RTSTR_F_ZEROPAD);
3893 *psz++ = ' ';
3894 }
3895 else
3896 {
3897 memcpy(psz, "-1 ", sizeof("-1 ") - 1);
3898 psz += sizeof("-1 ") - 1;
3899 } /* +9 */
3900 }
3901 #define CCH_PREFIX_15 CCH_PREFIX_14 + 9
3902
3903 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP)
3904 {
3905 const unsigned fGrp = pLogger->afGroups[pArgs->iGroup != ~0U ? pArgs->iGroup : 0];
3906 const char *pszGroup;
3907 size_t cch;
3908 switch (pArgs->fFlags & fGrp)
3909 {
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;
3927 }
3928 psz = rtLogStPNCpyPad(psz, pszGroup, 16, 8);
3929 }
3930 #define CCH_PREFIX_16 CCH_PREFIX_15 + 17
3931
3932 #define CCH_PREFIX ( CCH_PREFIX_16 )
3933 { AssertCompile(CCH_PREFIX < 256); }
3934
3935 /*
3936 * Done, figure what we've used and advance the buffer and free size.
3937 */
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;
3942 }
3943 else if (cb <= 0)
3944 {
3945 rtlogFlush(pLogger, true /*fNeedSpace*/);
3946 offScratch = pLogger->offScratch;
3947 cb = sizeof(pLogger->achScratch) - offScratch - 1;
3948 }
3949
3950 #if defined(DEBUG) && defined(IN_RING3)
3951 /* sanity */
3952 if (offScratch >= sizeof(pLogger->achScratch))
3953 {
3954 fprintf(stderr, "offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
3955 offScratch, (unsigned)sizeof(pLogger->achScratch));
3956 AssertBreakpoint(); AssertBreakpoint();
3957 }
3958 #endif
3959
3960 /* how much */
3961 if (cb > cbChars)
3962 cb = cbChars;
3963
3964 /* have newline? */
3965 pszNewLine = (const char *)memchr(pachChars, '\n', cb);
3966 if (pszNewLine)
3967 {
3968 if (pLogger->fFlags & RTLOGFLAGS_USECRLF)
3969 cb = pszNewLine - pachChars;
3970 else
3971 {
3972 cb = pszNewLine - pachChars + 1;
3973 *pfPendingPrefix = true;
3974 }
3975 }
3976
3977 /* copy */
3978 memcpy(&pLogger->achScratch[offScratch], pachChars, cb);
3979
3980 /* advance */
3981 pLogger->offScratch = offScratch += (uint32_t)cb;
3982 cbRet += cb;
3983 cbChars -= cb;
3984
3985 if ( pszNewLine
3986 && (pLogger->fFlags & RTLOGFLAGS_USECRLF)
3987 && offScratch + 2 < sizeof(pLogger->achScratch))
3988 {
3989 memcpy(&pLogger->achScratch[offScratch], "\r\n", 2);
3990 pLogger->offScratch = offScratch += 2;
3991 cbRet++;
3992 cbChars--;
3993 cb++;
3994 *pfPendingPrefix = true;
3995 }
3996
3997 /* done? */
3998 if (cbChars <= 0)
3999 return cbRet;
4000 pachChars += cb;
4001 }
4002
4003 /* won't ever get here! */
4004 }
4005 else
4006 {
4007 /*
4008 * Termination call.
4009 * There's always space for a terminator, and it's not counted.
4010 */
4011 pLogger->achScratch[pLogger->offScratch] = '\0';
4012 return 0;
4013 }
4014 }
4015
4016
4017 /**
4018 * Write to a logger instance (worker function).
4019 *
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.
4022 *
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.
4030 */
4031 static void rtlogLoggerExVLocked(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
4032 {
4033 /*
4034 * Format the message and perhaps flush it.
4035 */
4036 if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF))
4037 {
4038 RTLOGOUTPUTPREFIXEDARGS OutputArgs;
4039 OutputArgs.pLogger = pLogger;
4040 OutputArgs.iGroup = iGroup;
4041 OutputArgs.fFlags = fFlags;
4042 RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);
4043 }
4044 else
4045 RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);
4046 if ( !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)
4047 && pLogger->offScratch)
4048 rtlogFlush(pLogger, false /*fNeedSpace*/);
4049 }
4050
4051
4052 #ifndef IN_RC
4053 /**
4054 * For calling rtlogLoggerExVLocked.
4055 *
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.
4063 */
4064 static void rtlogLoggerExFLocked(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
4065 {
4066 va_list va;
4067 va_start(va, pszFormat);
4068 rtlogLoggerExVLocked(pLogger, fFlags, iGroup, pszFormat, va);
4069 va_end(va);
4070 }
4071 #endif /* !IN_RC */
4072