VirtualBox

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

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

Runtime/common/log/log: gcc thinks that the address of a variable is always != NULL but this is not true for g_Logger (special case in our RC module loader)

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