VirtualBox

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

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

Runtime/common/log/log: make g_Logger weak which is the better solution

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