VirtualBox

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

Last change on this file since 36408 was 36408, checked in by vboxsync, 14 years ago

log rotation review and adjustments: Don't delete any excess files if log roation is disabled - we don't know what these files might be. Moved RTLOGGERFILE into log.c. Keep RTLogCreate simple, anyone needing rotation can use RTLogCreateEx[V]. Made RTLogGetDestinations produce the log rotation bits.

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