VirtualBox

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

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

IPRT: Some efence adjustments, adding RTMemAllocVar and RTMemAllocZVar for dealing with struct + string style allocations.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette