VirtualBox

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

Last change on this file since 38601 was 37818, checked in by vboxsync, 13 years ago

Fix logging from R0

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 110.8 KB
Line 
1/* $Id: log.cpp 37818 2011-07-07 13:25:03Z vboxsync $ */
2/** @file
3 * Runtime VBox - Logger.
4 */
5
6/*
7 * Copyright (C) 2006-2011 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* Structures and Typedefs *
67*******************************************************************************/
68/**
69 * Arguments passed to the output function.
70 */
71typedef struct RTLOGOUTPUTPREFIXEDARGS
72{
73 /** The logger instance. */
74 PRTLOGGER pLogger;
75 /** The flags. (used for prefixing.) */
76 unsigned fFlags;
77 /** The group. (used for prefixing.) */
78 unsigned iGroup;
79} RTLOGOUTPUTPREFIXEDARGS, *PRTLOGOUTPUTPREFIXEDARGS;
80
81/**
82 * Internal logger data.
83 *
84 * @remarks Don't make casual changes to this structure.
85 */
86typedef struct RTLOGGERINTERNAL
87{
88 /** The structure revision (RTLOGGERINTERNAL_REV). */
89 uint32_t uRevision;
90 /** The size of the internal logger structure. */
91 uint32_t cbSelf;
92
93 /** Spinning mutex semaphore. Can be NIL. */
94 RTSEMSPINMUTEX hSpinMtx;
95 /** Pointer to the flush function. */
96 PFNRTLOGFLUSH pfnFlush;
97
98 /** Custom prefix callback. */
99 PFNRTLOGPREFIX pfnPrefix;
100 /** Prefix callback argument. */
101 void *pvPrefixUserArg;
102 /** This is set if a prefix is pending. */
103 bool fPendingPrefix;
104 /** Alignment padding. */
105 bool afPadding1[3];
106
107 /** The max number of groups that there is room for in afGroups and papszGroups.
108 * Used by RTLogCopyGroupAndFlags(). */
109 uint32_t cMaxGroups;
110 /** Pointer to the group name array.
111 * (The data is readonly and provided by the user.) */
112 const char * const *papszGroups;
113
114 /** The number of log entries per group. NULL if
115 * RTLOGFLAGS_RESTRICT_GROUPS is not specified. */
116 uint32_t *pacEntriesPerGroup;
117 /** The max number of entries per group. */
118 uint32_t cMaxEntriesPerGroup;
119 /** Padding. */
120 uint32_t u32Padding2;
121
122#ifdef IN_RING3 /* Note! Must be at the end! */
123 /** @name File logging bits for the logger.
124 * @{ */
125 /** Pointer to the function called when starting logging, and when
126 * ending or starting a new log file as part of history rotation.
127 * This can be NULL. */
128 PFNRTLOGPHASE pfnPhase;
129
130 /** Handle to log file (if open). */
131 RTFILE hFile;
132 /** Log file history settings: maximum amount of data to put in a file. */
133 uint64_t cbHistoryFileMax;
134 /** Log file history settings: current amount of data in a file. */
135 uint64_t cbHistoryFileWritten;
136 /** Log file history settings: maximum time to use a file (in seconds). */
137 uint32_t cSecsHistoryTimeSlot;
138 /** Log file history settings: in what time slot was the file created. */
139 uint32_t uHistoryTimeSlotStart;
140 /** Log file history settings: number of older files to keep.
141 * 0 means no history. */
142 uint32_t cHistory;
143 /** Pointer to filename. */
144 char szFilename[RTPATH_MAX];
145 /** @} */
146#endif /* IN_RING3 */
147} RTLOGGERINTERNAL;
148
149/** The revision of the internal logger structure. */
150#define RTLOGGERINTERNAL_REV UINT32_C(9)
151
152#ifdef IN_RING3
153/** The size of the RTLOGGERINTERNAL structure in ring-0. */
154# define RTLOGGERINTERNAL_R0_SIZE RT_OFFSETOF(RTLOGGERINTERNAL, pfnPhase)
155AssertCompileMemberAlignment(RTLOGGERINTERNAL, hFile, sizeof(void *));
156AssertCompileMemberAlignment(RTLOGGERINTERNAL, cbHistoryFileMax, sizeof(uint64_t));
157#endif
158
159/*******************************************************************************
160* Internal Functions *
161*******************************************************************************/
162#ifndef IN_RC
163static unsigned rtlogGroupFlags(const char *psz);
164#endif
165#ifdef IN_RING0
166static void rtR0LogLoggerExFallback(uint32_t fDestFlags, uint32_t fFlags, const char *pszFormat, va_list va);
167#endif
168#ifdef IN_RING3
169static int rtlogFileOpen(PRTLOGGER pLogger, char *pszErrorMsg, size_t cchErrorMsg);
170static void rtlogRotate(PRTLOGGER pLogger, uint32_t uTimeSlot, bool fFirst);
171#endif
172static void rtlogFlush(PRTLOGGER pLogger);
173static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars);
174static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars);
175static void rtlogLoggerExVLocked(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args);
176static void rtlogLoggerExFLocked(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...);
177
178
179/*******************************************************************************
180* Global Variables *
181*******************************************************************************/
182#ifdef IN_RC
183/** Default logger instance. */
184extern "C" DECLIMPORT(RTLOGGERRC) g_Logger;
185#else /* !IN_RC */
186/** Default logger instance. */
187static PRTLOGGER g_pLogger;
188#endif /* !IN_RC */
189#ifdef IN_RING3
190/** The RTThreadGetWriteLockCount() change caused by the logger mutex semaphore. */
191static uint32_t volatile g_cLoggerLockCount;
192#endif
193
194#ifdef IN_RING0
195/** Number of per-thread loggers. */
196static int32_t volatile g_cPerThreadLoggers;
197/** Per-thread loggers.
198 * This is just a quick TLS hack suitable for debug logging only.
199 * If we run out of entries, just unload and reload the driver. */
200static struct RTLOGGERPERTHREAD
201{
202 /** The thread. */
203 RTNATIVETHREAD volatile NativeThread;
204 /** The (process / session) key. */
205 uintptr_t volatile uKey;
206 /** The logger instance.*/
207 PRTLOGGER volatile pLogger;
208} g_aPerThreadLoggers[8] =
209{
210 { NIL_RTNATIVETHREAD, 0, 0},
211 { NIL_RTNATIVETHREAD, 0, 0},
212 { NIL_RTNATIVETHREAD, 0, 0},
213 { NIL_RTNATIVETHREAD, 0, 0},
214 { NIL_RTNATIVETHREAD, 0, 0},
215 { NIL_RTNATIVETHREAD, 0, 0},
216 { NIL_RTNATIVETHREAD, 0, 0},
217 { NIL_RTNATIVETHREAD, 0, 0}
218};
219#endif /* IN_RING0 */
220
221/**
222 * Logger flags instructions.
223 */
224static struct
225{
226 const char *pszInstr; /**< The name */
227 size_t cchInstr; /**< The size of the name. */
228 uint32_t fFlag; /**< The flag value. */
229 bool fInverted; /**< Inverse meaning? */
230} const s_aLogFlags[] =
231{
232 { "disabled", sizeof("disabled" ) - 1, RTLOGFLAGS_DISABLED, false },
233 { "enabled", sizeof("enabled" ) - 1, RTLOGFLAGS_DISABLED, true },
234 { "buffered", sizeof("buffered" ) - 1, RTLOGFLAGS_BUFFERED, false },
235 { "unbuffered", sizeof("unbuffered" ) - 1, RTLOGFLAGS_BUFFERED, true },
236 { "usecrlf", sizeof("usecrlf" ) - 1, RTLOGFLAGS_USECRLF, false },
237 { "uself", sizeof("uself" ) - 1, RTLOGFLAGS_USECRLF, true },
238 { "append", sizeof("append" ) - 1, RTLOGFLAGS_APPEND, false },
239 { "overwrite", sizeof("overwrite" ) - 1, RTLOGFLAGS_APPEND, true },
240 { "rel", sizeof("rel" ) - 1, RTLOGFLAGS_REL_TS, false },
241 { "abs", sizeof("abs" ) - 1, RTLOGFLAGS_REL_TS, true },
242 { "dec", sizeof("dec" ) - 1, RTLOGFLAGS_DECIMAL_TS, false },
243 { "hex", sizeof("hex" ) - 1, RTLOGFLAGS_DECIMAL_TS, true },
244 { "writethru", sizeof("writethru" ) - 1, RTLOGFLAGS_WRITE_THROUGH, false },
245 { "writethrough", sizeof("writethrough") - 1, RTLOGFLAGS_WRITE_THROUGH, false },
246 { "flush", sizeof("flush" ) - 1, RTLOGFLAGS_FLUSH, false },
247 { "lockcnts", sizeof("lockcnts" ) - 1, RTLOGFLAGS_PREFIX_LOCK_COUNTS, false },
248 { "cpuid", sizeof("cpuid" ) - 1, RTLOGFLAGS_PREFIX_CPUID, false },
249 { "pid", sizeof("pid" ) - 1, RTLOGFLAGS_PREFIX_PID, false },
250 { "flagno", sizeof("flagno" ) - 1, RTLOGFLAGS_PREFIX_FLAG_NO, false },
251 { "flag", sizeof("flag" ) - 1, RTLOGFLAGS_PREFIX_FLAG, false },
252 { "groupno", sizeof("groupno" ) - 1, RTLOGFLAGS_PREFIX_GROUP_NO, false },
253 { "group", sizeof("group" ) - 1, RTLOGFLAGS_PREFIX_GROUP, false },
254 { "tid", sizeof("tid" ) - 1, RTLOGFLAGS_PREFIX_TID, false },
255 { "thread", sizeof("thread" ) - 1, RTLOGFLAGS_PREFIX_THREAD, false },
256 { "custom", sizeof("custom" ) - 1, RTLOGFLAGS_PREFIX_CUSTOM, false },
257 { "timeprog", sizeof("timeprog" ) - 1, RTLOGFLAGS_PREFIX_TIME_PROG, false },
258 { "time", sizeof("time" ) - 1, RTLOGFLAGS_PREFIX_TIME, false },
259 { "msprog", sizeof("msprog" ) - 1, RTLOGFLAGS_PREFIX_MS_PROG, false },
260 { "tsc", sizeof("tsc" ) - 1, RTLOGFLAGS_PREFIX_TSC, false }, /* before ts! */
261 { "ts", sizeof("ts" ) - 1, RTLOGFLAGS_PREFIX_TS, false },
262 /* We intentionally omit RTLOGFLAGS_RESTRICT_GROUPS. */
263};
264
265/**
266 * Logger destination instructions.
267 */
268static struct
269{
270 const char *pszInstr; /**< The name. */
271 size_t cchInstr; /**< The size of the name. */
272 uint32_t fFlag; /**< The corresponding destination flag. */
273} const s_aLogDst[] =
274{
275 { "file", sizeof("file" ) - 1, RTLOGDEST_FILE }, /* Must be 1st! */
276 { "dir", sizeof("dir" ) - 1, RTLOGDEST_FILE }, /* Must be 2nd! */
277 { "history", sizeof("history" ) - 1, 0 }, /* Must be 3rd! */
278 { "histsize", sizeof("histsize") - 1, 0 }, /* Must be 4th! */
279 { "histtime", sizeof("histtime") - 1, 0 }, /* Must be 5th! */
280 { "stdout", sizeof("stdout" ) - 1, RTLOGDEST_STDOUT },
281 { "stderr", sizeof("stderr" ) - 1, RTLOGDEST_STDERR },
282 { "debugger", sizeof("debugger") - 1, RTLOGDEST_DEBUGGER },
283 { "com", sizeof("com" ) - 1, RTLOGDEST_COM },
284 { "user", sizeof("user" ) - 1, RTLOGDEST_USER },
285};
286
287
288/**
289 * Locks the logger instance.
290 *
291 * @returns See RTSemSpinMutexRequest().
292 * @param pLogger The logger instance.
293 */
294DECLINLINE(int) rtlogLock(PRTLOGGER pLogger)
295{
296#ifndef IN_RC
297 PRTLOGGERINTERNAL pInt = pLogger->pInt;
298 AssertMsgReturn(pInt->uRevision == RTLOGGERINTERNAL_REV, ("%#x != %#x\n", pInt->uRevision, RTLOGGERINTERNAL_REV),
299 VERR_LOG_REVISION_MISMATCH);
300 AssertMsgReturn(pInt->cbSelf == sizeof(*pInt), ("%#x != %#x\n", pInt->cbSelf, sizeof(*pInt)),
301 VERR_LOG_REVISION_MISMATCH);
302 if (pInt->hSpinMtx != NIL_RTSEMSPINMUTEX)
303 {
304 int rc = RTSemSpinMutexRequest(pInt->hSpinMtx);
305 if (RT_FAILURE(rc))
306 return rc;
307 }
308#endif
309 return VINF_SUCCESS;
310}
311
312
313/**
314 * Unlocks the logger instance.
315 * @param pLogger The logger instance.
316 */
317DECLINLINE(void) rtlogUnlock(PRTLOGGER pLogger)
318{
319#ifndef IN_RC
320 if (pLogger->pInt->hSpinMtx != NIL_RTSEMSPINMUTEX)
321 RTSemSpinMutexRelease(pLogger->pInt->hSpinMtx);
322#endif
323 return;
324}
325
326#ifndef IN_RC
327# ifdef IN_RING3
328
329/**
330 * Logging to file, output callback.
331 *
332 * @param pvArg User argument.
333 * @param pachChars Pointer to an array of utf-8 characters.
334 * @param cbChars Number of bytes in the character array pointed to by pachChars.
335 */
336static DECLCALLBACK(size_t) rtlogPhaseWrite(void *pvArg, const char *pachChars, size_t cbChars)
337{
338 PRTLOGGER pLogger = (PRTLOGGER)pvArg;
339 RTFileWrite(pLogger->pInt->hFile, pachChars, cbChars, NULL);
340 return cbChars;
341}
342
343
344/**
345 * Callback to format VBox formatting extentions.
346 * See @ref pg_rt_str_format for a reference on the format types.
347 *
348 * @returns The number of bytes formatted.
349 * @param pvArg Formatter argument.
350 * @param pfnOutput Pointer to output function.
351 * @param pvArgOutput Argument for the output function.
352 * @param ppszFormat Pointer to the format string pointer. Advance this till the char
353 * after the format specifier.
354 * @param pArgs Pointer to the argument list. Use this to fetch the arguments.
355 * @param cchWidth Format Width. -1 if not specified.
356 * @param cchPrecision Format Precision. -1 if not specified.
357 * @param fFlags Flags (RTSTR_NTFS_*).
358 * @param chArgSize The argument size specifier, 'l' or 'L'.
359 */
360static DECLCALLBACK(size_t) rtlogPhaseFormatStr(void *pvArg, PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
361 const char **ppszFormat, va_list *pArgs, int cchWidth,
362 int cchPrecision, unsigned fFlags, char chArgSize)
363{
364 char ch = *(*ppszFormat)++;
365
366 AssertMsgFailed(("Invalid logger phase format type '%%%c%.10s'!\n", ch, *ppszFormat)); NOREF(ch);
367
368 return 0;
369}
370
371
372/**
373 * Log phase callback function, assumes the lock is already held
374 *
375 * @param pLogger The logger instance.
376 * @param pszFormat Format string.
377 * @param ... Optional arguments as specified in the format string.
378 */
379static DECLCALLBACK(void) rtlogPhaseMsgLocked(PRTLOGGER pLogger, const char *pszFormat, ...)
380{
381 va_list args;
382 AssertPtrReturnVoid(pLogger);
383 AssertPtrReturnVoid(pLogger->pInt);
384 Assert(pLogger->pInt->hSpinMtx != NIL_RTSEMSPINMUTEX);
385
386 va_start(args, pszFormat);
387 rtlogLoggerExVLocked(pLogger, 0, ~0, pszFormat, args);
388 va_end(args);
389}
390
391
392/**
393 * Log phase callback function, assumes the lock is not held.
394 *
395 * @param pLogger The logger instance.
396 * @param pszFormat Format string.
397 * @param ... Optional arguments as specified in the format string.
398 */
399static DECLCALLBACK(void) rtlogPhaseMsgNormal(PRTLOGGER pLogger, const char *pszFormat, ...)
400{
401 va_list args;
402 AssertPtrReturnVoid(pLogger);
403 AssertPtrReturnVoid(pLogger->pInt);
404 Assert(pLogger->pInt->hSpinMtx != NIL_RTSEMSPINMUTEX);
405
406 va_start(args, pszFormat);
407 RTLogLoggerExV(pLogger, 0, ~0, pszFormat, args);
408 va_end(args);
409}
410
411# endif /* IN_RING3 */
412
413RTDECL(int) RTLogCreateExV(PRTLOGGER *ppLogger, uint32_t fFlags, const char *pszGroupSettings,
414 const char *pszEnvVarBase, unsigned cGroups, const char * const *papszGroups,
415 uint32_t fDestFlags, PFNRTLOGPHASE pfnPhase, uint32_t cHistory,
416 uint64_t cbHistoryFileMax, uint32_t cSecsHistoryTimeSlot,
417 char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, va_list args)
418{
419 int rc;
420 size_t offInternal;
421 size_t cbLogger;
422 PRTLOGGER pLogger;
423
424 /*
425 * Validate input.
426 */
427 if ( (cGroups && !papszGroups)
428 || !VALID_PTR(ppLogger) )
429 {
430 AssertMsgFailed(("Invalid parameters!\n"));
431 return VERR_INVALID_PARAMETER;
432 }
433 *ppLogger = NULL;
434
435 if (pszErrorMsg)
436 RTStrPrintf(pszErrorMsg, cchErrorMsg, N_("unknown error"));
437
438 AssertMsgReturn(cHistory < _1M, ("%#x", cHistory), VERR_OUT_OF_RANGE);
439
440 /*
441 * Allocate a logger instance.
442 */
443 offInternal = RT_OFFSETOF(RTLOGGER, afGroups[cGroups]);
444 offInternal = RT_ALIGN_Z(offInternal, sizeof(uint64_t));
445 cbLogger = offInternal + sizeof(RTLOGGERINTERNAL);
446 if (fFlags & RTLOGFLAGS_RESTRICT_GROUPS)
447 cbLogger += cGroups * sizeof(uint32_t);
448 pLogger = (PRTLOGGER)RTMemAllocZVar(cbLogger);
449 if (pLogger)
450 {
451# if defined(RT_ARCH_X86) && (!defined(LOG_USE_C99) || !defined(RT_WITHOUT_EXEC_ALLOC))
452 uint8_t *pu8Code;
453# endif
454 pLogger->u32Magic = RTLOGGER_MAGIC;
455 pLogger->cGroups = cGroups;
456 pLogger->fFlags = fFlags;
457 pLogger->fDestFlags = fDestFlags;
458 pLogger->pInt = (PRTLOGGERINTERNAL)((uintptr_t)pLogger + offInternal);
459 pLogger->pInt->uRevision = RTLOGGERINTERNAL_REV;
460 pLogger->pInt->cbSelf = sizeof(RTLOGGERINTERNAL);
461 pLogger->pInt->hSpinMtx = NIL_RTSEMSPINMUTEX;
462 pLogger->pInt->pfnFlush = NULL;
463 pLogger->pInt->pfnPrefix = NULL;
464 pLogger->pInt->pvPrefixUserArg = NULL;
465 pLogger->pInt->afPadding1[0] = false;
466 pLogger->pInt->afPadding1[1] = false;
467 pLogger->pInt->afPadding1[2] = false;
468 pLogger->pInt->cMaxGroups = cGroups;
469 pLogger->pInt->papszGroups = papszGroups;
470 if (fFlags & RTLOGFLAGS_RESTRICT_GROUPS)
471 pLogger->pInt->pacEntriesPerGroup = (uint32_t *)(pLogger->pInt + 1);
472 else
473 pLogger->pInt->pacEntriesPerGroup = NULL;
474 pLogger->pInt->cMaxEntriesPerGroup = UINT32_MAX;
475# ifdef IN_RING3
476 pLogger->pInt->pfnPhase = pfnPhase;
477 pLogger->pInt->hFile = NIL_RTFILE;
478 pLogger->pInt->cHistory = cHistory;
479 if (cbHistoryFileMax == 0)
480 pLogger->pInt->cbHistoryFileMax = UINT64_MAX;
481 else
482 pLogger->pInt->cbHistoryFileMax = cbHistoryFileMax;
483 if (cSecsHistoryTimeSlot == 0)
484 pLogger->pInt->cSecsHistoryTimeSlot = UINT32_MAX;
485 else
486 pLogger->pInt->cSecsHistoryTimeSlot = cSecsHistoryTimeSlot;
487# endif /* IN_RING3 */
488 if (pszGroupSettings)
489 RTLogGroupSettings(pLogger, pszGroupSettings);
490
491# if defined(RT_ARCH_X86) && (!defined(LOG_USE_C99) || !defined(RT_WITHOUT_EXEC_ALLOC))
492 /*
493 * Emit wrapper code.
494 */
495 pu8Code = (uint8_t *)RTMemExecAlloc(64);
496 if (pu8Code)
497 {
498 pLogger->pfnLogger = *(PFNRTLOGGER*)&pu8Code;
499 *pu8Code++ = 0x68; /* push imm32 */
500 *(void **)pu8Code = pLogger;
501 pu8Code += sizeof(void *);
502 *pu8Code++ = 0xe8; /* call rel32 */
503 *(uint32_t *)pu8Code = (uintptr_t)RTLogLogger - ((uintptr_t)pu8Code + sizeof(uint32_t));
504 pu8Code += sizeof(uint32_t);
505 *pu8Code++ = 0x8d; /* lea esp, [esp + 4] */
506 *pu8Code++ = 0x64;
507 *pu8Code++ = 0x24;
508 *pu8Code++ = 0x04;
509 *pu8Code++ = 0xc3; /* ret near */
510 AssertMsg((uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger <= 64,
511 ("Wrapper assembly is too big! %d bytes\n", (uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger));
512 rc = VINF_SUCCESS;
513 }
514 else
515 {
516# ifdef RT_OS_LINUX
517 if (pszErrorMsg) /* Most probably SELinux causing trouble since the larger RTMemAlloc succeeded. */
518 RTStrPrintf(pszErrorMsg, cchErrorMsg, N_("mmap(PROT_WRITE | PROT_EXEC) failed -- SELinux?"));
519# endif
520 rc = VERR_NO_MEMORY;
521 }
522 if (RT_SUCCESS(rc))
523# endif /* X86 wrapper code*/
524 {
525# ifdef IN_RING3 /* files and env.vars. are only accessible when in R3 at the present time. */
526 /*
527 * Format the filename.
528 */
529 if (pszFilenameFmt)
530 {
531 /** @todo validate the length, fail on overflow. */
532 RTStrPrintfV(pLogger->pInt->szFilename, sizeof(pLogger->pInt->szFilename), pszFilenameFmt, args);
533 pLogger->fDestFlags |= RTLOGDEST_FILE;
534 }
535
536 /*
537 * Parse the environment variables.
538 */
539 if (pszEnvVarBase)
540 {
541 /* make temp copy of environment variable base. */
542 size_t cchEnvVarBase = strlen(pszEnvVarBase);
543 char *pszEnvVar = (char *)alloca(cchEnvVarBase + 16);
544 memcpy(pszEnvVar, pszEnvVarBase, cchEnvVarBase);
545
546 /*
547 * Destination.
548 */
549 strcpy(pszEnvVar + cchEnvVarBase, "_DEST");
550 const char *pszVar = RTEnvGet(pszEnvVar);
551 if (pszVar)
552 RTLogDestinations(pLogger, pszVar);
553
554 /*
555 * The flags.
556 */
557 strcpy(pszEnvVar + cchEnvVarBase, "_FLAGS");
558 pszVar = RTEnvGet(pszEnvVar);
559 if (pszVar)
560 RTLogFlags(pLogger, pszVar);
561
562 /*
563 * The group settings.
564 */
565 pszEnvVar[cchEnvVarBase] = '\0';
566 pszVar = RTEnvGet(pszEnvVar);
567 if (pszVar)
568 RTLogGroupSettings(pLogger, pszVar);
569 }
570# endif /* IN_RING3 */
571
572 /*
573 * Open the destination(s).
574 */
575 rc = VINF_SUCCESS;
576# ifdef IN_RING3
577 if (pLogger->fDestFlags & RTLOGDEST_FILE)
578 {
579 if (pLogger->fFlags & RTLOGFLAGS_APPEND)
580 {
581 rc = rtlogFileOpen(pLogger, pszErrorMsg, cchErrorMsg);
582
583 /* Rotate in case of appending to a too big log file,
584 otherwise this simply doesn't do anything. */
585 rtlogRotate(pLogger, 0, true /* fFirst */);
586 }
587 else
588 {
589 /* Force rotation if it is configured. */
590 pLogger->pInt->cbHistoryFileWritten = UINT64_MAX;
591 rtlogRotate(pLogger, 0, true /* fFirst */);
592
593 /* If the file is not open then rotation is not set up. */
594 if (pLogger->pInt->hFile == NIL_RTFILE)
595 {
596 pLogger->pInt->cbHistoryFileWritten = 0;
597 rc = rtlogFileOpen(pLogger, pszErrorMsg, cchErrorMsg);
598 }
599 }
600 }
601# endif /* IN_RING3 */
602
603 /*
604 * Create mutex and check how much it counts when entering the lock
605 * so that we can report the values for RTLOGFLAGS_PREFIX_LOCK_COUNTS.
606 */
607 if (RT_SUCCESS(rc))
608 {
609 rc = RTSemSpinMutexCreate(&pLogger->pInt->hSpinMtx, RTSEMSPINMUTEX_FLAGS_IRQ_SAFE);
610 if (RT_SUCCESS(rc))
611 {
612# ifdef IN_RING3 /** @todo do counters in ring-0 too? */
613 RTTHREAD Thread = RTThreadSelf();
614 if (Thread != NIL_RTTHREAD)
615 {
616 int32_t c = RTLockValidatorWriteLockGetCount(Thread);
617 RTSemSpinMutexRequest(pLogger->pInt->hSpinMtx);
618 c = RTLockValidatorWriteLockGetCount(Thread) - c;
619 RTSemSpinMutexRelease(pLogger->pInt->hSpinMtx);
620 ASMAtomicWriteU32(&g_cLoggerLockCount, c);
621 }
622
623 /* Use the callback to generate some initial log contents. */
624 Assert(VALID_PTR(pLogger->pInt->pfnPhase) || pLogger->pInt->pfnPhase == NULL);
625 if (pLogger->pInt->pfnPhase)
626 pLogger->pInt->pfnPhase(pLogger, RTLOGPHASE_BEGIN, rtlogPhaseMsgNormal);
627# endif
628 *ppLogger = pLogger;
629 return VINF_SUCCESS;
630 }
631
632 if (pszErrorMsg)
633 RTStrPrintf(pszErrorMsg, cchErrorMsg, N_("failed to create semaphore"));
634 }
635# ifdef IN_RING3
636 RTFileClose(pLogger->pInt->hFile);
637# endif
638# if defined(LOG_USE_C99) && defined(RT_WITHOUT_EXEC_ALLOC)
639 RTMemFree(*(void **)&pLogger->pfnLogger);
640# else
641 RTMemExecFree(*(void **)&pLogger->pfnLogger, 64);
642# endif
643 }
644 RTMemFree(pLogger);
645 }
646 else
647 rc = VERR_NO_MEMORY;
648
649 return rc;
650}
651RT_EXPORT_SYMBOL(RTLogCreateExV);
652
653
654RTDECL(int) RTLogCreate(PRTLOGGER *ppLogger, uint32_t fFlags, const char *pszGroupSettings,
655 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
656 uint32_t fDestFlags, const char *pszFilenameFmt, ...)
657{
658 va_list args;
659 int rc;
660
661 va_start(args, pszFilenameFmt);
662 rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups,
663 fDestFlags, NULL /*pfnPhase*/, 0 /*cHistory*/, 0 /*cbHistoryFileMax*/, 0 /*cSecsHistoryTimeSlot*/,
664 NULL /*pszErrorMsg*/, 0 /*cchErrorMsg*/, pszFilenameFmt, args);
665 va_end(args);
666 return rc;
667}
668RT_EXPORT_SYMBOL(RTLogCreate);
669
670
671RTDECL(int) RTLogCreateEx(PRTLOGGER *ppLogger, uint32_t fFlags, const char *pszGroupSettings,
672 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
673 uint32_t fDestFlags, PFNRTLOGPHASE pfnPhase, uint32_t cHistory,
674 uint64_t cbHistoryFileMax, uint32_t cSecsHistoryTimeSlot,
675 char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, ...)
676{
677 va_list args;
678 int rc;
679
680 va_start(args, pszFilenameFmt);
681 rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups,
682 fDestFlags, pfnPhase, cHistory, cbHistoryFileMax, cSecsHistoryTimeSlot,
683 pszErrorMsg, cchErrorMsg, pszFilenameFmt, args);
684 va_end(args);
685 return rc;
686}
687RT_EXPORT_SYMBOL(RTLogCreateEx);
688
689
690/**
691 * Destroys a logger instance.
692 *
693 * The instance is flushed and all output destinations closed (where applicable).
694 *
695 * @returns iprt status code.
696 * @param pLogger The logger instance which close destroyed. NULL is fine.
697 */
698RTDECL(int) RTLogDestroy(PRTLOGGER pLogger)
699{
700 int rc;
701 uint32_t iGroup;
702 RTSEMSPINMUTEX hSpinMtx;
703
704 /*
705 * Validate input.
706 */
707 if (!pLogger)
708 return VINF_SUCCESS;
709 AssertPtrReturn(pLogger, VERR_INVALID_POINTER);
710 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
711 AssertPtrReturn(pLogger->pInt, VERR_INVALID_POINTER);
712
713 /*
714 * Acquire logger instance sem and disable all logging. (paranoia)
715 */
716 rc = rtlogLock(pLogger);
717 AssertMsgRCReturn(rc, ("%Rrc\n", rc), rc);
718
719 pLogger->fFlags |= RTLOGFLAGS_DISABLED;
720 iGroup = pLogger->cGroups;
721 while (iGroup-- > 0)
722 pLogger->afGroups[iGroup] = 0;
723
724 /*
725 * Flush it.
726 */
727 rtlogFlush(pLogger);
728
729# ifdef IN_RING3
730 /*
731 * Add end of logging message.
732 */
733 if ( (pLogger->fDestFlags & RTLOGDEST_FILE)
734 && pLogger->pInt->hFile != NIL_RTFILE)
735 pLogger->pInt->pfnPhase(pLogger, RTLOGPHASE_END, rtlogPhaseMsgLocked);
736
737 /*
738 * Close output stuffs.
739 */
740 if (pLogger->pInt->hFile != NIL_RTFILE)
741 {
742 int rc2 = RTFileClose(pLogger->pInt->hFile);
743 AssertRC(rc2);
744 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
745 rc = rc2;
746 pLogger->pInt->hFile = NIL_RTFILE;
747 }
748# endif
749
750 /*
751 * Free the mutex, the wrapper and the instance memory.
752 */
753 hSpinMtx = pLogger->pInt->hSpinMtx;
754 pLogger->pInt->hSpinMtx = NIL_RTSEMSPINMUTEX;
755 if (hSpinMtx != NIL_RTSEMSPINMUTEX)
756 {
757 int rc2;
758 RTSemSpinMutexRelease(hSpinMtx);
759 rc2 = RTSemSpinMutexDestroy(hSpinMtx);
760 AssertRC(rc2);
761 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
762 rc = rc2;
763 }
764
765 if (pLogger->pfnLogger)
766 {
767# if defined(LOG_USE_C99) && defined(RT_WITHOUT_EXEC_ALLOC)
768 RTMemFree(*(void **)&pLogger->pfnLogger);
769# else
770 RTMemExecFree(*(void **)&pLogger->pfnLogger, 64);
771# endif
772 pLogger->pfnLogger = NULL;
773 }
774 RTMemFree(pLogger);
775
776 return rc;
777}
778RT_EXPORT_SYMBOL(RTLogDestroy);
779
780
781/**
782 * Create a logger instance clone for RC usage.
783 *
784 * @returns iprt status code.
785 *
786 * @param pLogger The logger instance to be cloned.
787 * @param pLoggerRC Where to create the RC logger instance.
788 * @param cbLoggerRC Amount of memory allocated to for the RC logger
789 * instance clone.
790 * @param pfnLoggerRCPtr Pointer to logger wrapper function for this
791 * instance (RC Ptr).
792 * @param pfnFlushRCPtr Pointer to flush function (RC Ptr).
793 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
794 */
795RTDECL(int) RTLogCloneRC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerRC, size_t cbLoggerRC,
796 RTRCPTR pfnLoggerRCPtr, RTRCPTR pfnFlushRCPtr, uint32_t fFlags)
797{
798 /*
799 * Validate input.
800 */
801 if ( !pLoggerRC
802 || !pfnFlushRCPtr
803 || !pfnLoggerRCPtr)
804 {
805 AssertMsgFailed(("Invalid parameters!\n"));
806 return VERR_INVALID_PARAMETER;
807 }
808 if (cbLoggerRC < sizeof(*pLoggerRC))
809 {
810 AssertMsgFailed(("%d min=%d\n", cbLoggerRC, sizeof(*pLoggerRC)));
811 return VERR_INVALID_PARAMETER;
812 }
813
814 /*
815 * Initialize GC instance.
816 */
817 pLoggerRC->offScratch = 0;
818 pLoggerRC->fPendingPrefix = false;
819 pLoggerRC->pfnLogger = pfnLoggerRCPtr;
820 pLoggerRC->pfnFlush = pfnFlushRCPtr;
821 pLoggerRC->u32Magic = RTLOGGERRC_MAGIC;
822 pLoggerRC->fFlags = fFlags | RTLOGFLAGS_DISABLED;
823 pLoggerRC->cGroups = 1;
824 pLoggerRC->afGroups[0] = 0;
825
826 /*
827 * Resolve defaults.
828 */
829 if (!pLogger)
830 {
831 pLogger = RTLogDefaultInstance();
832 if (!pLogger)
833 return VINF_SUCCESS;
834 }
835
836 /*
837 * Check if there's enough space for the groups.
838 */
839 if (cbLoggerRC < (size_t)RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]))
840 {
841 AssertMsgFailed(("%d req=%d cGroups=%d\n", cbLoggerRC, RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]), pLogger->cGroups));
842 return VERR_BUFFER_OVERFLOW;
843 }
844 memcpy(&pLoggerRC->afGroups[0], &pLogger->afGroups[0], pLogger->cGroups * sizeof(pLoggerRC->afGroups[0]));
845 pLoggerRC->cGroups = pLogger->cGroups;
846
847 /*
848 * Copy bits from the HC instance.
849 */
850 pLoggerRC->fPendingPrefix = pLogger->pInt->fPendingPrefix;
851 pLoggerRC->fFlags |= pLogger->fFlags;
852
853 /*
854 * Check if we can remove the disabled flag.
855 */
856 if ( pLogger->fDestFlags
857 && !((pLogger->fFlags | fFlags) & RTLOGFLAGS_DISABLED))
858 pLoggerRC->fFlags &= ~RTLOGFLAGS_DISABLED;
859
860 return VINF_SUCCESS;
861}
862RT_EXPORT_SYMBOL(RTLogCloneRC);
863
864
865/**
866 * Flushes a RC logger instance to a R3 logger.
867 *
868 *
869 * @returns iprt status code.
870 * @param pLogger The R3 logger instance to flush pLoggerRC to. If NULL
871 * the default logger is used.
872 * @param pLoggerRC The RC logger instance to flush.
873 */
874RTDECL(void) RTLogFlushRC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerRC)
875{
876 /*
877 * Resolve defaults.
878 */
879 if (!pLogger)
880 {
881 pLogger = RTLogDefaultInstance();
882 if (!pLogger)
883 {
884 pLoggerRC->offScratch = 0;
885 return;
886 }
887 }
888
889 /*
890 * Any thing to flush?
891 */
892 if ( pLogger->offScratch
893 || pLoggerRC->offScratch)
894 {
895 /*
896 * Acquire logger instance sem.
897 */
898 int rc = rtlogLock(pLogger);
899 if (RT_FAILURE(rc))
900 return;
901
902 /*
903 * Write whatever the GC instance contains to the HC one, and then
904 * flush the HC instance.
905 */
906 if (pLoggerRC->offScratch)
907 {
908 rtLogOutput(pLogger, pLoggerRC->achScratch, pLoggerRC->offScratch);
909 rtLogOutput(pLogger, NULL, 0);
910 pLoggerRC->offScratch = 0;
911 }
912
913 /*
914 * Release the semaphore.
915 */
916 rtlogUnlock(pLogger);
917 }
918}
919RT_EXPORT_SYMBOL(RTLogFlushRC);
920
921# ifdef IN_RING3
922
923RTDECL(int) RTLogCreateForR0(PRTLOGGER pLogger, size_t cbLogger,
924 RTR0PTR pLoggerR0Ptr, RTR0PTR pfnLoggerR0Ptr, RTR0PTR pfnFlushR0Ptr,
925 uint32_t fFlags, uint32_t fDestFlags)
926{
927 /*
928 * Validate input.
929 */
930 AssertPtrReturn(pLogger, VERR_INVALID_PARAMETER);
931 size_t const cbRequired = sizeof(*pLogger) + RTLOGGERINTERNAL_R0_SIZE;
932 AssertReturn(cbLogger >= cbRequired, VERR_BUFFER_OVERFLOW);
933 AssertReturn(pLoggerR0Ptr != NIL_RTR0PTR, VERR_INVALID_PARAMETER);
934 AssertReturn(pfnLoggerR0Ptr != NIL_RTR0PTR, VERR_INVALID_PARAMETER);
935
936 /*
937 * Initialize the ring-0 instance.
938 */
939 pLogger->achScratch[0] = 0;
940 pLogger->offScratch = 0;
941 pLogger->pfnLogger = (PFNRTLOGGER)pfnLoggerR0Ptr;
942 pLogger->fFlags = fFlags;
943 pLogger->fDestFlags = fDestFlags & ~RTLOGDEST_FILE;
944 pLogger->pInt = NULL;
945 pLogger->cGroups = 1;
946 pLogger->afGroups[0] = 0;
947
948 uint32_t cMaxGroups = (uint32_t)((cbLogger - cbRequired) / sizeof(pLogger->afGroups[0]));
949 if (fFlags & RTLOGFLAGS_RESTRICT_GROUPS)
950 cMaxGroups /= 2;
951 PRTLOGGERINTERNAL pInt;
952 for (;;)
953 {
954 AssertReturn(cMaxGroups > 0, VERR_BUFFER_OVERFLOW);
955 pInt = (PRTLOGGERINTERNAL)&pLogger->afGroups[cMaxGroups];
956 if (!((uintptr_t)pInt & (sizeof(uint64_t) - 1)))
957 break;
958 cMaxGroups--;
959 }
960 pLogger->pInt = (PRTLOGGERINTERNAL)(pLoggerR0Ptr + (uintptr_t)pInt - (uintptr_t)pLogger);
961 pInt->uRevision = RTLOGGERINTERNAL_REV;
962 pInt->cbSelf = RTLOGGERINTERNAL_R0_SIZE;
963 pInt->hSpinMtx = NIL_RTSEMSPINMUTEX; /* Not serialized. */
964 pInt->pfnFlush = (PFNRTLOGFLUSH)pfnFlushR0Ptr;
965 pInt->pfnPrefix = NULL;
966 pInt->pvPrefixUserArg = NULL;
967 pInt->fPendingPrefix = false;
968 pInt->cMaxGroups = cMaxGroups;
969 pInt->papszGroups = NULL;
970 pInt->cMaxEntriesPerGroup = UINT32_MAX;
971 if (fFlags & RTLOGFLAGS_RESTRICT_GROUPS)
972 {
973 memset(pInt + 1, 0, sizeof(uint32_t) * cMaxGroups);
974 pInt->pacEntriesPerGroup= (uint32_t *)(pLogger->pInt + 1);
975 }
976 else
977 pInt->pacEntriesPerGroup= NULL;
978
979 pLogger->u32Magic = RTLOGGER_MAGIC;
980 return VINF_SUCCESS;
981}
982RT_EXPORT_SYMBOL(RTLogCreateForR0);
983
984
985RTDECL(size_t) RTLogCalcSizeForR0(uint32_t cGroups, uint32_t fFlags)
986{
987 size_t cb = RT_OFFSETOF(RTLOGGER, afGroups[cGroups]);
988 cb = RT_ALIGN_Z(cb, sizeof(uint64_t));
989 cb += sizeof(RTLOGGERINTERNAL);
990 if (fFlags & RTLOGFLAGS_RESTRICT_GROUPS)
991 cb += sizeof(uint32_t) * cGroups;
992 return cb;
993}
994RT_EXPORT_SYMBOL(RTLogCalcSizeForR0);
995
996
997RTDECL(int) RTLogCopyGroupsAndFlagsForR0(PRTLOGGER pDstLogger, RTR0PTR pDstLoggerR0Ptr,
998 PCRTLOGGER pSrcLogger, uint32_t fFlagsOr, uint32_t fFlagsAnd)
999{
1000 /*
1001 * Validate input.
1002 */
1003 AssertPtrReturn(pDstLogger, VERR_INVALID_PARAMETER);
1004 AssertPtrNullReturn(pSrcLogger, VERR_INVALID_PARAMETER);
1005
1006 /*
1007 * Resolve defaults.
1008 */
1009 if (!pSrcLogger)
1010 {
1011 pSrcLogger = RTLogDefaultInstance();
1012 if (!pSrcLogger)
1013 {
1014 pDstLogger->fFlags |= RTLOGFLAGS_DISABLED | fFlagsOr;
1015 pDstLogger->cGroups = 1;
1016 pDstLogger->afGroups[0] = 0;
1017 return VINF_SUCCESS;
1018 }
1019 }
1020
1021 /*
1022 * Copy flags and group settings.
1023 */
1024 pDstLogger->fFlags = (pSrcLogger->fFlags & fFlagsAnd & ~RTLOGFLAGS_RESTRICT_GROUPS) | fFlagsOr;
1025
1026 PRTLOGGERINTERNAL pDstInt = (PRTLOGGERINTERNAL)((uintptr_t)pDstLogger->pInt - pDstLoggerR0Ptr + (uintptr_t)pDstLogger);
1027 int rc = VINF_SUCCESS;
1028 uint32_t cGroups = pSrcLogger->cGroups;
1029 if (cGroups > pDstInt->cMaxGroups)
1030 {
1031 AssertMsgFailed(("cMaxGroups=%zd cGroups=%zd (min size %d)\n", pDstInt->cMaxGroups,
1032 pSrcLogger->cGroups, RT_OFFSETOF(RTLOGGER, afGroups[pSrcLogger->cGroups]) + RTLOGGERINTERNAL_R0_SIZE));
1033 rc = VERR_INVALID_PARAMETER;
1034 cGroups = pDstInt->cMaxGroups;
1035 }
1036 memcpy(&pDstLogger->afGroups[0], &pSrcLogger->afGroups[0], cGroups * sizeof(pDstLogger->afGroups[0]));
1037 pDstLogger->cGroups = cGroups;
1038
1039 return rc;
1040}
1041RT_EXPORT_SYMBOL(RTLogCopyGroupsAndFlagsForR0);
1042
1043
1044RTDECL(int) RTLogSetCustomPrefixCallbackForR0(PRTLOGGER pLogger, RTR0PTR pLoggerR0Ptr,
1045 RTR0PTR pfnCallbackR0Ptr, RTR0PTR pvUserR0Ptr)
1046{
1047 AssertPtrReturn(pLogger, VERR_INVALID_POINTER);
1048 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
1049
1050 /*
1051 * Do the work.
1052 */
1053 PRTLOGGERINTERNAL pInt = (PRTLOGGERINTERNAL)((uintptr_t)pLogger->pInt - pLoggerR0Ptr + (uintptr_t)pLogger);
1054 AssertReturn(pInt->uRevision == RTLOGGERINTERNAL_REV, VERR_LOG_REVISION_MISMATCH);
1055 pInt->pvPrefixUserArg = (void *)pvUserR0Ptr;
1056 pInt->pfnPrefix = (PFNRTLOGPREFIX)pfnCallbackR0Ptr;
1057
1058 return VINF_SUCCESS;
1059}
1060RT_EXPORT_SYMBOL(RTLogSetCustomPrefixCallbackForR0);
1061
1062RTDECL(void) RTLogFlushR0(PRTLOGGER pLogger, PRTLOGGER pLoggerR0)
1063{
1064 /*
1065 * Resolve defaults.
1066 */
1067 if (!pLogger)
1068 {
1069 pLogger = RTLogDefaultInstance();
1070 if (!pLogger)
1071 {
1072 /* flushing to "/dev/null". */
1073 if (pLoggerR0->offScratch)
1074 pLoggerR0->offScratch = 0;
1075 return;
1076 }
1077 }
1078
1079 /*
1080 * Any thing to flush?
1081 */
1082 if ( pLoggerR0->offScratch
1083 || pLogger->offScratch)
1084 {
1085 /*
1086 * Acquire logger semaphores.
1087 */
1088 int rc = rtlogLock(pLogger);
1089 if (RT_FAILURE(rc))
1090 return;
1091 if (RT_SUCCESS(rc))
1092 {
1093 /*
1094 * Write whatever the GC instance contains to the HC one, and then
1095 * flush the HC instance.
1096 */
1097 if (pLoggerR0->offScratch)
1098 {
1099 rtLogOutput(pLogger, pLoggerR0->achScratch, pLoggerR0->offScratch);
1100 rtLogOutput(pLogger, NULL, 0);
1101 pLoggerR0->offScratch = 0;
1102 }
1103 }
1104 rtlogUnlock(pLogger);
1105 }
1106}
1107RT_EXPORT_SYMBOL(RTLogFlushR0);
1108
1109# endif /* IN_RING3 */
1110
1111
1112/**
1113 * Flushes the buffer in one logger instance onto another logger.
1114 *
1115 * @returns iprt status code.
1116 *
1117 * @param pSrcLogger The logger instance to flush.
1118 * @param pDstLogger The logger instance to flush onto.
1119 * If NULL the default logger will be used.
1120 */
1121RTDECL(void) RTLogFlushToLogger(PRTLOGGER pSrcLogger, PRTLOGGER pDstLogger)
1122{
1123 /*
1124 * Resolve defaults.
1125 */
1126 if (!pDstLogger)
1127 {
1128 pDstLogger = RTLogDefaultInstance();
1129 if (!pDstLogger)
1130 {
1131 /* flushing to "/dev/null". */
1132 if (pSrcLogger->offScratch)
1133 {
1134 int rc = rtlogLock(pSrcLogger);
1135 if (RT_SUCCESS(rc))
1136 {
1137 pSrcLogger->offScratch = 0;
1138 rtlogUnlock(pSrcLogger);
1139 }
1140 }
1141 return;
1142 }
1143 }
1144
1145 /*
1146 * Any thing to flush?
1147 */
1148 if ( pSrcLogger->offScratch
1149 || pDstLogger->offScratch)
1150 {
1151 /*
1152 * Acquire logger semaphores.
1153 */
1154 int rc = rtlogLock(pDstLogger);
1155 if (RT_FAILURE(rc))
1156 return;
1157 rc = rtlogLock(pSrcLogger);
1158 if (RT_SUCCESS(rc))
1159 {
1160 /*
1161 * Write whatever the GC instance contains to the HC one, and then
1162 * flush the HC instance.
1163 */
1164 if (pSrcLogger->offScratch)
1165 {
1166 rtLogOutput(pDstLogger, pSrcLogger->achScratch, pSrcLogger->offScratch);
1167 rtLogOutput(pDstLogger, NULL, 0);
1168 pSrcLogger->offScratch = 0;
1169 }
1170
1171 /*
1172 * Release the semaphores.
1173 */
1174 rtlogUnlock(pSrcLogger);
1175 }
1176 rtlogUnlock(pDstLogger);
1177 }
1178}
1179RT_EXPORT_SYMBOL(RTLogFlushToLogger);
1180
1181
1182/**
1183 * Sets the custom prefix callback.
1184 *
1185 * @returns IPRT status code.
1186 * @param pLogger The logger instance.
1187 * @param pfnCallback The callback.
1188 * @param pvUser The user argument for the callback.
1189 * */
1190RTDECL(int) RTLogSetCustomPrefixCallback(PRTLOGGER pLogger, PFNRTLOGPREFIX pfnCallback, void *pvUser)
1191{
1192 /*
1193 * Resolve defaults.
1194 */
1195 if (!pLogger)
1196 {
1197 pLogger = RTLogDefaultInstance();
1198 if (!pLogger)
1199 return VINF_SUCCESS;
1200 }
1201 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
1202
1203 /*
1204 * Do the work.
1205 */
1206 rtlogLock(pLogger);
1207 pLogger->pInt->pvPrefixUserArg = pvUser;
1208 pLogger->pInt->pfnPrefix = pfnCallback;
1209 rtlogUnlock(pLogger);
1210
1211 return VINF_SUCCESS;
1212}
1213RT_EXPORT_SYMBOL(RTLogSetCustomPrefixCallback);
1214
1215
1216/**
1217 * Matches a group name with a pattern mask in an case insensitive manner (ASCII).
1218 *
1219 * @returns true if matching and *ppachMask set to the end of the pattern.
1220 * @returns false if no match.
1221 * @param pszGrp The group name.
1222 * @param ppachMask Pointer to the pointer to the mask. Only wildcard supported is '*'.
1223 * @param cchMask The length of the mask, including modifiers. The modifiers is why
1224 * we update *ppachMask on match.
1225 */
1226static bool rtlogIsGroupMatching(const char *pszGrp, const char **ppachMask, size_t cchMask)
1227{
1228 const char *pachMask;
1229
1230 if (!pszGrp || !*pszGrp)
1231 return false;
1232 pachMask = *ppachMask;
1233 for (;;)
1234 {
1235 if (RT_C_TO_LOWER(*pszGrp) != RT_C_TO_LOWER(*pachMask))
1236 {
1237 const char *pszTmp;
1238
1239 /*
1240 * Check for wildcard and do a minimal match if found.
1241 */
1242 if (*pachMask != '*')
1243 return false;
1244
1245 /* eat '*'s. */
1246 do pachMask++;
1247 while (--cchMask && *pachMask == '*');
1248
1249 /* is there more to match? */
1250 if ( !cchMask
1251 || *pachMask == '.'
1252 || *pachMask == '=')
1253 break; /* we're good */
1254
1255 /* do extremely minimal matching (fixme) */
1256 pszTmp = strchr(pszGrp, RT_C_TO_LOWER(*pachMask));
1257 if (!pszTmp)
1258 pszTmp = strchr(pszGrp, RT_C_TO_UPPER(*pachMask));
1259 if (!pszTmp)
1260 return false;
1261 pszGrp = pszTmp;
1262 continue;
1263 }
1264
1265 /* done? */
1266 if (!*++pszGrp)
1267 {
1268 /* trailing wildcard is ok. */
1269 do
1270 {
1271 pachMask++;
1272 cchMask--;
1273 } while (cchMask && *pachMask == '*');
1274 if ( !cchMask
1275 || *pachMask == '.'
1276 || *pachMask == '=')
1277 break; /* we're good */
1278 return false;
1279 }
1280
1281 if (!--cchMask)
1282 return false;
1283 pachMask++;
1284 }
1285
1286 /* match */
1287 *ppachMask = pachMask;
1288 return true;
1289}
1290
1291
1292/**
1293 * Updates the group settings for the logger instance using the specified
1294 * specification string.
1295 *
1296 * @returns iprt status code.
1297 * Failures can safely be ignored.
1298 * @param pLogger Logger instance.
1299 * @param pszVar Value to parse.
1300 */
1301RTDECL(int) RTLogGroupSettings(PRTLOGGER pLogger, const char *pszVar)
1302{
1303 /*
1304 * Resolve defaults.
1305 */
1306 if (!pLogger)
1307 {
1308 pLogger = RTLogDefaultInstance();
1309 if (!pLogger)
1310 return VINF_SUCCESS;
1311 }
1312
1313 /*
1314 * Iterate the string.
1315 */
1316 while (*pszVar)
1317 {
1318 /*
1319 * Skip prefixes (blanks, ;, + and -).
1320 */
1321 bool fEnabled = true;
1322 char ch;
1323 const char *pszStart;
1324 unsigned i;
1325 size_t cch;
1326
1327 while ((ch = *pszVar) == '+' || ch == '-' || ch == ' ' || ch == '\t' || ch == '\n' || ch == ';')
1328 {
1329 if (ch == '+' || ch == '-' || ch == ';')
1330 fEnabled = ch != '-';
1331 pszVar++;
1332 }
1333 if (!*pszVar)
1334 break;
1335
1336 /*
1337 * Find end.
1338 */
1339 pszStart = pszVar;
1340 while ((ch = *pszVar) != '\0' && ch != '+' && ch != '-' && ch != ' ' && ch != '\t')
1341 pszVar++;
1342
1343 /*
1344 * Find the group (ascii case insensitive search).
1345 * Special group 'all'.
1346 */
1347 cch = pszVar - pszStart;
1348 if ( cch >= 3
1349 && (pszStart[0] == 'a' || pszStart[0] == 'A')
1350 && (pszStart[1] == 'l' || pszStart[1] == 'L')
1351 && (pszStart[2] == 'l' || pszStart[2] == 'L')
1352 && (cch == 3 || pszStart[3] == '.' || pszStart[3] == '='))
1353 {
1354 /*
1355 * All.
1356 */
1357 unsigned fFlags = cch == 3
1358 ? RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1
1359 : rtlogGroupFlags(&pszStart[3]);
1360 for (i = 0; i < pLogger->cGroups; i++)
1361 {
1362 if (fEnabled)
1363 pLogger->afGroups[i] |= fFlags;
1364 else
1365 pLogger->afGroups[i] &= ~fFlags;
1366 }
1367 }
1368 else
1369 {
1370 /*
1371 * Specific group(s).
1372 */
1373 for (i = 0; i < pLogger->cGroups; i++)
1374 {
1375 const char *psz2 = (const char*)pszStart;
1376 if (rtlogIsGroupMatching(pLogger->pInt->papszGroups[i], &psz2, cch))
1377 {
1378 unsigned fFlags = RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1;
1379 if (*psz2 == '.' || *psz2 == '=')
1380 fFlags = rtlogGroupFlags(psz2);
1381 if (fEnabled)
1382 pLogger->afGroups[i] |= fFlags;
1383 else
1384 pLogger->afGroups[i] &= ~fFlags;
1385 }
1386 } /* for each group */
1387 }
1388
1389 } /* parse specification */
1390
1391 return VINF_SUCCESS;
1392}
1393RT_EXPORT_SYMBOL(RTLogGroupSettings);
1394
1395
1396/**
1397 * Interprets the group flags suffix.
1398 *
1399 * @returns Flags specified. (0 is possible!)
1400 * @param psz Start of Suffix. (Either dot or equal sign.)
1401 */
1402static unsigned rtlogGroupFlags(const char *psz)
1403{
1404 unsigned fFlags = 0;
1405
1406 /*
1407 * Literal flags.
1408 */
1409 while (*psz == '.')
1410 {
1411 static struct
1412 {
1413 const char *pszFlag; /* lowercase!! */
1414 unsigned fFlag;
1415 } aFlags[] =
1416 {
1417 { "eo", RTLOGGRPFLAGS_ENABLED },
1418 { "enabledonly",RTLOGGRPFLAGS_ENABLED },
1419 { "e", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },
1420 { "enabled", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },
1421 { "l1", RTLOGGRPFLAGS_LEVEL_1 },
1422 { "level1", RTLOGGRPFLAGS_LEVEL_1 },
1423 { "l", RTLOGGRPFLAGS_LEVEL_2 },
1424 { "l2", RTLOGGRPFLAGS_LEVEL_2 },
1425 { "level2", RTLOGGRPFLAGS_LEVEL_2 },
1426 { "l3", RTLOGGRPFLAGS_LEVEL_3 },
1427 { "level3", RTLOGGRPFLAGS_LEVEL_3 },
1428 { "l4", RTLOGGRPFLAGS_LEVEL_4 },
1429 { "level4", RTLOGGRPFLAGS_LEVEL_4 },
1430 { "l5", RTLOGGRPFLAGS_LEVEL_5 },
1431 { "level5", RTLOGGRPFLAGS_LEVEL_5 },
1432 { "l6", RTLOGGRPFLAGS_LEVEL_6 },
1433 { "level6", RTLOGGRPFLAGS_LEVEL_6 },
1434 { "f", RTLOGGRPFLAGS_FLOW },
1435 { "flow", RTLOGGRPFLAGS_FLOW },
1436 { "restrict", RTLOGGRPFLAGS_RESTRICT },
1437
1438 { "lelik", RTLOGGRPFLAGS_LELIK },
1439 { "michael", RTLOGGRPFLAGS_MICHAEL },
1440 { "sunlover", RTLOGGRPFLAGS_SUNLOVER },
1441 { "achim", RTLOGGRPFLAGS_ACHIM },
1442 { "achimha", RTLOGGRPFLAGS_ACHIM },
1443 { "s", RTLOGGRPFLAGS_SANDER },
1444 { "sander", RTLOGGRPFLAGS_SANDER },
1445 { "sandervl", RTLOGGRPFLAGS_SANDER },
1446 { "klaus", RTLOGGRPFLAGS_KLAUS },
1447 { "frank", RTLOGGRPFLAGS_FRANK },
1448 { "b", RTLOGGRPFLAGS_BIRD },
1449 { "bird", RTLOGGRPFLAGS_BIRD },
1450 { "aleksey", RTLOGGRPFLAGS_ALEKSEY },
1451 { "dj", RTLOGGRPFLAGS_DJ },
1452 { "n", RTLOGGRPFLAGS_NONAME },
1453 { "noname", RTLOGGRPFLAGS_NONAME }
1454 };
1455 unsigned i;
1456 bool fFound = false;
1457 psz++;
1458 for (i = 0; i < RT_ELEMENTS(aFlags) && !fFound; i++)
1459 {
1460 const char *psz1 = aFlags[i].pszFlag;
1461 const char *psz2 = psz;
1462 while (*psz1 == RT_C_TO_LOWER(*psz2))
1463 {
1464 psz1++;
1465 psz2++;
1466 if (!*psz1)
1467 {
1468 if ( (*psz2 >= 'a' && *psz2 <= 'z')
1469 || (*psz2 >= 'A' && *psz2 <= 'Z')
1470 || (*psz2 >= '0' && *psz2 <= '9') )
1471 break;
1472 fFlags |= aFlags[i].fFlag;
1473 fFound = true;
1474 psz = psz2;
1475 break;
1476 }
1477 } /* strincmp */
1478 } /* for each flags */
1479 }
1480
1481 /*
1482 * Flag value.
1483 */
1484 if (*psz == '=')
1485 {
1486 psz++;
1487 if (*psz == '~')
1488 fFlags = ~RTStrToInt32(psz + 1);
1489 else
1490 fFlags = RTStrToInt32(psz);
1491 }
1492
1493 return fFlags;
1494}
1495
1496/**
1497 * Helper for RTLogGetGroupSettings.
1498 */
1499static int rtLogGetGroupSettingsAddOne(const char *pszName, uint32_t fGroup, char **ppszBuf, size_t *pcchBuf, bool *pfNotFirst)
1500{
1501# define APPEND_PSZ(psz,cch) do { memcpy(*ppszBuf, (psz), (cch)); *ppszBuf += (cch); *pcchBuf -= (cch); } while (0)
1502# define APPEND_SZ(sz) APPEND_PSZ(sz, sizeof(sz) - 1)
1503# define APPEND_CH(ch) do { **ppszBuf = (ch); *ppszBuf += 1; *pcchBuf -= 1; } while (0)
1504
1505 /*
1506 * Add the name.
1507 */
1508 size_t cchName = strlen(pszName);
1509 if (cchName + 1 + *pfNotFirst > *pcchBuf)
1510 return VERR_BUFFER_OVERFLOW;
1511 if (*pfNotFirst)
1512 APPEND_CH(' ');
1513 else
1514 *pfNotFirst = true;
1515 APPEND_PSZ(pszName, cchName);
1516
1517 /*
1518 * Only generate mnemonics for the simple+common bits.
1519 */
1520 if (fGroup == (RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1))
1521 /* nothing */;
1522 else if ( fGroup == (RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 | RTLOGGRPFLAGS_LEVEL_2 | RTLOGGRPFLAGS_FLOW)
1523 && *pcchBuf >= sizeof(".e.l.f"))
1524 APPEND_SZ(".e.l.f");
1525 else if ( fGroup == (RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 | RTLOGGRPFLAGS_FLOW)
1526 && *pcchBuf >= sizeof(".e.f"))
1527 APPEND_SZ(".e.f");
1528 else if (*pcchBuf >= 1 + 10 + 1)
1529 {
1530 size_t cch;
1531 APPEND_CH('=');
1532 cch = RTStrFormatNumber(*ppszBuf, fGroup, 16, 0, 0, RTSTR_F_SPECIAL | RTSTR_F_32BIT);
1533 *ppszBuf += cch;
1534 *pcchBuf -= cch;
1535 }
1536 else
1537 return VERR_BUFFER_OVERFLOW;
1538
1539# undef APPEND_PSZ
1540# undef APPEND_SZ
1541# undef APPEND_CH
1542 return VINF_SUCCESS;
1543}
1544
1545
1546/**
1547 * Get the current log group settings as a string.
1548 *
1549 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
1550 * @param pLogger Logger instance (NULL for default logger).
1551 * @param pszBuf The output buffer.
1552 * @param cchBuf The size of the output buffer. Must be greater
1553 * than zero.
1554 */
1555RTDECL(int) RTLogGetGroupSettings(PRTLOGGER pLogger, char *pszBuf, size_t cchBuf)
1556{
1557 bool fNotFirst = false;
1558 int rc = VINF_SUCCESS;
1559 uint32_t cGroups;
1560 uint32_t fGroup;
1561 uint32_t i;
1562
1563 Assert(cchBuf);
1564
1565 /*
1566 * Resolve defaults.
1567 */
1568 if (!pLogger)
1569 {
1570 pLogger = RTLogDefaultInstance();
1571 if (!pLogger)
1572 {
1573 *pszBuf = '\0';
1574 return VINF_SUCCESS;
1575 }
1576 }
1577
1578 cGroups = pLogger->cGroups;
1579
1580 /*
1581 * Check if all are the same.
1582 */
1583 fGroup = pLogger->afGroups[0];
1584 for (i = 1; i < cGroups; i++)
1585 if (pLogger->afGroups[i] != fGroup)
1586 break;
1587 if (i >= cGroups)
1588 rc = rtLogGetGroupSettingsAddOne("all", fGroup, &pszBuf, &cchBuf, &fNotFirst);
1589 else
1590 {
1591
1592 /*
1593 * Iterate all the groups and print all that are enabled.
1594 */
1595 for (i = 0; i < cGroups; i++)
1596 {
1597 fGroup = pLogger->afGroups[i];
1598 if (fGroup)
1599 {
1600 const char *pszName = pLogger->pInt->papszGroups[i];
1601 if (pszName)
1602 {
1603 rc = rtLogGetGroupSettingsAddOne(pszName, fGroup, &pszBuf, &cchBuf, &fNotFirst);
1604 if (rc)
1605 break;
1606 }
1607 }
1608 }
1609 }
1610
1611 *pszBuf = '\0';
1612 return rc;
1613}
1614RT_EXPORT_SYMBOL(RTLogGetGroupSettings);
1615
1616#endif /* !IN_RC */
1617
1618/**
1619 * Updates the flags for the logger instance using the specified
1620 * specification string.
1621 *
1622 * @returns iprt status code.
1623 * Failures can safely be ignored.
1624 * @param pLogger Logger instance (NULL for default logger).
1625 * @param pszVar Value to parse.
1626 */
1627RTDECL(int) RTLogFlags(PRTLOGGER pLogger, const char *pszVar)
1628{
1629 int rc = VINF_SUCCESS;
1630
1631 /*
1632 * Resolve defaults.
1633 */
1634 if (!pLogger)
1635 {
1636 pLogger = RTLogDefaultInstance();
1637 if (!pLogger)
1638 return VINF_SUCCESS;
1639 }
1640
1641 /*
1642 * Iterate the string.
1643 */
1644 while (*pszVar)
1645 {
1646 /* check no prefix. */
1647 bool fNo = false;
1648 char ch;
1649 unsigned i;
1650
1651 /* skip blanks. */
1652 while (RT_C_IS_SPACE(*pszVar))
1653 pszVar++;
1654 if (!*pszVar)
1655 return rc;
1656
1657 while ((ch = *pszVar) != '\0')
1658 {
1659 if (ch == 'n' && pszVar[1] == 'o')
1660 {
1661 pszVar += 2;
1662 fNo = !fNo;
1663 }
1664 else if (ch == '+')
1665 {
1666 pszVar++;
1667 fNo = true;
1668 }
1669 else if (ch == '-' || ch == '!' || ch == '~')
1670 {
1671 pszVar++;
1672 fNo = !fNo;
1673 }
1674 else
1675 break;
1676 }
1677
1678 /* instruction. */
1679 for (i = 0; i < RT_ELEMENTS(s_aLogFlags); i++)
1680 {
1681 if (!strncmp(pszVar, s_aLogFlags[i].pszInstr, s_aLogFlags[i].cchInstr))
1682 {
1683 if (fNo == s_aLogFlags[i].fInverted)
1684 pLogger->fFlags |= s_aLogFlags[i].fFlag;
1685 else
1686 pLogger->fFlags &= ~s_aLogFlags[i].fFlag;
1687 pszVar += s_aLogFlags[i].cchInstr;
1688 break;
1689 }
1690 }
1691
1692 /* unknown instruction? */
1693 if (i >= RT_ELEMENTS(s_aLogFlags))
1694 {
1695 AssertMsgFailed(("Invalid flags! unknown instruction %.20s\n", pszVar));
1696 pszVar++;
1697 }
1698
1699 /* skip blanks and delimiters. */
1700 while (RT_C_IS_SPACE(*pszVar) || *pszVar == ';')
1701 pszVar++;
1702 } /* while more environment variable value left */
1703
1704 return rc;
1705}
1706RT_EXPORT_SYMBOL(RTLogFlags);
1707
1708
1709/**
1710 * Changes the buffering setting of the specified logger.
1711 *
1712 * This can be used for optimizing longish logging sequences.
1713 *
1714 * @returns The old state.
1715 * @param pLogger The logger instance (NULL is an alias for the
1716 * default logger).
1717 * @param fBuffered The new state.
1718 */
1719RTDECL(bool) RTLogSetBuffering(PRTLOGGER pLogger, bool fBuffered)
1720{
1721 bool fOld;
1722
1723 /*
1724 * Resolve the logger instance.
1725 */
1726 if (!pLogger)
1727 {
1728 pLogger = RTLogDefaultInstance();
1729 if (!pLogger)
1730 return false;
1731 }
1732
1733 rtlogLock(pLogger);
1734 fOld = !!(pLogger->fFlags & RTLOGFLAGS_BUFFERED);
1735 if (fBuffered)
1736 pLogger->fFlags |= RTLOGFLAGS_BUFFERED;
1737 else
1738 pLogger->fFlags &= ~RTLOGFLAGS_BUFFERED;
1739 rtlogUnlock(pLogger);
1740
1741 return fOld;
1742}
1743RT_EXPORT_SYMBOL(RTLogSetBuffering);
1744
1745
1746#ifdef IN_RING3
1747RTDECL(uint32_t) RTLogSetGroupLimit(PRTLOGGER pLogger, uint32_t cMaxEntriesPerGroup)
1748{
1749 /*
1750 * Resolve the logger instance.
1751 */
1752 if (!pLogger)
1753 {
1754 pLogger = RTLogDefaultInstance();
1755 if (!pLogger)
1756 return UINT32_MAX;
1757 }
1758
1759 rtlogLock(pLogger);
1760 uint32_t cOld = pLogger->pInt->cMaxEntriesPerGroup;
1761 pLogger->pInt->cMaxEntriesPerGroup = cMaxEntriesPerGroup;
1762 rtlogUnlock(pLogger);
1763
1764 return cOld;
1765}
1766#endif
1767
1768#ifndef IN_RC
1769
1770/**
1771 * Get the current log flags as a string.
1772 *
1773 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
1774 * @param pLogger Logger instance (NULL for default logger).
1775 * @param pszBuf The output buffer.
1776 * @param cchBuf The size of the output buffer. Must be greater
1777 * than zero.
1778 */
1779RTDECL(int) RTLogGetFlags(PRTLOGGER pLogger, char *pszBuf, size_t cchBuf)
1780{
1781 bool fNotFirst = false;
1782 int rc = VINF_SUCCESS;
1783 uint32_t fFlags;
1784 unsigned i;
1785
1786 Assert(cchBuf);
1787
1788 /*
1789 * Resolve defaults.
1790 */
1791 if (!pLogger)
1792 {
1793 pLogger = RTLogDefaultInstance();
1794 if (!pLogger)
1795 {
1796 *pszBuf = '\0';
1797 return VINF_SUCCESS;
1798 }
1799 }
1800
1801 /*
1802 * Add the flags in the list.
1803 */
1804 fFlags = pLogger->fFlags;
1805 for (i = 0; i < RT_ELEMENTS(s_aLogFlags); i++)
1806 if ( !s_aLogFlags[i].fInverted
1807 ? (s_aLogFlags[i].fFlag & fFlags)
1808 : !(s_aLogFlags[i].fFlag & fFlags))
1809 {
1810 size_t cchInstr = s_aLogFlags[i].cchInstr;
1811 if (cchInstr + fNotFirst + 1 > cchBuf)
1812 {
1813 rc = VERR_BUFFER_OVERFLOW;
1814 break;
1815 }
1816 if (fNotFirst)
1817 {
1818 *pszBuf++ = ' ';
1819 cchBuf--;
1820 }
1821 memcpy(pszBuf, s_aLogFlags[i].pszInstr, cchInstr);
1822 pszBuf += cchInstr;
1823 cchBuf -= cchInstr;
1824 fNotFirst = true;
1825 }
1826 *pszBuf = '\0';
1827 return rc;
1828}
1829RT_EXPORT_SYMBOL(RTLogGetFlags);
1830
1831
1832/**
1833 * Updates the logger destination using the specified string.
1834 *
1835 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
1836 * @param pLogger Logger instance (NULL for default logger).
1837 * @param pszVar The value to parse.
1838 */
1839RTDECL(int) RTLogDestinations(PRTLOGGER pLogger, char const *pszVar)
1840{
1841 /*
1842 * Resolve defaults.
1843 */
1844 if (!pLogger)
1845 {
1846 pLogger = RTLogDefaultInstance();
1847 if (!pLogger)
1848 return VINF_SUCCESS;
1849 }
1850
1851 /*
1852 * Do the parsing.
1853 */
1854 while (*pszVar)
1855 {
1856 bool fNo;
1857 unsigned i;
1858
1859 /* skip blanks. */
1860 while (RT_C_IS_SPACE(*pszVar))
1861 pszVar++;
1862 if (!*pszVar)
1863 break;
1864
1865 /* check no prefix. */
1866 fNo = false;
1867 if (pszVar[0] == 'n' && pszVar[1] == 'o')
1868 {
1869 fNo = true;
1870 pszVar += 2;
1871 }
1872
1873 /* instruction. */
1874 for (i = 0; i < RT_ELEMENTS(s_aLogDst); i++)
1875 {
1876 size_t cchInstr = strlen(s_aLogDst[i].pszInstr);
1877 if (!strncmp(pszVar, s_aLogDst[i].pszInstr, cchInstr))
1878 {
1879 if (!fNo)
1880 pLogger->fDestFlags |= s_aLogDst[i].fFlag;
1881 else
1882 pLogger->fDestFlags &= ~s_aLogDst[i].fFlag;
1883 pszVar += cchInstr;
1884
1885 /* check for value. */
1886 while (RT_C_IS_SPACE(*pszVar))
1887 pszVar++;
1888 if (*pszVar == '=' || *pszVar == ':')
1889 {
1890 const char *pszEnd;
1891
1892 pszVar++;
1893 pszEnd = strchr(pszVar, ';');
1894 if (!pszEnd)
1895 pszEnd = strchr(pszVar, '\0');
1896# ifdef IN_RING3
1897 size_t cch = pszEnd - pszVar;
1898
1899 /* log file name */
1900 if (i == 0 /* file */ && !fNo)
1901 {
1902 AssertReturn(cch < sizeof(pLogger->pInt->szFilename), VERR_OUT_OF_RANGE);
1903 memcpy(pLogger->pInt->szFilename, pszVar, cch);
1904 pLogger->pInt->szFilename[cch] = '\0';
1905 }
1906 /* log directory */
1907 else if (i == 1 /* dir */ && !fNo)
1908 {
1909 char szTmp[sizeof(pLogger->pInt->szFilename)];
1910 const char *pszFile = RTPathFilename(pLogger->pInt->szFilename);
1911 size_t cchFile = pszFile ? strlen(pszFile) : 0;
1912 AssertReturn(cchFile + cch + 1 < sizeof(pLogger->pInt->szFilename), VERR_OUT_OF_RANGE);
1913 memcpy(szTmp, cchFile ? pszFile : "", cchFile + 1);
1914
1915 memcpy(pLogger->pInt->szFilename, pszVar, cch);
1916 pLogger->pInt->szFilename[cch] = '\0';
1917 RTPathStripTrailingSlash(pLogger->pInt->szFilename);
1918
1919 cch = strlen(pLogger->pInt->szFilename);
1920 pLogger->pInt->szFilename[cch++] = '/';
1921 memcpy(&pLogger->pInt->szFilename[cch], szTmp, cchFile);
1922 pLogger->pInt->szFilename[cch + cchFile] = '\0';
1923 }
1924 else if (i == 2 /* history */)
1925 {
1926 if (!fNo)
1927 {
1928 uint32_t cHistory = 0;
1929 char szTmp[32];
1930 int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszVar, cch);
1931 if (RT_SUCCESS(rc))
1932 rc = RTStrToUInt32Full(szTmp, 0, &cHistory);
1933 AssertMsgReturn(RT_SUCCESS(rc) && cHistory < _1M, ("Invalid history value %s (%Rrc)!\n", szTmp, rc), rc);
1934 pLogger->pInt->cHistory = cHistory;
1935 }
1936 else
1937 pLogger->pInt->cHistory = 0;
1938 }
1939 else if (i == 3 /* histsize */)
1940 {
1941 if (!fNo)
1942 {
1943 char szTmp[32];
1944 int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszVar, cch);
1945 if (RT_SUCCESS(rc))
1946 rc = RTStrToUInt64Full(szTmp, 0, &pLogger->pInt->cbHistoryFileMax);
1947 AssertMsgRCReturn(rc, ("Invalid history file size value %s (%Rrc)!\n", szTmp, rc), rc);
1948 if (pLogger->pInt->cbHistoryFileMax == 0)
1949 pLogger->pInt->cbHistoryFileMax = UINT64_MAX;
1950 }
1951 else
1952 pLogger->pInt->cbHistoryFileMax = UINT64_MAX;
1953 }
1954 else if (i == 4 /* histtime */)
1955 {
1956 if (!fNo)
1957 {
1958 char szTmp[32];
1959 int rc = RTStrCopyEx(szTmp, sizeof(szTmp), pszVar, cch);
1960 if (RT_SUCCESS(rc))
1961 rc = RTStrToUInt32Full(szTmp, 0, &pLogger->pInt->cSecsHistoryTimeSlot);
1962 AssertMsgRCReturn(rc, ("Invalid history time slot value %s (%Rrc)!\n", szTmp, rc), rc);
1963 if (pLogger->pInt->cSecsHistoryTimeSlot == 0)
1964 pLogger->pInt->cSecsHistoryTimeSlot = UINT32_MAX;
1965 }
1966 else
1967 pLogger->pInt->cSecsHistoryTimeSlot = UINT32_MAX;
1968 }
1969 else
1970 AssertMsgFailedReturn(("Invalid destination value! %s%s doesn't take a value!\n",
1971 fNo ? "no" : "", s_aLogDst[i].pszInstr),
1972 VERR_INVALID_PARAMETER);
1973# endif /* IN_RING3 */
1974 pszVar = pszEnd + (*pszEnd != '\0');
1975 }
1976 break;
1977 }
1978 }
1979
1980 /* assert known instruction */
1981 AssertMsgReturn(i < RT_ELEMENTS(s_aLogDst),
1982 ("Invalid destination value! unknown instruction %.20s\n", pszVar),
1983 VERR_INVALID_PARAMETER);
1984
1985 /* skip blanks and delimiters. */
1986 while (RT_C_IS_SPACE(*pszVar) || *pszVar == ';')
1987 pszVar++;
1988 } /* while more environment variable value left */
1989
1990 return VINF_SUCCESS;
1991}
1992RT_EXPORT_SYMBOL(RTLogDestinations);
1993
1994
1995/**
1996 * Get the current log destinations as a string.
1997 *
1998 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
1999 * @param pLogger Logger instance (NULL for default logger).
2000 * @param pszBuf The output buffer.
2001 * @param cchBuf The size of the output buffer. Must be greater
2002 * than 0.
2003 */
2004RTDECL(int) RTLogGetDestinations(PRTLOGGER pLogger, char *pszBuf, size_t cchBuf)
2005{
2006 bool fNotFirst = false;
2007 int rc = VINF_SUCCESS;
2008 uint32_t fDestFlags;
2009 unsigned i;
2010
2011 AssertReturn(cchBuf, VERR_INVALID_PARAMETER);
2012 *pszBuf = '\0';
2013
2014 /*
2015 * Resolve defaults.
2016 */
2017 if (!pLogger)
2018 {
2019 pLogger = RTLogDefaultInstance();
2020 if (!pLogger)
2021 return VINF_SUCCESS;
2022 }
2023
2024 /*
2025 * Add the flags in the list.
2026 */
2027 fDestFlags = pLogger->fDestFlags;
2028 for (i = 2; i < RT_ELEMENTS(s_aLogDst); i++)
2029 if (s_aLogDst[i].fFlag & fDestFlags)
2030 {
2031 if (fNotFirst)
2032 {
2033 rc = RTStrCopyP(&pszBuf, &cchBuf, " ");
2034 if (RT_FAILURE(rc))
2035 return rc;
2036 }
2037 rc = RTStrCopyP(&pszBuf, &cchBuf, s_aLogDst[i].pszInstr);
2038 if (RT_FAILURE(rc))
2039 return rc;
2040 fNotFirst = true;
2041 }
2042
2043# ifdef IN_RING3
2044 /*
2045 * Add the filename.
2046 */
2047 if (fDestFlags & RTLOGDEST_FILE)
2048 {
2049 rc = RTStrCopyP(&pszBuf, &cchBuf, fNotFirst ? " file=" : "file=");
2050 if (RT_FAILURE(rc))
2051 return rc;
2052 rc = RTStrCopyP(&pszBuf, &cchBuf, pLogger->pInt->szFilename);
2053 if (RT_FAILURE(rc))
2054 return rc;
2055 fNotFirst = true;
2056 }
2057
2058 if (fDestFlags & RTLOGDEST_FILE)
2059 {
2060 char szNum[32];
2061 if (pLogger->pInt->cHistory)
2062 {
2063 RTStrPrintf(szNum, sizeof(szNum), fNotFirst ? "history=%u" : " history=%u", pLogger->pInt->cHistory);
2064 rc = RTStrCopyP(&pszBuf, &cchBuf, szNum);
2065 if (RT_FAILURE(rc))
2066 return rc;
2067 }
2068 if (pLogger->pInt->cbHistoryFileMax != UINT64_MAX)
2069 {
2070 RTStrPrintf(szNum, sizeof(szNum), fNotFirst ? "histsize=%llu" : " histsize=%llu", pLogger->pInt->cbHistoryFileMax);
2071 rc = RTStrCopyP(&pszBuf, &cchBuf, szNum);
2072 if (RT_FAILURE(rc))
2073 return rc;
2074 }
2075 if (pLogger->pInt->cSecsHistoryTimeSlot != UINT32_MAX)
2076 {
2077 RTStrPrintf(szNum, sizeof(szNum), fNotFirst ? "histtime=%llu" : " histtime=%llu", pLogger->pInt->cSecsHistoryTimeSlot);
2078 rc = RTStrCopyP(&pszBuf, &cchBuf, szNum);
2079 if (RT_FAILURE(rc))
2080 return rc;
2081 }
2082 }
2083# endif /* IN_RING3 */
2084
2085 return VINF_SUCCESS;
2086}
2087RT_EXPORT_SYMBOL(RTLogGetDestinations);
2088
2089#endif /* !IN_RC */
2090
2091/**
2092 * Flushes the specified logger.
2093 *
2094 * @param pLogger The logger instance to flush.
2095 * If NULL the default instance is used. The default instance
2096 * will not be initialized by this call.
2097 */
2098RTDECL(void) RTLogFlush(PRTLOGGER pLogger)
2099{
2100 /*
2101 * Resolve defaults.
2102 */
2103 if (!pLogger)
2104 {
2105#ifdef IN_RC
2106 pLogger = &g_Logger;
2107#else
2108 pLogger = g_pLogger;
2109#endif
2110 if (!pLogger)
2111 return;
2112 }
2113
2114 /*
2115 * Any thing to flush?
2116 */
2117 if (pLogger->offScratch)
2118 {
2119#ifndef IN_RC
2120 /*
2121 * Acquire logger instance sem.
2122 */
2123 int rc = rtlogLock(pLogger);
2124 if (RT_FAILURE(rc))
2125 return;
2126#endif
2127 /*
2128 * Call worker.
2129 */
2130 rtlogFlush(pLogger);
2131
2132#ifndef IN_RC
2133 /*
2134 * Release the semaphore.
2135 */
2136 rtlogUnlock(pLogger);
2137#endif
2138 }
2139}
2140RT_EXPORT_SYMBOL(RTLogFlush);
2141
2142
2143/**
2144 * Gets the default logger instance, creating it if necessary.
2145 *
2146 * @returns Pointer to default logger instance.
2147 * @returns NULL if no default logger instance available.
2148 */
2149RTDECL(PRTLOGGER) RTLogDefaultInstance(void)
2150{
2151#ifdef IN_RC
2152 return &g_Logger;
2153
2154#else /* !IN_RC */
2155# ifdef IN_RING0
2156 /*
2157 * Check per thread loggers first.
2158 */
2159 if (g_cPerThreadLoggers)
2160 {
2161 const RTNATIVETHREAD Self = RTThreadNativeSelf();
2162 int32_t i = RT_ELEMENTS(g_aPerThreadLoggers);
2163 while (i-- > 0)
2164 if (g_aPerThreadLoggers[i].NativeThread == Self)
2165 return g_aPerThreadLoggers[i].pLogger;
2166 }
2167# endif /* IN_RING0 */
2168
2169 /*
2170 * If no per thread logger, use the default one.
2171 */
2172 if (!g_pLogger)
2173 g_pLogger = RTLogDefaultInit();
2174 return g_pLogger;
2175#endif /* !IN_RC */
2176}
2177RT_EXPORT_SYMBOL(RTLogDefaultInstance);
2178
2179
2180/**
2181 * Gets the default logger instance.
2182 *
2183 * @returns Pointer to default logger instance.
2184 * @returns NULL if no default logger instance available.
2185 */
2186RTDECL(PRTLOGGER) RTLogGetDefaultInstance(void)
2187{
2188#ifdef IN_RC
2189 return &g_Logger;
2190#else
2191# ifdef IN_RING0
2192 /*
2193 * Check per thread loggers first.
2194 */
2195 if (g_cPerThreadLoggers)
2196 {
2197 const RTNATIVETHREAD Self = RTThreadNativeSelf();
2198 int32_t i = RT_ELEMENTS(g_aPerThreadLoggers);
2199 while (i-- > 0)
2200 if (g_aPerThreadLoggers[i].NativeThread == Self)
2201 return g_aPerThreadLoggers[i].pLogger;
2202 }
2203# endif /* IN_RING0 */
2204
2205 return g_pLogger;
2206#endif
2207}
2208RT_EXPORT_SYMBOL(RTLogGetDefaultInstance);
2209
2210
2211#ifndef IN_RC
2212/**
2213 * Sets the default logger instance.
2214 *
2215 * @returns iprt status code.
2216 * @param pLogger The new default logger instance.
2217 */
2218RTDECL(PRTLOGGER) RTLogSetDefaultInstance(PRTLOGGER pLogger)
2219{
2220 return ASMAtomicXchgPtrT(&g_pLogger, pLogger, PRTLOGGER);
2221}
2222RT_EXPORT_SYMBOL(RTLogSetDefaultInstance);
2223#endif /* !IN_RC */
2224
2225
2226#ifdef IN_RING0
2227/**
2228 * Changes the default logger instance for the current thread.
2229 *
2230 * @returns IPRT status code.
2231 * @param pLogger The logger instance. Pass NULL for deregistration.
2232 * @param uKey Associated key for cleanup purposes. If pLogger is NULL,
2233 * all instances with this key will be deregistered. So in
2234 * order to only deregister the instance associated with the
2235 * current thread use 0.
2236 */
2237RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey)
2238{
2239 int rc;
2240 RTNATIVETHREAD Self = RTThreadNativeSelf();
2241 if (pLogger)
2242 {
2243 int32_t i;
2244 unsigned j;
2245
2246 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
2247
2248 /*
2249 * Iterate the table to see if there is already an entry for this thread.
2250 */
2251 i = RT_ELEMENTS(g_aPerThreadLoggers);
2252 while (i-- > 0)
2253 if (g_aPerThreadLoggers[i].NativeThread == Self)
2254 {
2255 ASMAtomicWritePtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
2256 g_aPerThreadLoggers[i].pLogger = pLogger;
2257 return VINF_SUCCESS;
2258 }
2259
2260 /*
2261 * Allocate a new table entry.
2262 */
2263 i = ASMAtomicIncS32(&g_cPerThreadLoggers);
2264 if (i > (int32_t)RT_ELEMENTS(g_aPerThreadLoggers))
2265 {
2266 ASMAtomicDecS32(&g_cPerThreadLoggers);
2267 return VERR_BUFFER_OVERFLOW; /* horrible error code! */
2268 }
2269
2270 for (j = 0; j < 10; j++)
2271 {
2272 i = RT_ELEMENTS(g_aPerThreadLoggers);
2273 while (i-- > 0)
2274 {
2275 AssertCompile(sizeof(RTNATIVETHREAD) == sizeof(void*));
2276 if ( g_aPerThreadLoggers[i].NativeThread == NIL_RTNATIVETHREAD
2277 && ASMAtomicCmpXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)Self, (void *)NIL_RTNATIVETHREAD))
2278 {
2279 ASMAtomicWritePtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
2280 ASMAtomicWritePtr(&g_aPerThreadLoggers[i].pLogger, pLogger);
2281 return VINF_SUCCESS;
2282 }
2283 }
2284 }
2285
2286 ASMAtomicDecS32(&g_cPerThreadLoggers);
2287 rc = VERR_INTERNAL_ERROR;
2288 }
2289 else
2290 {
2291 /*
2292 * Search the array for the current thread.
2293 */
2294 int32_t i = RT_ELEMENTS(g_aPerThreadLoggers);
2295 while (i-- > 0)
2296 if ( g_aPerThreadLoggers[i].NativeThread == Self
2297 || g_aPerThreadLoggers[i].uKey == uKey)
2298 {
2299 ASMAtomicWriteNullPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey);
2300 ASMAtomicWriteNullPtr(&g_aPerThreadLoggers[i].pLogger);
2301 ASMAtomicWriteHandle(&g_aPerThreadLoggers[i].NativeThread, NIL_RTNATIVETHREAD);
2302 ASMAtomicDecS32(&g_cPerThreadLoggers);
2303 }
2304
2305 rc = VINF_SUCCESS;
2306 }
2307 return rc;
2308}
2309RT_EXPORT_SYMBOL(RTLogSetDefaultInstanceThread);
2310#endif /* IN_RING0 */
2311
2312
2313/**
2314 * Write to a logger instance.
2315 *
2316 * @param pLogger Pointer to logger instance.
2317 * @param pszFormat Format string.
2318 * @param args Format arguments.
2319 */
2320RTDECL(void) RTLogLoggerV(PRTLOGGER pLogger, const char *pszFormat, va_list args)
2321{
2322 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
2323}
2324RT_EXPORT_SYMBOL(RTLogLoggerV);
2325
2326
2327/**
2328 * Write to a logger instance.
2329 *
2330 * This function will check whether the instance, group and flags makes up a
2331 * logging kind which is currently enabled before writing anything to the log.
2332 *
2333 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.
2334 * @param fFlags The logging flags.
2335 * @param iGroup The group.
2336 * The value ~0U is reserved for compatibility with RTLogLogger[V] and is
2337 * only for internal usage!
2338 * @param pszFormat Format string.
2339 * @param args Format arguments.
2340 */
2341RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
2342{
2343 int rc;
2344
2345 /*
2346 * A NULL logger means default instance.
2347 */
2348 if (!pLogger)
2349 {
2350 pLogger = RTLogDefaultInstance();
2351 if (!pLogger)
2352 return;
2353 }
2354
2355 /*
2356 * Validate and correct iGroup.
2357 */
2358 if (iGroup != ~0U && iGroup >= pLogger->cGroups)
2359 iGroup = 0;
2360
2361 /*
2362 * If no output, then just skip it.
2363 */
2364 if ( (pLogger->fFlags & RTLOGFLAGS_DISABLED)
2365#ifndef IN_RC
2366 || !pLogger->fDestFlags
2367#endif
2368 || !pszFormat || !*pszFormat)
2369 return;
2370 if ( iGroup != ~0U
2371 && (pLogger->afGroups[iGroup] & (fFlags | RTLOGGRPFLAGS_ENABLED)) != (fFlags | RTLOGGRPFLAGS_ENABLED))
2372 return;
2373
2374 /*
2375 * Acquire logger instance sem.
2376 */
2377 rc = rtlogLock(pLogger);
2378 if (RT_FAILURE(rc))
2379 {
2380#ifdef IN_RING0
2381 if (pLogger->fDestFlags & ~RTLOGDEST_FILE)
2382 rtR0LogLoggerExFallback(pLogger->fDestFlags, pLogger->fFlags, pszFormat, args);
2383#endif
2384 return;
2385 }
2386
2387 /*
2388 * Check restrictions and call worker.
2389 */
2390#ifndef IN_RC
2391 if (RT_UNLIKELY( (pLogger->fFlags & RTLOGFLAGS_RESTRICT_GROUPS)
2392 && iGroup < pLogger->cGroups
2393 && (pLogger->afGroups[iGroup] & RTLOGGRPFLAGS_RESTRICT)
2394 && ++pLogger->pInt->pacEntriesPerGroup[iGroup] >= pLogger->pInt->cMaxEntriesPerGroup ))
2395 {
2396 uint32_t cEntries = pLogger->pInt->pacEntriesPerGroup[iGroup];
2397 if (cEntries > pLogger->pInt->cMaxEntriesPerGroup)
2398 pLogger->pInt->pacEntriesPerGroup[iGroup] = cEntries - 1;
2399 else
2400 {
2401 rtlogLoggerExVLocked(pLogger, fFlags, iGroup, pszFormat, args);
2402 if ( pLogger->pInt->papszGroups
2403 && pLogger->pInt->papszGroups[iGroup])
2404 rtlogLoggerExFLocked(pLogger, fFlags, iGroup, "%u messages from group %s (#%u), muting it.\n",
2405 cEntries, pLogger->pInt->papszGroups[iGroup], iGroup);
2406 else
2407 rtlogLoggerExFLocked(pLogger, fFlags, iGroup, "%u messages from group #%u, muting it.\n",
2408 cEntries, iGroup);
2409 }
2410 }
2411 else
2412#endif
2413 rtlogLoggerExVLocked(pLogger, fFlags, iGroup, pszFormat, args);
2414
2415 /*
2416 * Release the semaphore.
2417 */
2418 rtlogUnlock(pLogger);
2419}
2420RT_EXPORT_SYMBOL(RTLogLoggerExV);
2421
2422
2423#ifdef IN_RING0
2424/**
2425 * For rtR0LogLoggerExFallbackOutput and rtR0LogLoggerExFallbackFlush.
2426 */
2427typedef struct RTR0LOGLOGGERFALLBACK
2428{
2429 /** The current scratch buffer offset. */
2430 uint32_t offScratch;
2431 /** The destination flags. */
2432 uint32_t fDestFlags;
2433 /** The scratch buffer. */
2434 char achScratch[80];
2435} RTR0LOGLOGGERFALLBACK;
2436/** Pointer to RTR0LOGLOGGERFALLBACK which is used by
2437 * rtR0LogLoggerExFallbackOutput. */
2438typedef RTR0LOGLOGGERFALLBACK *PRTR0LOGLOGGERFALLBACK;
2439
2440
2441/**
2442 * Flushes the fallback buffer.
2443 *
2444 * @param pThis The scratch buffer.
2445 */
2446static void rtR0LogLoggerExFallbackFlush(PRTR0LOGLOGGERFALLBACK pThis)
2447{
2448 if (!pThis->offScratch)
2449 return;
2450
2451 if (pThis->fDestFlags & RTLOGDEST_USER)
2452 RTLogWriteUser(pThis->achScratch, pThis->offScratch);
2453
2454 if (pThis->fDestFlags & RTLOGDEST_DEBUGGER)
2455 RTLogWriteDebugger(pThis->achScratch, pThis->offScratch);
2456
2457 if (pThis->fDestFlags & RTLOGDEST_STDOUT)
2458 RTLogWriteStdOut(pThis->achScratch, pThis->offScratch);
2459
2460 if (pThis->fDestFlags & RTLOGDEST_STDERR)
2461 RTLogWriteStdErr(pThis->achScratch, pThis->offScratch);
2462
2463# ifndef LOG_NO_COM
2464 if (pThis->fDestFlags & RTLOGDEST_COM)
2465 RTLogWriteCom(pThis->achScratch, pThis->offScratch);
2466# endif
2467
2468 /* empty the buffer. */
2469 pThis->offScratch = 0;
2470}
2471
2472
2473/**
2474 * Callback for RTLogFormatV used by rtR0LogLoggerExFallback.
2475 * See PFNLOGOUTPUT() for details.
2476 */
2477static DECLCALLBACK(size_t) rtR0LogLoggerExFallbackOutput(void *pv, const char *pachChars, size_t cbChars)
2478{
2479 PRTR0LOGLOGGERFALLBACK pThis = (PRTR0LOGLOGGERFALLBACK)pv;
2480 if (cbChars)
2481 {
2482 size_t cbRet = 0;
2483 for (;;)
2484 {
2485 /* how much */
2486 uint32_t cb = sizeof(pThis->achScratch) - pThis->offScratch - 1; /* minus 1 - for the string terminator. */
2487 if (cb > cbChars)
2488 cb = (uint32_t)cbChars;
2489
2490 /* copy */
2491 memcpy(&pThis->achScratch[pThis->offScratch], pachChars, cb);
2492
2493 /* advance */
2494 pThis->offScratch += cb;
2495 cbRet += cb;
2496 cbChars -= cb;
2497
2498 /* done? */
2499 if (cbChars <= 0)
2500 return cbRet;
2501
2502 pachChars += cb;
2503
2504 /* flush */
2505 pThis->achScratch[pThis->offScratch] = '\0';
2506 rtR0LogLoggerExFallbackFlush(pThis);
2507 }
2508
2509 /* won't ever get here! */
2510 }
2511 else
2512 {
2513 /*
2514 * Termination call, flush the log.
2515 */
2516 pThis->achScratch[pThis->offScratch] = '\0';
2517 rtR0LogLoggerExFallbackFlush(pThis);
2518 return 0;
2519 }
2520}
2521
2522
2523/**
2524 * Ring-0 fallback for cases where we're unable to grab the lock.
2525 *
2526 * This will happen when we're at a too high IRQL on Windows for instance and
2527 * needs to be dealt with or we'll drop a lot of log output. This fallback will
2528 * only output to some of the log destinations as a few of them may be doing
2529 * dangerous things. We won't be doing any prefixing here either, at least not
2530 * for the present, because it's too much hassle.
2531 *
2532 * @param fDestFlags The destination flags.
2533 * @param fFlags The logger flags.
2534 * @param pszFormat The format string.
2535 * @param va The format arguments.
2536 */
2537static void rtR0LogLoggerExFallback(uint32_t fDestFlags, uint32_t fFlags, const char *pszFormat, va_list va)
2538{
2539 RTR0LOGLOGGERFALLBACK This;
2540 This.fDestFlags = fDestFlags;
2541
2542 /* fallback indicator. */
2543 This.offScratch = 2;
2544 This.achScratch[0] = '[';
2545 This.achScratch[1] = 'F';
2546
2547 /* selected prefixes */
2548 if (fFlags & RTLOGFLAGS_PREFIX_PID)
2549 {
2550 RTPROCESS Process = RTProcSelf();
2551 This.achScratch[This.offScratch++] = ' ';
2552 This.offScratch += RTStrFormatNumber(&This.achScratch[This.offScratch], Process, 16, sizeof(RTPROCESS) * 2, 0, RTSTR_F_ZEROPAD);
2553 }
2554 if (fFlags & RTLOGFLAGS_PREFIX_TID)
2555 {
2556 RTNATIVETHREAD Thread = RTThreadNativeSelf();
2557 This.achScratch[This.offScratch++] = ' ';
2558 This.offScratch += RTStrFormatNumber(&This.achScratch[This.offScratch], Thread, 16, sizeof(RTNATIVETHREAD) * 2, 0, RTSTR_F_ZEROPAD);
2559 }
2560
2561 This.achScratch[This.offScratch++] = ']';
2562 This.achScratch[This.offScratch++] = ' ';
2563
2564 RTLogFormatV(rtR0LogLoggerExFallbackOutput, &This, pszFormat, va);
2565}
2566#endif /* IN_RING0 */
2567
2568
2569/**
2570 * vprintf like function for writing to the default log.
2571 *
2572 * @param pszFormat Printf like format string.
2573 * @param args Optional arguments as specified in pszFormat.
2574 *
2575 * @remark The API doesn't support formatting of floating point numbers at the moment.
2576 */
2577RTDECL(void) RTLogPrintfV(const char *pszFormat, va_list args)
2578{
2579 RTLogLoggerV(NULL, pszFormat, args);
2580}
2581RT_EXPORT_SYMBOL(RTLogPrintfV);
2582
2583#ifdef IN_RING3
2584
2585/**
2586 * Opens/creates the log file.
2587 *
2588 * @param pLogger The logger instance to update. NULL is not allowed!
2589 * @param pszErrorMsg A buffer which is filled with an error message if
2590 * something fails. May be NULL.
2591 * @param cchErrorMsg The size of the error message buffer.
2592 */
2593static int rtlogFileOpen(PRTLOGGER pLogger, char *pszErrorMsg, size_t cchErrorMsg)
2594{
2595 uint32_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
2596 if (pLogger->fFlags & RTLOGFLAGS_APPEND)
2597 fOpen |= RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND;
2598 else
2599 fOpen |= RTFILE_O_CREATE_REPLACE;
2600 if (pLogger->fFlags & RTLOGFLAGS_WRITE_THROUGH)
2601 fOpen |= RTFILE_O_WRITE_THROUGH;
2602
2603 int rc = RTFileOpen(&pLogger->pInt->hFile, pLogger->pInt->szFilename, fOpen);
2604 if (RT_FAILURE(rc))
2605 {
2606 pLogger->pInt->hFile = NIL_RTFILE;
2607 if (pszErrorMsg)
2608 RTStrPrintf(pszErrorMsg, cchErrorMsg, N_("could not open file '%s' (fOpen=%#x)"), pLogger->pInt->szFilename, fOpen);
2609 }
2610 else
2611 {
2612 rc = RTFileGetSize(pLogger->pInt->hFile, &pLogger->pInt->cbHistoryFileWritten);
2613 if (RT_FAILURE(rc))
2614 {
2615 /* Don't complain if this fails, assume the file is empty. */
2616 pLogger->pInt->cbHistoryFileWritten = 0;
2617 rc = VINF_SUCCESS;
2618 }
2619 }
2620 return rc;
2621}
2622
2623
2624/**
2625 * Closes, rotates and opens the log files if necessary.
2626 *
2627 * Used by the rtlogFlush() function as well as RTLogCreateExV.
2628 *
2629 * @param pLogger The logger instance to update. NULL is not allowed!
2630 * @param uTimeSlit Current time slot (for tikme based rotation).
2631 * @param fFirst Flag whether this is the beginning of logging, i.e.
2632 * called from RTLogCreateExV. Prevents pfnPhase from
2633 * being called.
2634 */
2635static void rtlogRotate(PRTLOGGER pLogger, uint32_t uTimeSlot, bool fFirst)
2636{
2637 /* Suppress rotating empty log files simply because the time elapsed. */
2638 if (RT_UNLIKELY(!pLogger->pInt->cbHistoryFileWritten))
2639 pLogger->pInt->uHistoryTimeSlotStart = uTimeSlot;
2640
2641 /* Check rotation condition: file still small enough and not too old? */
2642 if (RT_LIKELY( pLogger->pInt->cbHistoryFileWritten < pLogger->pInt->cbHistoryFileMax
2643 && uTimeSlot == pLogger->pInt->uHistoryTimeSlotStart))
2644 return;
2645
2646 /*
2647 * Save "disabled" log flag and make sure logging is disabled.
2648 * The logging in the functions called during log file history
2649 * rotation would cause severe trouble otherwise.
2650 */
2651 uint32_t const fSavedFlags = pLogger->fFlags;
2652 pLogger->fFlags |= RTLOGFLAGS_DISABLED;
2653
2654 /*
2655 * Disable log rotation temporarily, otherwise with extreme settings and
2656 * chatty phase logging we could run into endless rotation.
2657 */
2658 uint32_t const cSavedHistory = pLogger->pInt->cHistory;
2659 pLogger->pInt->cHistory = 0;
2660
2661 /*
2662 * Close the old log file.
2663 */
2664 if (pLogger->pInt->hFile != NIL_RTFILE)
2665 {
2666 /* Use the callback to generate some final log contents, but only if
2667 * this is a rotation with a fully set up logger. Leave the other case
2668 * to the RTLogCreateExV function. */
2669 if (pLogger->pInt->pfnPhase && !fFirst)
2670 {
2671 uint32_t fODestFlags = pLogger->fDestFlags;
2672 pLogger->fDestFlags &= RTLOGDEST_FILE;
2673 pLogger->pInt->pfnPhase(pLogger, RTLOGPHASE_PREROTATE, rtlogPhaseMsgLocked);
2674 pLogger->fDestFlags = fODestFlags;
2675 }
2676 RTFileClose(pLogger->pInt->hFile);
2677 pLogger->pInt->hFile = NIL_RTFILE;
2678 }
2679
2680 if (cSavedHistory)
2681 {
2682 /*
2683 * Rotate the log files.
2684 */
2685 for (uint32_t i = cSavedHistory - 1; i + 1 > 0; i--)
2686 {
2687 char szOldName[sizeof(pLogger->pInt->szFilename) + 32];
2688 if (i > 0)
2689 RTStrPrintf(szOldName, sizeof(szOldName), "%s.%u", pLogger->pInt->szFilename, i);
2690 else
2691 RTStrCopy(szOldName, sizeof(szOldName), pLogger->pInt->szFilename);
2692
2693 char szNewName[sizeof(pLogger->pInt->szFilename) + 32];
2694 RTStrPrintf(szNewName, sizeof(szNewName), "%s.%u", pLogger->pInt->szFilename, i + 1);
2695 if ( RTFileRename(szOldName, szNewName, RTFILEMOVE_FLAGS_REPLACE)
2696 == VERR_FILE_NOT_FOUND)
2697 RTFileDelete(szNewName);
2698 }
2699
2700 /*
2701 * Delete excess log files.
2702 */
2703 for (uint32_t i = cSavedHistory + 1; ; i++)
2704 {
2705 char szExcessName[sizeof(pLogger->pInt->szFilename) + 32];
2706 RTStrPrintf(szExcessName, sizeof(szExcessName), "%s.%u", pLogger->pInt->szFilename, i);
2707 int rc = RTFileDelete(szExcessName);
2708 if (RT_FAILURE(rc))
2709 break;
2710 }
2711 }
2712
2713 /*
2714 * Update logger state and create new log file.
2715 */
2716 pLogger->pInt->cbHistoryFileWritten = 0;
2717 pLogger->pInt->uHistoryTimeSlotStart = uTimeSlot;
2718 rtlogFileOpen(pLogger, NULL, 0);
2719
2720 /*
2721 * Use the callback to generate some initial log contents, but only if this
2722 * is a rotation with a fully set up logger. Leave the other case to the
2723 * RTLogCreateExV function.
2724 */
2725 if (pLogger->pInt->pfnPhase && !fFirst)
2726 {
2727 uint32_t const fSavedDestFlags = pLogger->fDestFlags;
2728 pLogger->fDestFlags &= RTLOGDEST_FILE;
2729 pLogger->pInt->pfnPhase(pLogger, RTLOGPHASE_POSTROTATE, rtlogPhaseMsgLocked);
2730 pLogger->fDestFlags = fSavedDestFlags;
2731 }
2732
2733 /* Restore saved values. */
2734 pLogger->pInt->cHistory = cSavedHistory;
2735 pLogger->fFlags = fSavedFlags;
2736}
2737
2738#endif /* IN_RING3 */
2739
2740/**
2741 * Writes the buffer to the given log device without checking for buffered
2742 * data or anything.
2743 * Used by the RTLogFlush() function.
2744 *
2745 * @param pLogger The logger instance to write to. NULL is not allowed!
2746 */
2747static void rtlogFlush(PRTLOGGER pLogger)
2748{
2749 if (pLogger->offScratch == 0)
2750 return; /* nothing to flush. */
2751
2752#ifndef IN_RC
2753 if (pLogger->fDestFlags & RTLOGDEST_USER)
2754 RTLogWriteUser(pLogger->achScratch, pLogger->offScratch);
2755
2756 if (pLogger->fDestFlags & RTLOGDEST_DEBUGGER)
2757 RTLogWriteDebugger(pLogger->achScratch, pLogger->offScratch);
2758
2759# ifdef IN_RING3
2760 if (pLogger->fDestFlags & RTLOGDEST_FILE)
2761 {
2762 if (pLogger->pInt->hFile != NIL_RTFILE)
2763 {
2764 RTFileWrite(pLogger->pInt->hFile, pLogger->achScratch, pLogger->offScratch, NULL);
2765 if (pLogger->fFlags & RTLOGFLAGS_FLUSH)
2766 RTFileFlush(pLogger->pInt->hFile);
2767 }
2768 if (pLogger->pInt->cHistory)
2769 pLogger->pInt->cbHistoryFileWritten += pLogger->offScratch;
2770 }
2771# endif
2772
2773 if (pLogger->fDestFlags & RTLOGDEST_STDOUT)
2774 RTLogWriteStdOut(pLogger->achScratch, pLogger->offScratch);
2775
2776 if (pLogger->fDestFlags & RTLOGDEST_STDERR)
2777 RTLogWriteStdErr(pLogger->achScratch, pLogger->offScratch);
2778
2779# if (defined(IN_RING0) || defined(IN_RC)) && !defined(LOG_NO_COM)
2780 if (pLogger->fDestFlags & RTLOGDEST_COM)
2781 RTLogWriteCom(pLogger->achScratch, pLogger->offScratch);
2782# endif
2783#endif /* !IN_RC */
2784
2785#ifdef IN_RC
2786 if (pLogger->pfnFlush)
2787 pLogger->pfnFlush(pLogger);
2788#else
2789 if (pLogger->pInt->pfnFlush)
2790 pLogger->pInt->pfnFlush(pLogger);
2791#endif
2792
2793 /* empty the buffer. */
2794 pLogger->offScratch = 0;
2795
2796#ifdef IN_RING3
2797 /*
2798 * Rotate the log file if configured. Must be done after everything is
2799 * flushed, since this will also use logging/flushing to write the header
2800 * and footer messages.
2801 */
2802 if ( (pLogger->fDestFlags & RTLOGDEST_FILE)
2803 && pLogger->pInt->cHistory)
2804 rtlogRotate(pLogger, RTTimeProgramSecTS() / pLogger->pInt->cSecsHistoryTimeSlot, false /* fFirst */);
2805#endif
2806}
2807
2808
2809/**
2810 * Callback for RTLogFormatV which writes to the com port.
2811 * See PFNLOGOUTPUT() for details.
2812 */
2813static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars)
2814{
2815 PRTLOGGER pLogger = (PRTLOGGER)pv;
2816 if (cbChars)
2817 {
2818 size_t cbRet = 0;
2819 for (;;)
2820 {
2821#if defined(DEBUG) && defined(IN_RING3)
2822 /* sanity */
2823 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
2824 {
2825 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
2826 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
2827 AssertBreakpoint(); AssertBreakpoint();
2828 }
2829#endif
2830
2831 /* how much */
2832 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2833 if (cb > cbChars)
2834 cb = cbChars;
2835
2836 /* copy */
2837 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
2838
2839 /* advance */
2840 pLogger->offScratch += (uint32_t)cb;
2841 cbRet += cb;
2842 cbChars -= cb;
2843
2844 /* done? */
2845 if (cbChars <= 0)
2846 return cbRet;
2847
2848 pachChars += cb;
2849
2850 /* flush */
2851 rtlogFlush(pLogger);
2852 }
2853
2854 /* won't ever get here! */
2855 }
2856 else
2857 {
2858 /*
2859 * Termination call.
2860 * There's always space for a terminator, and it's not counted.
2861 */
2862 pLogger->achScratch[pLogger->offScratch] = '\0';
2863 return 0;
2864 }
2865}
2866
2867
2868
2869/**
2870 * Callback for RTLogFormatV which writes to the logger instance.
2871 * This version supports prefixes.
2872 *
2873 * See PFNLOGOUTPUT() for details.
2874 */
2875static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars)
2876{
2877 PRTLOGOUTPUTPREFIXEDARGS pArgs = (PRTLOGOUTPUTPREFIXEDARGS)pv;
2878 PRTLOGGER pLogger = pArgs->pLogger;
2879 if (cbChars)
2880 {
2881 size_t cbRet = 0;
2882 for (;;)
2883 {
2884 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2885 const char *pszNewLine;
2886 char *psz;
2887#ifdef IN_RC
2888 bool *pfPendingPrefix = &pLogger->fPendingPrefix;
2889#else
2890 bool *pfPendingPrefix = &pLogger->pInt->fPendingPrefix;
2891#endif
2892
2893 /*
2894 * Pending prefix?
2895 */
2896 if (*pfPendingPrefix)
2897 {
2898 *pfPendingPrefix = false;
2899
2900#if defined(DEBUG) && defined(IN_RING3)
2901 /* sanity */
2902 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
2903 {
2904 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
2905 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
2906 AssertBreakpoint(); AssertBreakpoint();
2907 }
2908#endif
2909
2910 /*
2911 * Flush the buffer if there isn't enough room for the maximum prefix config.
2912 * Max is 256, add a couple of extra bytes.
2913 */
2914 if (cb < 256 + 16)
2915 {
2916 rtlogFlush(pLogger);
2917 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
2918 }
2919
2920 /*
2921 * Write the prefixes.
2922 * psz is pointing to the current position.
2923 */
2924 psz = &pLogger->achScratch[pLogger->offScratch];
2925 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TS)
2926 {
2927 uint64_t u64 = RTTimeNanoTS();
2928 int iBase = 16;
2929 unsigned int fFlags = RTSTR_F_ZEROPAD;
2930 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
2931 {
2932 iBase = 10;
2933 fFlags = 0;
2934 }
2935 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
2936 {
2937 static volatile uint64_t s_u64LastTs;
2938 uint64_t u64DiffTs = u64 - s_u64LastTs;
2939 s_u64LastTs = u64;
2940 /* We could have been preempted just before reading of s_u64LastTs by
2941 * another thread which wrote s_u64LastTs. In that case the difference
2942 * is negative which we simply ignore. */
2943 u64 = (int64_t)u64DiffTs < 0 ? 0 : u64DiffTs;
2944 }
2945 /* 1E15 nanoseconds = 11 days */
2946 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags); /* +17 */
2947 *psz++ = ' ';
2948 }
2949 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TSC)
2950 {
2951#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
2952 uint64_t u64 = ASMReadTSC();
2953#else
2954 uint64_t u64 = RTTimeNanoTS();
2955#endif
2956 int iBase = 16;
2957 unsigned int fFlags = RTSTR_F_ZEROPAD;
2958 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
2959 {
2960 iBase = 10;
2961 fFlags = 0;
2962 }
2963 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
2964 {
2965 static volatile uint64_t s_u64LastTsc;
2966 int64_t i64DiffTsc = u64 - s_u64LastTsc;
2967 s_u64LastTsc = u64;
2968 /* We could have been preempted just before reading of s_u64LastTsc by
2969 * another thread which wrote s_u64LastTsc. In that case the difference
2970 * is negative which we simply ignore. */
2971 u64 = i64DiffTsc < 0 ? 0 : i64DiffTsc;
2972 }
2973 /* 1E15 ticks at 4GHz = 69 hours */
2974 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags); /* +17 */
2975 *psz++ = ' ';
2976 }
2977 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_MS_PROG)
2978 {
2979#if defined(IN_RING3) || defined(IN_RC)
2980 uint64_t u64 = RTTimeProgramMilliTS();
2981#else
2982 uint64_t u64 = 0;
2983#endif
2984 /* 1E8 milliseconds = 27 hours */
2985 psz += RTStrFormatNumber(psz, u64, 10, 9, 0, RTSTR_F_ZEROPAD);
2986 *psz++ = ' ';
2987 }
2988 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME)
2989 {
2990#if defined(IN_RING3) || defined(IN_RING0)
2991 RTTIMESPEC TimeSpec;
2992 RTTIME Time;
2993 RTTimeExplode(&Time, RTTimeNow(&TimeSpec));
2994 psz += RTStrFormatNumber(psz, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD);
2995 *psz++ = ':';
2996 psz += RTStrFormatNumber(psz, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD);
2997 *psz++ = ':';
2998 psz += RTStrFormatNumber(psz, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD);
2999 *psz++ = '.';
3000 psz += RTStrFormatNumber(psz, Time.u32Nanosecond / 1000000, 10, 3, 0, RTSTR_F_ZEROPAD);
3001 *psz++ = ' '; /* +17 (3+1+3+1+3+1+4+1) */
3002#else
3003 memset(psz, ' ', 13);
3004 psz += 13;
3005#endif
3006 }
3007 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME_PROG)
3008 {
3009#if defined(IN_RING3) || defined(IN_RC)
3010 uint64_t u64 = RTTimeProgramMilliTS();
3011 psz += RTStrFormatNumber(psz, (uint32_t)(u64 / (60 * 60 * 1000)), 10, 2, 0, RTSTR_F_ZEROPAD);
3012 *psz++ = ':';
3013 uint32_t u32 = (uint32_t)(u64 % (60 * 60 * 1000));
3014 psz += RTStrFormatNumber(psz, u32 / (60 * 1000), 10, 2, 0, RTSTR_F_ZEROPAD);
3015 *psz++ = ':';
3016 u32 %= 60 * 1000;
3017 psz += RTStrFormatNumber(psz, u32 / 1000, 10, 2, 0, RTSTR_F_ZEROPAD);
3018 *psz++ = '.';
3019 psz += RTStrFormatNumber(psz, u32 % 1000, 10, 3, 0, RTSTR_F_ZEROPAD);
3020 *psz++ = ' '; /* +20 (9+1+2+1+2+1+3+1) */
3021#else
3022 memset(psz, ' ', 13);
3023 psz += 13;
3024#endif
3025 }
3026# if 0
3027 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_DATETIME)
3028 {
3029 char szDate[32];
3030 RTTIMESPEC Time;
3031 RTTimeSpecToString(RTTimeNow(&Time), szDate, sizeof(szDate));
3032 size_t cch = strlen(szDate);
3033 memcpy(psz, szDate, cch);
3034 psz += cch;
3035 *psz++ = ' '; /* +32 */
3036 }
3037# endif
3038 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_PID)
3039 {
3040#ifndef IN_RC
3041 RTPROCESS Process = RTProcSelf();
3042#else
3043 RTPROCESS Process = NIL_RTPROCESS;
3044#endif
3045 psz += RTStrFormatNumber(psz, Process, 16, sizeof(RTPROCESS) * 2, 0, RTSTR_F_ZEROPAD);
3046 *psz++ = ' '; /* +9 */
3047 }
3048 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TID)
3049 {
3050#ifndef IN_RC
3051 RTNATIVETHREAD Thread = RTThreadNativeSelf();
3052#else
3053 RTNATIVETHREAD Thread = NIL_RTNATIVETHREAD;
3054#endif
3055 psz += RTStrFormatNumber(psz, Thread, 16, sizeof(RTNATIVETHREAD) * 2, 0, RTSTR_F_ZEROPAD);
3056 *psz++ = ' '; /* +17 */
3057 }
3058 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_THREAD)
3059 {
3060#ifdef IN_RING3
3061 const char *pszName = RTThreadSelfName();
3062#elif defined IN_RC
3063 const char *pszName = "EMT-RC";
3064#else
3065 const char *pszName = "R0";
3066#endif
3067 size_t cch = 0;
3068 if (pszName)
3069 {
3070 cch = strlen(pszName);
3071 cch = RT_MIN(cch, 16);
3072 memcpy(psz, pszName, cch);
3073 psz += cch;
3074 }
3075 do
3076 *psz++ = ' ';
3077 while (cch++ < 8); /* +17 */
3078 }
3079 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_CPUID)
3080 {
3081#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
3082 const uint8_t idCpu = ASMGetApicId();
3083#else
3084 const RTCPUID idCpu = RTMpCpuId();
3085#endif
3086 psz += RTStrFormatNumber(psz, idCpu, 16, sizeof(idCpu) * 2, 0, RTSTR_F_ZEROPAD);
3087 *psz++ = ' '; /* +17 */
3088 }
3089#ifndef IN_RC
3090 if ( (pLogger->fFlags & RTLOGFLAGS_PREFIX_CUSTOM)
3091 && pLogger->pInt->pfnPrefix)
3092 {
3093 psz += pLogger->pInt->pfnPrefix(pLogger, psz, 31, pLogger->pInt->pvPrefixUserArg);
3094 *psz++ = ' '; /* +32 */
3095 }
3096#endif
3097 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_LOCK_COUNTS)
3098 {
3099#ifdef IN_RING3 /** @todo implement these counters in ring-0 too? */
3100 RTTHREAD Thread = RTThreadSelf();
3101 if (Thread != NIL_RTTHREAD)
3102 {
3103 uint32_t cReadLocks = RTLockValidatorReadLockGetCount(Thread);
3104 uint32_t cWriteLocks = RTLockValidatorWriteLockGetCount(Thread) - g_cLoggerLockCount;
3105 cReadLocks = RT_MIN(0xfff, cReadLocks);
3106 cWriteLocks = RT_MIN(0xfff, cWriteLocks);
3107 psz += RTStrFormatNumber(psz, cReadLocks, 16, 1, 0, RTSTR_F_ZEROPAD);
3108 *psz++ = '/';
3109 psz += RTStrFormatNumber(psz, cWriteLocks, 16, 1, 0, RTSTR_F_ZEROPAD);
3110 }
3111 else
3112#endif
3113 {
3114 *psz++ = '?';
3115 *psz++ = '/';
3116 *psz++ = '?';
3117 }
3118 *psz++ = ' '; /* +8 */
3119 }
3120 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG_NO)
3121 {
3122 psz += RTStrFormatNumber(psz, pArgs->fFlags, 16, 8, 0, RTSTR_F_ZEROPAD);
3123 *psz++ = ' '; /* +9 */
3124 }
3125 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG)
3126 {
3127#ifdef IN_RING3
3128 const char *pszGroup = pArgs->iGroup != ~0U ? pLogger->pInt->papszGroups[pArgs->iGroup] : NULL;
3129#else
3130 const char *pszGroup = NULL;
3131#endif
3132 size_t cch = 0;
3133 if (pszGroup)
3134 {
3135 cch = strlen(pszGroup);
3136 cch = RT_MIN(cch, 16);
3137 memcpy(psz, pszGroup, cch);
3138 psz += cch;
3139 }
3140 do
3141 *psz++ = ' ';
3142 while (cch++ < 8); /* +17 */
3143 }
3144 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP_NO)
3145 {
3146 if (pArgs->iGroup != ~0U)
3147 {
3148 psz += RTStrFormatNumber(psz, pArgs->iGroup, 16, 3, 0, RTSTR_F_ZEROPAD);
3149 *psz++ = ' ';
3150 }
3151 else
3152 {
3153 memcpy(psz, "-1 ", sizeof("-1 ") - 1);
3154 psz += sizeof("-1 ") - 1;
3155 } /* +9 */
3156 }
3157 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP)
3158 {
3159 const unsigned fGrp = pLogger->afGroups[pArgs->iGroup != ~0U ? pArgs->iGroup : 0];
3160 const char *pszGroup;
3161 size_t cch;
3162 switch (pArgs->fFlags & fGrp)
3163 {
3164 case 0: pszGroup = "--------"; cch = sizeof("--------") - 1; break;
3165 case RTLOGGRPFLAGS_ENABLED: pszGroup = "enabled" ; cch = sizeof("enabled" ) - 1; break;
3166 case RTLOGGRPFLAGS_LEVEL_1: pszGroup = "level 1" ; cch = sizeof("level 1" ) - 1; break;
3167 case RTLOGGRPFLAGS_LEVEL_2: pszGroup = "level 2" ; cch = sizeof("level 2" ) - 1; break;
3168 case RTLOGGRPFLAGS_LEVEL_3: pszGroup = "level 3" ; cch = sizeof("level 3" ) - 1; break;
3169 case RTLOGGRPFLAGS_LEVEL_4: pszGroup = "level 4" ; cch = sizeof("level 4" ) - 1; break;
3170 case RTLOGGRPFLAGS_LEVEL_5: pszGroup = "level 5" ; cch = sizeof("level 5" ) - 1; break;
3171 case RTLOGGRPFLAGS_LEVEL_6: pszGroup = "level 6" ; cch = sizeof("level 6" ) - 1; break;
3172 case RTLOGGRPFLAGS_FLOW: pszGroup = "flow" ; cch = sizeof("flow" ) - 1; break;
3173
3174 /* personal groups */
3175 case RTLOGGRPFLAGS_LELIK: pszGroup = "lelik" ; cch = sizeof("lelik" ) - 1; break;
3176 case RTLOGGRPFLAGS_MICHAEL: pszGroup = "Michael" ; cch = sizeof("Michael" ) - 1; break;
3177 case RTLOGGRPFLAGS_SUNLOVER: pszGroup = "sunlover"; cch = sizeof("sunlover") - 1; break;
3178 case RTLOGGRPFLAGS_ACHIM: pszGroup = "Achim" ; cch = sizeof("Achim" ) - 1; break;
3179 case RTLOGGRPFLAGS_SANDER: pszGroup = "Sander" ; cch = sizeof("Sander" ) - 1; break;
3180 case RTLOGGRPFLAGS_KLAUS: pszGroup = "Klaus" ; cch = sizeof("Klaus" ) - 1; break;
3181 case RTLOGGRPFLAGS_FRANK: pszGroup = "Frank" ; cch = sizeof("Frank" ) - 1; break;
3182 case RTLOGGRPFLAGS_BIRD: pszGroup = "bird" ; cch = sizeof("bird" ) - 1; break;
3183 case RTLOGGRPFLAGS_NONAME: pszGroup = "noname" ; cch = sizeof("noname" ) - 1; break;
3184 default: pszGroup = "????????"; cch = sizeof("????????") - 1; break;
3185 }
3186 if (pszGroup)
3187 {
3188 cch = RT_MIN(cch, 16);
3189 memcpy(psz, pszGroup, cch);
3190 psz += cch;
3191 }
3192 do
3193 *psz++ = ' ';
3194 while (cch++ < 8); /* +17 */
3195 }
3196
3197 /*
3198 * Done, figure what we've used and advance the buffer and free size.
3199 */
3200 cb = psz - &pLogger->achScratch[pLogger->offScratch];
3201 AssertMsg(cb <= 223, ("%#zx (%zd) - fFlags=%#x\n", cb, cb, pLogger->fFlags));
3202 pLogger->offScratch += (uint32_t)cb;
3203 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
3204 }
3205 else if (cb <= 0)
3206 {
3207 rtlogFlush(pLogger);
3208 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
3209 }
3210
3211#if defined(DEBUG) && defined(IN_RING3)
3212 /* sanity */
3213 if (pLogger->offScratch >= sizeof(pLogger->achScratch))
3214 {
3215 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
3216 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
3217 AssertBreakpoint(); AssertBreakpoint();
3218 }
3219#endif
3220
3221 /* how much */
3222 if (cb > cbChars)
3223 cb = cbChars;
3224
3225 /* have newline? */
3226 pszNewLine = (const char *)memchr(pachChars, '\n', cb);
3227 if (pszNewLine)
3228 {
3229 if (pLogger->fFlags & RTLOGFLAGS_USECRLF)
3230 cb = pszNewLine - pachChars;
3231 else
3232 {
3233 cb = pszNewLine - pachChars + 1;
3234 *pfPendingPrefix = true;
3235 }
3236 }
3237
3238 /* copy */
3239 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
3240
3241 /* advance */
3242 pLogger->offScratch += (uint32_t)cb;
3243 cbRet += cb;
3244 cbChars -= cb;
3245
3246 if ( pszNewLine
3247 && (pLogger->fFlags & RTLOGFLAGS_USECRLF)
3248 && pLogger->offScratch + 2 < sizeof(pLogger->achScratch))
3249 {
3250 memcpy(&pLogger->achScratch[pLogger->offScratch], "\r\n", 2);
3251 pLogger->offScratch += 2;
3252 cbRet++;
3253 cbChars--;
3254 cb++;
3255 *pfPendingPrefix = true;
3256 }
3257
3258 /* done? */
3259 if (cbChars <= 0)
3260 return cbRet;
3261 pachChars += cb;
3262 }
3263
3264 /* won't ever get here! */
3265 }
3266 else
3267 {
3268 /*
3269 * Termination call.
3270 * There's always space for a terminator, and it's not counted.
3271 */
3272 pLogger->achScratch[pLogger->offScratch] = '\0';
3273 return 0;
3274 }
3275}
3276
3277
3278/**
3279 * Write to a logger instance (worker function).
3280 *
3281 * This function will check whether the instance, group and flags makes up a
3282 * logging kind which is currently enabled before writing anything to the log.
3283 *
3284 * @param pLogger Pointer to logger instance. Must be non-NULL.
3285 * @param fFlags The logging flags.
3286 * @param iGroup The group.
3287 * The value ~0U is reserved for compatibility with RTLogLogger[V] and is
3288 * only for internal usage!
3289 * @param pszFormat Format string.
3290 * @param args Format arguments.
3291 */
3292static void rtlogLoggerExVLocked(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
3293{
3294 /*
3295 * Format the message and perhaps flush it.
3296 */
3297 if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF))
3298 {
3299 RTLOGOUTPUTPREFIXEDARGS OutputArgs;
3300 OutputArgs.pLogger = pLogger;
3301 OutputArgs.iGroup = iGroup;
3302 OutputArgs.fFlags = fFlags;
3303 RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);
3304 }
3305 else
3306 RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);
3307 if ( !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)
3308 && pLogger->offScratch)
3309 rtlogFlush(pLogger);
3310}
3311
3312
3313/**
3314 * For calling rtlogLoggerExVLocked.
3315 *
3316 * @param pLogger The logger.
3317 * @param fFlags The logging flags.
3318 * @param iGroup The group.
3319 * The value ~0U is reserved for compatibility with RTLogLogger[V] and is
3320 * only for internal usage!
3321 * @param pszFormat Format string.
3322 * @param ... Format arguments.
3323 */
3324static void rtlogLoggerExFLocked(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
3325{
3326 va_list va;
3327 va_start(va, pszFormat);
3328 rtlogLoggerExVLocked(pLogger, fFlags, iGroup, pszFormat, va);
3329 va_end(va);
3330}
3331
Note: See TracBrowser for help on using the repository browser.

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