VirtualBox

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

Last change on this file since 24987 was 24987, checked in by vboxsync, 15 years ago

typo

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