VirtualBox

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

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

Runtime/common/log.cpp: gcc warning

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