~ubuntu-branches/ubuntu/gutsy/virtualbox-ose/gutsy

« back to all changes in this revision

Viewing changes to src/VBox/Runtime/log.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-09-08 16:44:58 UTC
  • Revision ID: james.westby@ubuntu.com-20070908164458-wao29470vqtr8ksy
Tags: upstream-1.5.0-dfsg2
ImportĀ upstreamĀ versionĀ 1.5.0-dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: log.cpp 4071 2007-08-07 17:07:59Z vboxsync $ */
 
2
/** @file
 
3
 * Runtime VBox - Logger.
 
4
 */
 
5
 
 
6
/*
 
7
 * Copyright (C) 2006-2007 innotek GmbH
 
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 as published by the Free Software Foundation,
 
13
 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
 
14
 * distribution. VirtualBox OSE is distributed in the hope that it will
 
15
 * be useful, but WITHOUT ANY WARRANTY of any kind.
 
16
 */
 
17
 
 
18
 
 
19
/*******************************************************************************
 
20
*   Header Files                                                               *
 
21
*******************************************************************************/
 
22
#include <iprt/log.h>
 
23
#ifndef IN_GC
 
24
# include <iprt/alloc.h>
 
25
# include <iprt/thread.h>
 
26
# include <iprt/semaphore.h>
 
27
#endif
 
28
#ifdef IN_RING3
 
29
# include <iprt/process.h>
 
30
# include <iprt/file.h>
 
31
# include <iprt/path.h>
 
32
#endif
 
33
#include <iprt/time.h>
 
34
#include <iprt/asm.h>
 
35
#include <iprt/assert.h>
 
36
#include <iprt/err.h>
 
37
#include <iprt/param.h>
 
38
 
 
39
#include <iprt/stdarg.h>
 
40
#include <iprt/string.h>
 
41
#ifdef IN_RING3
 
42
# include <iprt/ctype.h>
 
43
# include <iprt/alloca.h>
 
44
# include <stdio.h>
 
45
#else
 
46
# define isspace(ch) ( (ch) == ' ' || (ch) == '\t' )
 
47
#endif
 
48
 
 
49
 
 
50
 
 
51
/*******************************************************************************
 
52
*   Defined Constants And Macros                                               *
 
53
*******************************************************************************/
 
54
/** Ascii to lower macro. */
 
55
#define CHLOWER(ch)     (((unsigned char)(ch) < (unsigned char)'A') || ((unsigned char)(ch) > (unsigned char)'Z') ? (ch) : (ch) + ('a' - 'A'))
 
56
 
 
57
 
 
58
/*******************************************************************************
 
59
*   Structures and Typedefs                                                    *
 
60
*******************************************************************************/
 
61
/**
 
62
 * Arguments passed to the output function.
 
63
 */
 
64
typedef struct RTLOGOUTPUTPREFIXEDARGS
 
65
{
 
66
    /** The logger instance. */
 
67
    PRTLOGGER   pLogger;
 
68
    /** The flags. (used for prefixing.) */
 
69
    unsigned    fFlags;
 
70
    /** The group. (used for prefixing.) */
 
71
    unsigned    iGroup;
 
72
} RTLOGOUTPUTPREFIXEDARGS, *PRTLOGOUTPUTPREFIXEDARGS;
 
73
 
 
74
 
 
75
/*******************************************************************************
 
76
*   Internal Functions                                                         *
 
77
*******************************************************************************/
 
78
#ifndef IN_GC
 
79
static unsigned rtlogGroupFlags(const char *psz);
 
80
#endif
 
81
static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args);
 
82
static void rtlogFlush(PRTLOGGER pLogger);
 
83
static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars);
 
84
static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars);
 
85
 
 
86
 
 
87
/*******************************************************************************
 
88
*   Global Variables                                                           *
 
89
*******************************************************************************/
 
90
#ifdef IN_GC
 
91
/** Default logger instance. */
 
92
extern "C" DECLIMPORT(RTLOGGERGC)   g_Logger;
 
93
/** Default relese logger instance. */
 
94
extern "C" DECLIMPORT(RTLOGGERGC)   g_RelLogger;
 
95
#else /* !IN_GC */
 
96
/** Default logger instance. */
 
97
static PRTLOGGER                    g_pLogger;
 
98
/** Default release logger instance. */
 
99
static PRTLOGGER                    g_pRelLogger;
 
100
#endif /* !IN_GC */
 
101
#ifdef IN_RING0
 
102
/** Number of per-thread loggers. */
 
103
static int32_t volatile             g_cPerThreadLoggers;
 
104
/** Per-thread loggers.
 
105
 * This is just a quick TLS hack suitable for debug logging only.
 
106
 * If we run out of entries, just unload and reload the driver. */
 
107
static struct RTLOGGERPERTHREAD
 
108
{
 
109
    /** The thread. */
 
110
    RTNATIVETHREAD volatile NativeThread;
 
111
    /** The (process / session) key. */
 
112
    uintptr_t volatile      uKey;
 
113
    /** The logger instance.*/
 
114
    PRTLOGGER volatile      pLogger;
 
115
} g_aPerThreadLoggers[8] =
 
116
{   { NIL_RTNATIVETHREAD, 0, 0},
 
117
    { NIL_RTNATIVETHREAD, 0, 0},
 
118
    { NIL_RTNATIVETHREAD, 0, 0},
 
119
    { NIL_RTNATIVETHREAD, 0, 0},
 
120
    { NIL_RTNATIVETHREAD, 0, 0},
 
121
    { NIL_RTNATIVETHREAD, 0, 0},
 
122
    { NIL_RTNATIVETHREAD, 0, 0},
 
123
    { NIL_RTNATIVETHREAD, 0, 0}
 
124
};
 
125
#endif /* IN_RING0 */
 
126
 
 
127
 
 
128
/**
 
129
 * Locks the logger instance.
 
130
 *
 
131
 * @returns See RTSemFastMutexRequest().
 
132
 * @param   pLogger     The logger instance.
 
133
 */
 
134
DECLINLINE(int) rtlogLock(PRTLOGGER pLogger)
 
135
{
 
136
#ifdef IN_RING3
 
137
    if (pLogger->MutexSem != NIL_RTSEMFASTMUTEX)
 
138
    {
 
139
        int rc = RTSemFastMutexRequest(pLogger->MutexSem);
 
140
        AssertRCReturn(rc, rc);
 
141
    }
 
142
#endif
 
143
    return VINF_SUCCESS;
 
144
}
 
145
 
 
146
 
 
147
/**
 
148
 * Unlocks the logger instance.
 
149
 * @param   pLogger     The logger instance.
 
150
 */
 
151
DECLINLINE(void) rtlogUnlock(PRTLOGGER pLogger)
 
152
{
 
153
#ifdef IN_RING3
 
154
    if (pLogger->MutexSem != NIL_RTSEMFASTMUTEX)
 
155
        RTSemFastMutexRelease(pLogger->MutexSem);
 
156
#endif
 
157
    return;
 
158
}
 
159
 
 
160
 
 
161
#ifndef IN_GC
 
162
/**
 
163
 * Create a logger instance, comprehensive version.
 
164
 *
 
165
 * @returns iprt status code.
 
166
 *
 
167
 * @param   ppLogger            Where to store the logger instance.
 
168
 * @param   fFlags              Logger instance flags, a combination of the RTLOGFLAGS_* values.
 
169
 * @param   pszGroupSettings    The initial group settings.
 
170
 * @param   pszEnvVarBase       Base name for the environment variables for this instance.
 
171
 * @param   cGroups             Number of groups in the array.
 
172
 * @param   papszGroups         Pointer to array of groups. This must stick around for the life of the
 
173
 *                              logger instance.
 
174
 * @param   fDestFlags          The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
 
175
 * @param   pszErrorMsg         A buffer which is filled with an error message if something fails. May be NULL.
 
176
 * @param   cchErrorMsg         The size of the error message buffer.
 
177
 * @param   pszFilenameFmt      Log filename format string. Standard RTStrFormat().
 
178
 * @param   ...                 Format arguments.
 
179
 */
 
180
RTDECL(int) RTLogCreateExV(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,
 
181
                           const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
 
182
                           RTUINT fDestFlags, char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, va_list args)
 
183
{
 
184
    /*
 
185
     * Validate input.
 
186
     */
 
187
    if (    (cGroups && !papszGroups)
 
188
        ||  !VALID_PTR(ppLogger)
 
189
       )
 
190
    {
 
191
        AssertMsgFailed(("Invalid parameters!\n"));
 
192
        return VERR_INVALID_PARAMETER;
 
193
    }
 
194
    *ppLogger = NULL;
 
195
 
 
196
 
 
197
    /*
 
198
     * Allocate a logger instance.
 
199
     */
 
200
    int         rc;
 
201
    size_t      cb = RT_OFFSETOF(RTLOGGER, afGroups[cGroups + 1]) + RTPATH_MAX;
 
202
    PRTLOGGER   pLogger = (PRTLOGGER)RTMemAllocZ(cb);
 
203
    if (pLogger)
 
204
    {
 
205
        pLogger->u32Magic    = RTLOGGER_MAGIC;
 
206
        pLogger->papszGroups = papszGroups;
 
207
        pLogger->cMaxGroups  = cGroups;
 
208
        pLogger->cGroups     = cGroups;
 
209
        pLogger->pszFilename = (char *)&pLogger->afGroups[cGroups + 1];
 
210
        pLogger->File        = NIL_RTFILE;
 
211
        pLogger->fFlags      = fFlags;
 
212
        pLogger->fDestFlags  = fDestFlags;
 
213
        pLogger->fPendingPrefix = true;
 
214
        if (pszGroupSettings)
 
215
            RTLogGroupSettings(pLogger, pszGroupSettings);
 
216
 
 
217
        /*
 
218
         * Emit wrapper code.
 
219
         */
 
220
        uint8_t *pu8Code = (uint8_t *)RTMemExecAlloc(64);
 
221
        if (pu8Code)
 
222
        {
 
223
            pLogger->pfnLogger = *(PFNRTLOGGER*)&pu8Code;
 
224
#ifdef RT_ARCH_AMD64
 
225
            /* this wrapper will not be used on AMD64, we will be requiring C99 compilers there. */
 
226
            *pu8Code++ = 0xcc;
 
227
#else
 
228
            *pu8Code++ = 0x68;          /* push imm32 */
 
229
            *(void **)pu8Code = pLogger;
 
230
            pu8Code += sizeof(void *);
 
231
            *pu8Code++ = 0xe8;          /* call rel32 */
 
232
            *(uint32_t *)pu8Code = (uintptr_t)RTLogLogger - ((uintptr_t)pu8Code + sizeof(uint32_t));
 
233
            pu8Code += sizeof(uint32_t);
 
234
            *pu8Code++ = 0x8d;          /* lea esp, [esp + 4] */
 
235
            *pu8Code++ = 0x64;
 
236
            *pu8Code++ = 0x24;
 
237
            *pu8Code++ = 0x04;
 
238
            *pu8Code++ = 0xc3;          /* ret near */
 
239
#endif
 
240
            AssertMsg((uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger <= 64,
 
241
                      ("Wrapper assembly is too big! %d bytes\n", (uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger));
 
242
 
 
243
 
 
244
#ifdef IN_RING3 /* files and env.vars. are only accessible when in R3 at the present time. */
 
245
            /*
 
246
             * Format the filename.
 
247
             */
 
248
            if (pszFilenameFmt)
 
249
            {
 
250
                RTStrPrintfV(pLogger->pszFilename, RTPATH_MAX, pszFilenameFmt, args);
 
251
                pLogger->fDestFlags |= RTLOGDEST_FILE;
 
252
            }
 
253
 
 
254
            /*
 
255
             * Parse the environment variables.
 
256
             */
 
257
            if (pszEnvVarBase)
 
258
            {
 
259
                /* make temp copy of environment variable base. */
 
260
                size_t  cchEnvVarBase = strlen(pszEnvVarBase);
 
261
                char   *pszEnvVar = (char *)alloca(cchEnvVarBase + 16);
 
262
                memcpy(pszEnvVar, pszEnvVarBase, cchEnvVarBase);
 
263
 
 
264
                /*
 
265
                 * Destination.
 
266
                 */
 
267
                strcpy(pszEnvVar + cchEnvVarBase, "_DEST");
 
268
                const char *pszVar = getenv(pszEnvVar);
 
269
                if (pszVar)
 
270
                {
 
271
                    while (*pszVar)
 
272
                    {
 
273
                        /* skip blanks. */
 
274
                        while (isspace(*pszVar) || *pszVar == '\n' || *pszVar == '\r')
 
275
                            pszVar++;
 
276
                        if (!*pszVar)
 
277
                            break;
 
278
 
 
279
                        /* parse instruction. */
 
280
                        static struct
 
281
                        {
 
282
                            const char *pszInstr;
 
283
                            unsigned    fFlag;
 
284
                        } const aDest[] =
 
285
                        {
 
286
                            { "file",       RTLOGDEST_FILE }, /* Must be 1st! */
 
287
                            { "dir",        RTLOGDEST_FILE }, /* Must be 2nd! */
 
288
                            { "stdout",     RTLOGDEST_STDOUT },
 
289
                            { "stderr",     RTLOGDEST_STDERR },
 
290
                            { "debugger",   RTLOGDEST_DEBUGGER },
 
291
                            { "com",        RTLOGDEST_COM },
 
292
                            { "user",       RTLOGDEST_USER },
 
293
                        };
 
294
 
 
295
                        /* check no prefix. */
 
296
                        bool fNo = false;
 
297
                        if (pszVar[0] == 'n' && pszVar[1] == 'o')
 
298
                        {
 
299
                            fNo = true;
 
300
                            pszVar += 2;
 
301
                        }
 
302
 
 
303
                        /* instruction. */
 
304
                        unsigned i;
 
305
                        for (i = 0; i < ELEMENTS(aDest); i++)
 
306
                        {
 
307
                            size_t cchInstr = strlen(aDest[i].pszInstr);
 
308
                            if (!strncmp(pszVar, aDest[i].pszInstr, cchInstr))
 
309
                            {
 
310
                                if (!fNo)
 
311
                                    pLogger->fDestFlags |= aDest[i].fFlag;
 
312
                                else
 
313
                                    pLogger->fDestFlags &= ~aDest[i].fFlag;
 
314
                                pszVar += cchInstr;
 
315
 
 
316
                                /* check for value. */
 
317
                                while (isspace(*pszVar) || *pszVar == '\n' || *pszVar == '\r')
 
318
                                    pszVar++;
 
319
                                if (*pszVar == '=' || *pszVar == ':')
 
320
                                {
 
321
                                    pszVar++;
 
322
                                    const char *pszEnd = strchr(pszVar, ';');
 
323
                                    if (!pszEnd)
 
324
                                        pszEnd = strchr(pszVar, '\0');
 
325
 
 
326
                                    /* log file name */
 
327
                                    size_t cch = pszEnd - pszVar;
 
328
                                    if (i == 0 /* file */ && !fNo)
 
329
                                    {
 
330
                                        memcpy(pLogger->pszFilename, pszVar, cch);
 
331
                                        pLogger->pszFilename[cch] = '\0';
 
332
                                    }
 
333
                                    /* log directory */
 
334
                                    else if (i == 1 /* dir */ && !fNo)
 
335
                                    {
 
336
                                        char szTmp[RTPATH_MAX];
 
337
                                        const char *pszFile = RTPathFilename(pLogger->pszFilename);
 
338
                                        if (pszFile)
 
339
                                            strcpy(szTmp, pszFile);
 
340
                                        else
 
341
                                            pszFile = ""; /* you've screwed up, sir. */
 
342
 
 
343
                                        memcpy(pLogger->pszFilename, pszVar, cch);
 
344
                                        pLogger->pszFilename[cch] = '\0';
 
345
                                        RTPathStripTrailingSlash(pLogger->pszFilename);
 
346
 
 
347
                                        cch = strlen(pLogger->pszFilename);
 
348
                                        pLogger->pszFilename[cch++] = '/';
 
349
                                        strcpy(&pLogger->pszFilename[cch], szTmp);
 
350
                                    }
 
351
                                    else
 
352
                                        AssertMsgFailed(("Invalid %s_DEST! %s%s doesn't take a value!\n", pszEnvVarBase, fNo ? "no" : "", aDest[i].pszInstr));
 
353
                                    pszVar = pszEnd + (*pszEnd != '\0');
 
354
                                }
 
355
                                break;
 
356
                            }
 
357
                        }
 
358
                        /* unknown instruction? */
 
359
                        if (i >= ELEMENTS(aDest))
 
360
                        {
 
361
                            AssertMsgFailed(("Invalid %s_DEST! unknown instruction %.20s\n", pszEnvVarBase, pszVar));
 
362
                            pszVar++;
 
363
                        }
 
364
 
 
365
                        /* skip blanks and delimiters. */
 
366
                        while (isspace(*pszVar) || *pszVar == '\n' || *pszVar == '\r' || *pszVar == ';')
 
367
                            pszVar++;
 
368
                    } /* while more environment variable value left */
 
369
                }
 
370
 
 
371
                /*
 
372
                 * The flags.
 
373
                 */
 
374
                strcpy(pszEnvVar + cchEnvVarBase, "_FLAGS");
 
375
                pszVar = getenv(pszEnvVar);
 
376
                if (pszVar)
 
377
                    RTLogFlags(pLogger, pszVar);
 
378
 
 
379
                /*
 
380
                 * The group settings.
 
381
                 */
 
382
                pszEnvVar[cchEnvVarBase] = '\0';
 
383
                pszVar = getenv(pszEnvVar);
 
384
                if (pszVar)
 
385
                    RTLogGroupSettings(pLogger, pszVar);
 
386
            }
 
387
#endif /* IN_RING3 */
 
388
 
 
389
            /*
 
390
             * Open the destination(s).
 
391
             */
 
392
            rc = VINF_SUCCESS;
 
393
#ifdef IN_RING3
 
394
            if (pLogger->fDestFlags & RTLOGDEST_FILE)
 
395
            {
 
396
                rc = RTFileOpen(&pLogger->File, pLogger->pszFilename,
 
397
                                RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE);
 
398
                if (RT_FAILURE(rc) && pszErrorMsg)
 
399
                    RTStrPrintf(pszErrorMsg, cchErrorMsg, "could not open file '%s'", pLogger->pszFilename);
 
400
            }
 
401
#endif  /* IN_RING3 */
 
402
 
 
403
            /*
 
404
             * Create mutex.
 
405
             */
 
406
            if (RT_SUCCESS(rc))
 
407
            {
 
408
                rc = RTSemFastMutexCreate(&pLogger->MutexSem);
 
409
                if (RT_SUCCESS(rc))
 
410
                {
 
411
                    *ppLogger = pLogger;
 
412
                    return VINF_SUCCESS;
 
413
                }
 
414
                else if (pszErrorMsg)
 
415
                    RTStrPrintf(pszErrorMsg, cchErrorMsg, "failed to create sempahore");
 
416
            }
 
417
#ifdef IN_RING3
 
418
            RTFileClose(pLogger->File);
 
419
#endif
 
420
            RTMemExecFree(*(void **)&pLogger->pfnLogger);
 
421
        }
 
422
        else
 
423
            rc = VERR_NO_MEMORY;
 
424
        RTMemFree(pLogger);
 
425
    }
 
426
    else
 
427
        rc = VERR_NO_MEMORY;
 
428
 
 
429
    return rc;
 
430
}
 
431
 
 
432
/**
 
433
 * Create a logger instance.
 
434
 *
 
435
 * @returns iprt status code.
 
436
 *
 
437
 * @param   ppLogger            Where to store the logger instance.
 
438
 * @param   fFlags              Logger instance flags, a combination of the RTLOGFLAGS_* values.
 
439
 * @param   pszGroupSettings    The initial group settings.
 
440
 * @param   pszEnvVarBase       Base name for the environment variables for this instance.
 
441
 * @param   cGroups             Number of groups in the array.
 
442
 * @param   papszGroups         Pointer to array of groups. This must stick around for the life of the
 
443
 *                              logger instance.
 
444
 * @param   fDestFlags          The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
 
445
 * @param   pszFilenameFmt      Log filename format string. Standard RTStrFormat().
 
446
 * @param   ...                 Format arguments.
 
447
 */
 
448
RTDECL(int) RTLogCreate(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,
 
449
                        const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
 
450
                        RTUINT fDestFlags, const char *pszFilenameFmt, ...)
 
451
{
 
452
    va_list args;
 
453
    int rc;
 
454
 
 
455
    va_start(args, pszFilenameFmt);
 
456
    rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, NULL, 0, pszFilenameFmt, args);
 
457
    va_end(args);
 
458
    return rc;
 
459
}
 
460
 
 
461
/**
 
462
 * Create a logger instance.
 
463
 *
 
464
 * @returns iprt status code.
 
465
 *
 
466
 * @param   ppLogger            Where to store the logger instance.
 
467
 * @param   fFlags              Logger instance flags, a combination of the RTLOGFLAGS_* values.
 
468
 * @param   pszGroupSettings    The initial group settings.
 
469
 * @param   pszEnvVarBase       Base name for the environment variables for this instance.
 
470
 * @param   cGroups             Number of groups in the array.
 
471
 * @param   papszGroups         Pointer to array of groups. This must stick around for the life of the
 
472
 *                              logger instance.
 
473
 * @param   fDestFlags          The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
 
474
 * @param   pszErrorMsg         A buffer which is filled with an error message if something fails. May be NULL.
 
475
 * @param   cchErrorMsg         The size of the error message buffer.
 
476
 * @param   pszFilenameFmt      Log filename format string. Standard RTStrFormat().
 
477
 * @param   ...                 Format arguments.
 
478
 */
 
479
RTDECL(int) RTLogCreateEx(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,
 
480
                          const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,
 
481
                          RTUINT fDestFlags,  char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, ...)
 
482
{
 
483
    va_list args;
 
484
    int rc;
 
485
 
 
486
    va_start(args, pszFilenameFmt);
 
487
    rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, pszErrorMsg, cchErrorMsg, pszFilenameFmt, args);
 
488
    va_end(args);
 
489
    return rc;
 
490
}
 
491
 
 
492
/**
 
493
 * Destroys a logger instance.
 
494
 *
 
495
 * The instance is flushed and all output destinations closed (where applicable).
 
496
 *
 
497
 * @returns iprt status code.
 
498
 * @param   pLogger             The logger instance which close destroyed.
 
499
 */
 
500
RTDECL(int) RTLogDestroy(PRTLOGGER pLogger)
 
501
{
 
502
    /*
 
503
     * Validate input.
 
504
     */
 
505
    AssertReturn(VALID_PTR(pLogger), VERR_INVALID_POINTER);
 
506
    AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
 
507
 
 
508
    /*
 
509
     * Acquire logger instance sem and disable all logging. (paranoia)
 
510
     */
 
511
    int rc = rtlogLock(pLogger);
 
512
    if (RT_FAILURE(rc))
 
513
        return rc;
 
514
 
 
515
    pLogger->fFlags |= RTLOGFLAGS_DISABLED;
 
516
    RTUINT iGroup = pLogger->cGroups;
 
517
    while (iGroup-- > 0)
 
518
        pLogger->afGroups[iGroup] = 0;
 
519
 
 
520
    /*
 
521
     * Flush it.
 
522
     */
 
523
    RTLogFlush(pLogger);
 
524
 
 
525
    /*
 
526
     * Close output stuffs.
 
527
     */
 
528
#ifdef IN_RING3
 
529
    if (pLogger->File != NIL_RTFILE)
 
530
    {
 
531
        int rc2 = RTFileClose(pLogger->File);
 
532
        AssertRC(rc2);
 
533
        if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
 
534
            rc = rc2;
 
535
        pLogger->File = NIL_RTFILE;
 
536
    }
 
537
#endif
 
538
 
 
539
    /*
 
540
     * Free the mutex and the instance memory.
 
541
     */
 
542
    RTSEMFASTMUTEX MutexSem = pLogger->MutexSem;
 
543
    pLogger->MutexSem = NIL_RTSEMFASTMUTEX;
 
544
    if (MutexSem != NIL_RTSEMFASTMUTEX)
 
545
    {
 
546
        int rc2 = RTSemFastMutexDestroy(MutexSem);
 
547
        AssertRC(rc2);
 
548
        if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
 
549
            rc = rc2;
 
550
    }
 
551
 
 
552
    RTMemFree(pLogger);
 
553
 
 
554
    return rc;
 
555
}
 
556
 
 
557
 
 
558
/**
 
559
 * Create a logger instance clone for GC usage.
 
560
 *
 
561
 * @returns iprt status code.
 
562
 *
 
563
 * @param   pLogger             The logger instance to be cloned.
 
564
 * @param   pLoggerGC           Where to create the GC logger instance.
 
565
 * @param   cbLoggerGC          Amount of memory allocated to for the GC logger instance clone.
 
566
 * @param   pfnLoggerGCPtr      Pointer to logger wrapper function for this instance (GC Ptr).
 
567
 * @param   pfnFlushGCPtr       Pointer to flush function (GC Ptr).
 
568
 * @param   fFlags              Logger instance flags, a combination of the RTLOGFLAGS_* values.
 
569
 */
 
570
RTDECL(int) RTLogCloneGC(PRTLOGGER pLogger, PRTLOGGERGC pLoggerGC, size_t cbLoggerGC,
 
571
                         RTGCPTR pfnLoggerGCPtr, RTGCPTR pfnFlushGCPtr, RTUINT fFlags)
 
572
{
 
573
    /*
 
574
     * Validate input.
 
575
     */
 
576
   if (    !pLoggerGC
 
577
       ||  !pfnFlushGCPtr
 
578
       ||  !pfnLoggerGCPtr)
 
579
    {
 
580
       AssertMsgFailed(("Invalid parameters!\n"));
 
581
       return VERR_INVALID_PARAMETER;
 
582
    }
 
583
    if (cbLoggerGC < sizeof(*pLoggerGC))
 
584
    {
 
585
        AssertMsgFailed(("%d min=%d\n", cbLoggerGC, sizeof(*pLoggerGC)));
 
586
        return VERR_INVALID_PARAMETER;
 
587
    }
 
588
 
 
589
    /*
 
590
     * Initialize GC instance.
 
591
     */
 
592
    pLoggerGC->offScratch   = 0;
 
593
    pLoggerGC->fPendingPrefix = false;
 
594
    pLoggerGC->pfnLogger    = pfnLoggerGCPtr;
 
595
    pLoggerGC->pfnFlush     = pfnFlushGCPtr;
 
596
    pLoggerGC->u32Magic     = RTLOGGERGC_MAGIC;
 
597
    pLoggerGC->fFlags       = fFlags | RTLOGFLAGS_DISABLED;
 
598
    pLoggerGC->cGroups      = 1;
 
599
    pLoggerGC->afGroups[0]  = 0;
 
600
 
 
601
    /*
 
602
     * Resolve defaults.
 
603
     */
 
604
    if (!pLogger)
 
605
    {
 
606
        pLogger = RTLogDefaultInstance();
 
607
        if (!pLogger)
 
608
            return VINF_SUCCESS;
 
609
    }
 
610
 
 
611
    /*
 
612
     * Check if there's enough space for the groups.
 
613
     */
 
614
    if (cbLoggerGC < (size_t)RT_OFFSETOF(RTLOGGERGC, afGroups[pLogger->cGroups]))
 
615
    {
 
616
        AssertMsgFailed(("%d req=%d cGroups=%d\n", cbLoggerGC, RT_OFFSETOF(RTLOGGERGC, afGroups[pLogger->cGroups]), pLogger->cGroups));
 
617
        return VERR_INVALID_PARAMETER;
 
618
    }
 
619
    memcpy(&pLoggerGC->afGroups[0], &pLogger->afGroups[0], pLogger->cGroups * sizeof(pLoggerGC->afGroups[0]));
 
620
    pLoggerGC->cGroups = pLogger->cGroups;
 
621
 
 
622
    /*
 
623
     * Copy bits from the HC instance.
 
624
     */
 
625
    pLoggerGC->fPendingPrefix = pLogger->fPendingPrefix;
 
626
    pLoggerGC->fFlags |= pLogger->fFlags;
 
627
 
 
628
    /*
 
629
     * Check if we can remove the disabled flag.
 
630
     */
 
631
    if (    pLogger->fDestFlags
 
632
        &&  !((pLogger->fFlags | fFlags) & RTLOGFLAGS_DISABLED))
 
633
        pLoggerGC->fFlags &= ~RTLOGFLAGS_DISABLED;
 
634
 
 
635
    return VINF_SUCCESS;
 
636
}
 
637
 
 
638
 
 
639
/**
 
640
 * Flushes a GC logger instance to a HC logger.
 
641
 *
 
642
 *
 
643
 * @returns iprt status code.
 
644
 * @param   pLogger     The HC logger instance to flush pLoggerGC to.
 
645
 *                      If NULL the default logger is used.
 
646
 * @param   pLoggerGC   The GC logger instance to flush.
 
647
 */
 
648
RTDECL(void) RTLogFlushGC(PRTLOGGER pLogger, PRTLOGGERGC pLoggerGC)
 
649
{
 
650
    /*
 
651
     * Resolve defaults.
 
652
     */
 
653
    if (!pLogger)
 
654
    {
 
655
        pLogger = RTLogDefaultInstance();
 
656
        if (!pLogger)
 
657
        {
 
658
            pLoggerGC->offScratch = 0;
 
659
            return;
 
660
        }
 
661
    }
 
662
 
 
663
    /*
 
664
     * Any thing to flush?
 
665
     */
 
666
    if (    pLogger->offScratch
 
667
        ||  pLoggerGC->offScratch)
 
668
    {
 
669
        /*
 
670
         * Acquire logger instance sem.
 
671
         */
 
672
        int rc = rtlogLock(pLogger);
 
673
        if (RT_FAILURE(rc))
 
674
            return;
 
675
 
 
676
        /*
 
677
         * Write whatever the GC instance contains to the HC one, and then
 
678
         * flush the HC instance.
 
679
         */
 
680
        if (pLoggerGC->offScratch)
 
681
        {
 
682
            rtLogOutput(pLogger, pLoggerGC->achScratch, pLoggerGC->offScratch);
 
683
            rtLogOutput(pLogger, NULL, 0);
 
684
            pLoggerGC->offScratch = 0;
 
685
        }
 
686
 
 
687
        /*
 
688
         * Release the semaphore.
 
689
         */
 
690
        rtlogUnlock(pLogger);
 
691
    }
 
692
}
 
693
 
 
694
 
 
695
#ifdef IN_RING3
 
696
/**
 
697
 * Create a logger instance for singled threaded ring-0 usage.
 
698
 *
 
699
 * @returns iprt status code.
 
700
 *
 
701
 * @param   pLogger             Where to create the logger instance.
 
702
 * @param   cbLogger            The amount of memory available for the logger instance.
 
703
 * @param   pfnLogger           Pointer to logger wrapper function for the clone.
 
704
 * @param   pfnFlush            Pointer to flush function for the clone.
 
705
 * @param   fFlags              Logger instance flags for the clone, a combination of the RTLOGFLAGS_* values.
 
706
 * @param   fDestFlags          The destination flags.
 
707
 */
 
708
RTDECL(int) RTLogCreateForR0(PRTLOGGER pLogger, size_t cbLogger, PFNRTLOGGER pfnLogger, PFNRTLOGFLUSH pfnFlush, RTUINT fFlags, RTUINT fDestFlags)
 
709
{
 
710
    /*
 
711
     * Validate input.
 
712
     */
 
713
    AssertPtrReturn(pLogger, VERR_INVALID_PARAMETER);
 
714
    AssertReturn(cbLogger >= sizeof(*pLogger), VERR_INVALID_PARAMETER);
 
715
    AssertReturn(pfnLogger, VERR_INVALID_PARAMETER);
 
716
    AssertReturn(pfnFlush, VERR_INVALID_PARAMETER);
 
717
 
 
718
    /*
 
719
     * Initialize the ring-0 instance.
 
720
     */
 
721
    pLogger->offScratch   = 0;
 
722
    pLogger->fPendingPrefix = false;
 
723
    pLogger->pfnLogger    = pfnLogger;
 
724
    pLogger->pfnFlush     = pfnFlush;
 
725
    pLogger->MutexSem     = NIL_RTSEMFASTMUTEX; /* Not serialized. */
 
726
    pLogger->u32Magic     = RTLOGGER_MAGIC;
 
727
    pLogger->fFlags       = fFlags;
 
728
    pLogger->fDestFlags   = fDestFlags & ~RTLOGDEST_FILE;
 
729
    pLogger->File         = NIL_RTFILE;
 
730
    pLogger->pszFilename  = NULL;
 
731
    pLogger->papszGroups  = NULL;
 
732
    pLogger->cMaxGroups   = (cbLogger - RT_OFFSETOF(RTLOGGER, afGroups[0])) / sizeof(pLogger->afGroups[0]);
 
733
    pLogger->cGroups      = 1;
 
734
    pLogger->afGroups[0]  = 0;
 
735
    return VINF_SUCCESS;
 
736
}
 
737
#endif /* IN_RING3 */
 
738
 
 
739
 
 
740
/**
 
741
 * Copies the group settings and flags from logger instance to another.
 
742
 *
 
743
 * @returns IPRT status code.
 
744
 * @param   pDstLogger      The destination logger instance.
 
745
 * @param   pSrcLogger      The source logger instance. If NULL the default one is used.
 
746
 * @param   fFlagsOr        OR mask for the flags.
 
747
 * @param   fFlagsAnd       AND mask for the flags.
 
748
 */
 
749
RTDECL(int) RTLogCopyGroupsAndFlags(PRTLOGGER pDstLogger, PCRTLOGGER pSrcLogger, unsigned fFlagsOr, unsigned fFlagsAnd)
 
750
{
 
751
    /*
 
752
     * Validate input.
 
753
     */
 
754
    AssertPtrReturn(pDstLogger, VERR_INVALID_PARAMETER);
 
755
    AssertPtrNullReturn(pSrcLogger, VERR_INVALID_PARAMETER);
 
756
 
 
757
    /*
 
758
     * Resolve defaults.
 
759
     */
 
760
    if (!pSrcLogger)
 
761
    {
 
762
        pSrcLogger = RTLogDefaultInstance();
 
763
        if (!pSrcLogger)
 
764
        {
 
765
            pDstLogger->fFlags |= RTLOGFLAGS_DISABLED;
 
766
            pDstLogger->cGroups = 1;
 
767
            pDstLogger->afGroups[0] = 0;
 
768
            return VINF_SUCCESS;
 
769
        }
 
770
    }
 
771
 
 
772
    /*
 
773
     * Copy flags and group settings.
 
774
     */
 
775
    pDstLogger->fFlags = (pSrcLogger->fFlags & fFlagsAnd) | fFlagsOr;
 
776
 
 
777
    int rc = VINF_SUCCESS;
 
778
    unsigned cGroups = pSrcLogger->cGroups;
 
779
    if (cGroups < pDstLogger->cMaxGroups)
 
780
    {
 
781
        AssertMsgFailed(("cMaxGroups=%zd cGroups=%zd (min size %d)\n", pDstLogger->cMaxGroups,
 
782
                         pSrcLogger->cGroups, RT_OFFSETOF(RTLOGGER, afGroups[pSrcLogger->cGroups])));
 
783
        rc = VERR_INVALID_PARAMETER;
 
784
        cGroups = pDstLogger->cMaxGroups;
 
785
    }
 
786
    memcpy(&pDstLogger->afGroups[0], &pSrcLogger->afGroups[0], cGroups * sizeof(pDstLogger->afGroups[0]));
 
787
    pDstLogger->cGroups = cGroups;
 
788
 
 
789
    return rc;
 
790
}
 
791
 
 
792
 
 
793
/**
 
794
 * Flushes the buffer in one logger instance onto another logger.
 
795
 *
 
796
 * @returns iprt status code.
 
797
 *
 
798
 * @param   pSrcLogger   The logger instance to flush.
 
799
 * @param   pDstLogger   The logger instance to flush onto.
 
800
 *                       If NULL the default logger will be used.
 
801
 */
 
802
RTDECL(void) RTLogFlushToLogger(PRTLOGGER pSrcLogger, PRTLOGGER pDstLogger)
 
803
{
 
804
    /*
 
805
     * Resolve defaults.
 
806
     */
 
807
    if (!pDstLogger)
 
808
    {
 
809
        pDstLogger = RTLogDefaultInstance();
 
810
        if (!pDstLogger)
 
811
        {
 
812
            /* flushing to "/dev/null". */
 
813
            if (pSrcLogger->offScratch)
 
814
            {
 
815
                int rc = rtlogLock(pSrcLogger);
 
816
                if (RT_SUCCESS(rc))
 
817
                {
 
818
                    pSrcLogger->offScratch = 0;
 
819
                    rtlogLock(pSrcLogger);
 
820
                }
 
821
            }
 
822
            return;
 
823
        }
 
824
    }
 
825
 
 
826
    /*
 
827
     * Any thing to flush?
 
828
     */
 
829
    if (    pSrcLogger->offScratch
 
830
        ||  pDstLogger->offScratch)
 
831
    {
 
832
        /*
 
833
         * Acquire logger semaphores.
 
834
         */
 
835
        int rc = rtlogLock(pDstLogger);
 
836
        if (RT_FAILURE(rc))
 
837
            return;
 
838
        rc = rtlogLock(pSrcLogger);
 
839
        if (RT_SUCCESS(rc))
 
840
        {
 
841
            /*
 
842
             * Write whatever the GC instance contains to the HC one, and then
 
843
             * flush the HC instance.
 
844
             */
 
845
            if (pSrcLogger->offScratch)
 
846
            {
 
847
                rtLogOutput(pDstLogger, pSrcLogger->achScratch, pSrcLogger->offScratch);
 
848
                rtLogOutput(pDstLogger, NULL, 0);
 
849
                pSrcLogger->offScratch = 0;
 
850
            }
 
851
 
 
852
            /*
 
853
             * Release the semaphores.
 
854
             */
 
855
            rtlogUnlock(pSrcLogger);
 
856
        }
 
857
        rtlogUnlock(pDstLogger);
 
858
    }
 
859
}
 
860
 
 
861
 
 
862
/**
 
863
 * Matches a group name with a pattern mask in an case insensitive manner (ASCII).
 
864
 *
 
865
 * @returns true if matching and *ppachMask set to the end of the pattern.
 
866
 * @returns false if no match.
 
867
 * @param   pszGrp      The group name.
 
868
 * @param   ppachMask   Pointer to the pointer to the mask. Only wildcard supported is '*'.
 
869
 * @param   cchMask     The length of the mask, including modifiers. The modifiers is why
 
870
 *                      we update *ppachMask on match.
 
871
 */
 
872
static bool rtlogIsGroupMatching(const char *pszGrp, const char **ppachMask, unsigned cchMask)
 
873
{
 
874
    if (!pszGrp || !*pszGrp)
 
875
        return false;
 
876
    const char *pachMask = *ppachMask;
 
877
    for (;;)
 
878
    {
 
879
        if (CHLOWER(*pszGrp) != CHLOWER(*pachMask))
 
880
        {
 
881
            /*
 
882
             * Check for wildcard and do a minimal match if found.
 
883
             */
 
884
            if (*pachMask != '*')
 
885
                return false;
 
886
 
 
887
            /* eat '*'s. */
 
888
            do  pachMask++;
 
889
            while (--cchMask && *pachMask == '*');
 
890
 
 
891
            /* is there more to match? */
 
892
            if (    !cchMask
 
893
                ||  *pachMask == '.'
 
894
                ||  *pachMask == '=')
 
895
                break; /* we're good */
 
896
 
 
897
            /* do extremely minimal matching (fixme) */
 
898
            pszGrp = strchr(pszGrp, *pachMask);
 
899
            if (!pszGrp)
 
900
                return false;
 
901
            continue;
 
902
        }
 
903
 
 
904
        /* done? */
 
905
        if (!*++pszGrp)
 
906
        {
 
907
            /* trailing wildcard is ok. */
 
908
            do
 
909
            {
 
910
                pachMask++;
 
911
                cchMask--;
 
912
            } while (cchMask && *pachMask == '*');
 
913
            if (    !cchMask
 
914
                ||  *pachMask == '.'
 
915
                ||  *pachMask == '=')
 
916
                break; /* we're good */
 
917
            return false;
 
918
        }
 
919
 
 
920
        if (!--cchMask)
 
921
            return false;
 
922
        pachMask++;
 
923
    }
 
924
 
 
925
    /* match */
 
926
    *ppachMask = pachMask;
 
927
    return true;
 
928
}
 
929
 
 
930
 
 
931
/**
 
932
 * Updates the group settings for the logger instance using the specified
 
933
 * specification string.
 
934
 *
 
935
 * @returns iprt status code.
 
936
 *          Failures can safely be ignored.
 
937
 * @param   pLogger     Logger instance.
 
938
 * @param   pszVar      Value to parse.
 
939
 */
 
940
RTDECL(int) RTLogGroupSettings(PRTLOGGER pLogger, const char *pszVar)
 
941
{
 
942
    /*
 
943
     * Resolve defaults.
 
944
     */
 
945
    if (!pLogger)
 
946
    {
 
947
        pLogger = RTLogDefaultInstance();
 
948
        if (!pLogger)
 
949
            return VINF_SUCCESS;
 
950
    }
 
951
 
 
952
    /*
 
953
     * Iterate the string.
 
954
     */
 
955
    while (*pszVar)
 
956
    {
 
957
        /*
 
958
         * Skip prefixes (blanks, ;, + and -).
 
959
         */
 
960
        bool    fEnabled = true;
 
961
        char    ch;
 
962
        while ((ch = *pszVar) == '+' || ch == '-' || ch == ' ' || ch == '\t' || ch == '\n' || ch == ';')
 
963
        {
 
964
            if (ch == '+' || ch == '-' || ';')
 
965
                fEnabled = ch != '-';
 
966
            pszVar++;
 
967
        }
 
968
        if (!*pszVar)
 
969
            break;
 
970
 
 
971
        /*
 
972
         * Find end.
 
973
         */
 
974
        const char *pszStart = pszVar;
 
975
        while ((ch = *pszVar) != '\0' && ch != '+' && ch != '-' && ch != ' ' && ch != '\t')
 
976
            pszVar++;
 
977
 
 
978
        /*
 
979
         * Find the group (ascii case insensitive search).
 
980
         * Special group 'all'.
 
981
         */
 
982
        unsigned    i;
 
983
        size_t      cch = pszVar - pszStart;
 
984
        if (    cch >= 3
 
985
            &&  (pszStart[0] == 'a' || pszStart[0] == 'A')
 
986
            &&  (pszStart[1] == 'l' || pszStart[1] == 'L')
 
987
            &&  (pszStart[2] == 'l' || pszStart[2] == 'L')
 
988
            &&  (cch == 3 || pszStart[3] == '.' || pszStart[3] == '='))
 
989
        {
 
990
            /*
 
991
             * All.
 
992
             */
 
993
            unsigned fFlags = cch == 3
 
994
                            ? RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1
 
995
                            : rtlogGroupFlags(&pszStart[3]);
 
996
            for (i = 0; i < pLogger->cGroups; i++)
 
997
            {
 
998
                if (fEnabled)
 
999
                    pLogger->afGroups[i] |= fFlags;
 
1000
                else
 
1001
                    pLogger->afGroups[i] &= ~fFlags;
 
1002
            }
 
1003
        }
 
1004
        else
 
1005
        {
 
1006
            /*
 
1007
             * Specific group(s).
 
1008
             */
 
1009
            bool    fFound;
 
1010
            for (i = 0, fFound = false; i < pLogger->cGroups && !fFound; i++)
 
1011
            {
 
1012
                const char *psz2 = (const char*)pszStart;
 
1013
                if (rtlogIsGroupMatching(pLogger->papszGroups[i], &psz2, cch))
 
1014
                {
 
1015
                    unsigned fFlags = RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1;
 
1016
                    if (*psz2 == '.' || *psz2 == '=')
 
1017
                        fFlags = rtlogGroupFlags(psz2);
 
1018
                    if (fEnabled)
 
1019
                        pLogger->afGroups[i] |= fFlags;
 
1020
                    else
 
1021
                        pLogger->afGroups[i] &= ~fFlags;
 
1022
                }
 
1023
            } /* for each group */
 
1024
        }
 
1025
 
 
1026
    } /* parse specification */
 
1027
 
 
1028
    return VINF_SUCCESS;
 
1029
}
 
1030
 
 
1031
 
 
1032
/**
 
1033
 * Interprets the group flags suffix.
 
1034
 *
 
1035
 * @returns Flags specified. (0 is possible!)
 
1036
 * @param   psz     Start of Suffix. (Either dot or equal sign.)
 
1037
 */
 
1038
static unsigned rtlogGroupFlags(const char *psz)
 
1039
{
 
1040
    unsigned fFlags = 0;
 
1041
 
 
1042
    /*
 
1043
     * Litteral flags.
 
1044
     */
 
1045
    while (*psz == '.')
 
1046
    {
 
1047
        static struct
 
1048
        {
 
1049
            const char *pszFlag;        /* lowercase!! */
 
1050
            unsigned    fFlag;
 
1051
        } aFlags[] =
 
1052
        {
 
1053
            { "eo",         RTLOGGRPFLAGS_ENABLED },
 
1054
            { "enabledonly",RTLOGGRPFLAGS_ENABLED },
 
1055
            { "e",          RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },
 
1056
            { "enabled",    RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },
 
1057
            { "l1",         RTLOGGRPFLAGS_LEVEL_1 },
 
1058
            { "level1",     RTLOGGRPFLAGS_LEVEL_1 },
 
1059
            { "l",          RTLOGGRPFLAGS_LEVEL_2 },
 
1060
            { "l2",         RTLOGGRPFLAGS_LEVEL_2 },
 
1061
            { "level2",     RTLOGGRPFLAGS_LEVEL_2 },
 
1062
            { "l3",         RTLOGGRPFLAGS_LEVEL_3 },
 
1063
            { "level3",     RTLOGGRPFLAGS_LEVEL_3 },
 
1064
            { "l4",         RTLOGGRPFLAGS_LEVEL_4 },
 
1065
            { "level4",     RTLOGGRPFLAGS_LEVEL_4 },
 
1066
            { "l5",         RTLOGGRPFLAGS_LEVEL_5 },
 
1067
            { "level5",     RTLOGGRPFLAGS_LEVEL_5 },
 
1068
            { "l6",         RTLOGGRPFLAGS_LEVEL_6 },
 
1069
            { "level6",     RTLOGGRPFLAGS_LEVEL_6 },
 
1070
            { "f",          RTLOGGRPFLAGS_FLOW },
 
1071
            { "flow",       RTLOGGRPFLAGS_FLOW },
 
1072
 
 
1073
            { "lelik",      RTLOGGRPFLAGS_LELIK },
 
1074
            { "michael",    RTLOGGRPFLAGS_MICHAEL },
 
1075
            { "dmik",       RTLOGGRPFLAGS_DMIK },
 
1076
            { "sunlover",   RTLOGGRPFLAGS_SUNLOVER },
 
1077
            { "achim",      RTLOGGRPFLAGS_ACHIM },
 
1078
            { "achimha",    RTLOGGRPFLAGS_ACHIM },
 
1079
            { "s",          RTLOGGRPFLAGS_SANDER },
 
1080
            { "sander",     RTLOGGRPFLAGS_SANDER },
 
1081
            { "sandervl",   RTLOGGRPFLAGS_SANDER },
 
1082
            { "klaus",      RTLOGGRPFLAGS_KLAUS },
 
1083
            { "frank",      RTLOGGRPFLAGS_FRANK },
 
1084
            { "b",          RTLOGGRPFLAGS_BIRD },
 
1085
            { "bird",       RTLOGGRPFLAGS_BIRD },
 
1086
            { "n",          RTLOGGRPFLAGS_NONAME },
 
1087
            { "noname",     RTLOGGRPFLAGS_NONAME }
 
1088
        };
 
1089
        psz++;
 
1090
        unsigned    i;
 
1091
        bool        fFound = false;
 
1092
        for (i = 0; i < ELEMENTS(aFlags) && !fFound; i++)
 
1093
        {
 
1094
            const char *psz1 = aFlags[i].pszFlag;
 
1095
            const char *psz2 = psz;
 
1096
            while (*psz1 == CHLOWER(*psz2))
 
1097
            {
 
1098
                psz1++;
 
1099
                psz2++;
 
1100
                if (!*psz1)
 
1101
                {
 
1102
                    if (    (*psz2 >= 'a' && *psz2 <= 'z')
 
1103
                        ||  (*psz2 >= 'A' && *psz2 <= 'Z')
 
1104
                        ||  (*psz2 >= '0' && *psz2 <= '9') )
 
1105
                        break;
 
1106
                    fFlags |= aFlags[i].fFlag;
 
1107
                    fFound = true;
 
1108
                    psz = psz2;
 
1109
                    break;
 
1110
                }
 
1111
            } /* strincmp */
 
1112
        } /* for each flags */
 
1113
    }
 
1114
 
 
1115
    /*
 
1116
     * Flag value.
 
1117
     */
 
1118
    if (*psz == '=')
 
1119
    {
 
1120
        psz++;
 
1121
        if (*psz == '~')
 
1122
            fFlags = ~RTStrToInt32(psz + 1);
 
1123
        else
 
1124
            fFlags = RTStrToInt32(psz);
 
1125
    }
 
1126
 
 
1127
    return fFlags;
 
1128
}
 
1129
 
 
1130
#endif /* !IN_GC */
 
1131
 
 
1132
 
 
1133
/**
 
1134
 * Updates the flags for the logger instance using the specified
 
1135
 * specification string.
 
1136
 *
 
1137
 * @returns iprt status code.
 
1138
 *          Failures can safely be ignored.
 
1139
 * @param   pLogger     Logger instance (NULL for default logger).
 
1140
 * @param   pszVar      Value to parse.
 
1141
 */
 
1142
RTDECL(int) RTLogFlags(PRTLOGGER pLogger, const char *pszVar)
 
1143
{
 
1144
    int rc = VINF_SUCCESS;
 
1145
 
 
1146
    /*
 
1147
     * Resolve defaults.
 
1148
     */
 
1149
    if (!pLogger)
 
1150
    {
 
1151
        pLogger = RTLogDefaultInstance();
 
1152
        if (!pLogger)
 
1153
            return VINF_SUCCESS;
 
1154
    }
 
1155
 
 
1156
    /*
 
1157
     * Iterate the string.
 
1158
     */
 
1159
    while (*pszVar)
 
1160
    {
 
1161
        /* skip blanks. */
 
1162
        while (isspace(*pszVar) || *pszVar == '\n' || *pszVar == '\r')
 
1163
            pszVar++;
 
1164
        if (!*pszVar)
 
1165
            return rc;
 
1166
 
 
1167
        /* parse instruction. */
 
1168
        static struct
 
1169
        {
 
1170
            const char *pszInstr;
 
1171
            size_t      cchInstr;
 
1172
            unsigned    fFlag;
 
1173
            bool        fInverted;
 
1174
        } const aDest[] =
 
1175
        {
 
1176
            { "disabled",     sizeof("disabled"    ) - 1,   RTLOGFLAGS_DISABLED,            false },
 
1177
            { "enabled",      sizeof("enabled"     ) - 1,   RTLOGFLAGS_DISABLED,            true  },
 
1178
            { "buffered",     sizeof("buffered"    ) - 1,   RTLOGFLAGS_BUFFERED,            false },
 
1179
            { "unbuffered",   sizeof("unbuffered"  ) - 1,   RTLOGFLAGS_BUFFERED,            true  },
 
1180
            { "usecrlf",      sizeof("usecrlf"     ) - 1,   RTLOGFLAGS_USECRLF,             true },
 
1181
            { "uself",        sizeof("uself"       ) - 1,   RTLOGFLAGS_USECRLF,             false  },
 
1182
            { "rel",          sizeof("rel"         ) - 1,   RTLOGFLAGS_REL_TS,              false },
 
1183
            { "abs",          sizeof("abs"         ) - 1,   RTLOGFLAGS_REL_TS,              true  },
 
1184
            { "dec",          sizeof("dec"         ) - 1,   RTLOGFLAGS_DECIMAL_TS,          false },
 
1185
            { "hex",          sizeof("hex"         ) - 1,   RTLOGFLAGS_DECIMAL_TS,          true  },
 
1186
            { "flagno",       sizeof("flagno"      ) - 1,   RTLOGFLAGS_PREFIX_FLAG_NO,      false },
 
1187
            { "flag",         sizeof("flag"        ) - 1,   RTLOGFLAGS_PREFIX_FLAG,         false },
 
1188
            { "groupno",      sizeof("groupno"     ) - 1,   RTLOGFLAGS_PREFIX_GROUP_NO,     false },
 
1189
            { "group",        sizeof("group"       ) - 1,   RTLOGFLAGS_PREFIX_GROUP,        false },
 
1190
            { "tid",          sizeof("tid"         ) - 1,   RTLOGFLAGS_PREFIX_TID,          false },
 
1191
            { "thread",       sizeof("thread"      ) - 1,   RTLOGFLAGS_PREFIX_THREAD,       false },
 
1192
            { "timeprog",     sizeof("timeprog"    ) - 1,   RTLOGFLAGS_PREFIX_TIME_PROG,    false },
 
1193
            { "time",         sizeof("time"        ) - 1,   RTLOGFLAGS_PREFIX_TIME,         false },
 
1194
            { "msprog",       sizeof("msprog"      ) - 1,   RTLOGFLAGS_PREFIX_MS_PROG,      false },
 
1195
            { "tsc",          sizeof("tsc"         ) - 1,   RTLOGFLAGS_PREFIX_TSC,          false }, /* before ts! */
 
1196
            { "ts",           sizeof("ts"          ) - 1,   RTLOGFLAGS_PREFIX_TS,           false },
 
1197
        };
 
1198
 
 
1199
        /* check no prefix. */
 
1200
        bool fNo = false;
 
1201
        char ch;
 
1202
        while ((ch = *pszVar) != '\0')
 
1203
        {
 
1204
            if (ch == 'n' && pszVar[1] == 'o')
 
1205
            {
 
1206
                pszVar += 2;
 
1207
                fNo = !fNo;
 
1208
            }
 
1209
            else if (ch == '+')
 
1210
            {
 
1211
                pszVar++;
 
1212
                fNo = true;
 
1213
            }
 
1214
            else if (ch == '-' || ch == '!' || ch == '~')
 
1215
            {
 
1216
                pszVar++;
 
1217
                fNo = !fNo;
 
1218
            }
 
1219
            else
 
1220
                break;
 
1221
        }
 
1222
 
 
1223
        /* instruction. */
 
1224
        unsigned i;
 
1225
        for (i = 0; i < ELEMENTS(aDest); i++)
 
1226
        {
 
1227
            if (!strncmp(pszVar, aDest[i].pszInstr, aDest[i].cchInstr))
 
1228
            {
 
1229
                if (fNo == aDest[i].fInverted)
 
1230
                    pLogger->fFlags |= aDest[i].fFlag;
 
1231
                else
 
1232
                    pLogger->fFlags &= ~aDest[i].fFlag;
 
1233
                pszVar += aDest[i].cchInstr;
 
1234
                break;
 
1235
            }
 
1236
        }
 
1237
 
 
1238
        /* unknown instruction? */
 
1239
        if (i >= ELEMENTS(aDest))
 
1240
        {
 
1241
            AssertMsgFailed(("Invalid flags! unknown instruction %.20s\n", pszVar));
 
1242
            pszVar++;
 
1243
        }
 
1244
 
 
1245
        /* skip blanks and delimiters. */
 
1246
        while (isspace(*pszVar) || *pszVar == '\n' || *pszVar == '\r' || *pszVar == ';')
 
1247
            pszVar++;
 
1248
    } /* while more environment variable value left */
 
1249
 
 
1250
    return rc;
 
1251
}
 
1252
 
 
1253
 
 
1254
/**
 
1255
 * Flushes the specified logger.
 
1256
 *
 
1257
 * @param   pLogger     The logger instance to flush.
 
1258
 *                      If NULL the default instance is used. The default instance
 
1259
 *                      will not be initialized by this call.
 
1260
 */
 
1261
RTDECL(void) RTLogFlush(PRTLOGGER pLogger)
 
1262
{
 
1263
    /*
 
1264
     * Resolve defaults.
 
1265
     */
 
1266
    if (!pLogger)
 
1267
    {
 
1268
#ifdef IN_GC
 
1269
        pLogger = &g_Logger;
 
1270
#else
 
1271
        pLogger = g_pLogger;
 
1272
#endif
 
1273
        if (!pLogger)
 
1274
            return;
 
1275
    }
 
1276
 
 
1277
    /*
 
1278
     * Any thing to flush?
 
1279
     */
 
1280
    if (pLogger->offScratch)
 
1281
    {
 
1282
#ifndef IN_GC
 
1283
        /*
 
1284
         * Acquire logger instance sem.
 
1285
         */
 
1286
        int rc = rtlogLock(pLogger);
 
1287
        if (RT_FAILURE(rc))
 
1288
            return;
 
1289
#endif
 
1290
        /*
 
1291
         * Call worker.
 
1292
         */
 
1293
        rtlogFlush(pLogger);
 
1294
 
 
1295
#ifndef IN_GC
 
1296
        /*
 
1297
         * Release the semaphore.
 
1298
         */
 
1299
        rtlogUnlock(pLogger);
 
1300
#endif
 
1301
    }
 
1302
}
 
1303
 
 
1304
 
 
1305
/**
 
1306
 * Gets the default logger instance.
 
1307
 *
 
1308
 * @returns Pointer to default logger instance.
 
1309
 * @returns NULL if no default logger instance available.
 
1310
 */
 
1311
RTDECL(PRTLOGGER)   RTLogDefaultInstance(void)
 
1312
{
 
1313
#ifdef IN_GC
 
1314
    return &g_Logger;
 
1315
 
 
1316
#else /* !IN_GC */
 
1317
# ifdef IN_RING0
 
1318
    /*
 
1319
     * Check per thread loggers first.
 
1320
     */
 
1321
    if (g_cPerThreadLoggers)
 
1322
    {
 
1323
        const RTNATIVETHREAD Self = RTThreadNativeSelf();
 
1324
        int32_t i = ELEMENTS(g_aPerThreadLoggers);
 
1325
        while (i-- > 0)
 
1326
            if (g_aPerThreadLoggers[i].NativeThread == Self)
 
1327
                return g_aPerThreadLoggers[i].pLogger;
 
1328
    }
 
1329
# endif /* IN_RING0 */
 
1330
 
 
1331
    /*
 
1332
     * If no per thread logger, use the default one.
 
1333
     */
 
1334
    if (!g_pLogger)
 
1335
        g_pLogger = RTLogDefaultInit();
 
1336
    return g_pLogger;
 
1337
#endif /* !IN_GC */
 
1338
}
 
1339
 
 
1340
 
 
1341
#ifdef IN_RING0
 
1342
/**
 
1343
 * Changes the default logger instance for the current thread.
 
1344
 *
 
1345
 * @returns IPRT status code.
 
1346
 * @param   pLogger     The logger instance. Pass NULL for deregistration.
 
1347
 * @param   uKey        Associated key for cleanup purposes. If pLogger is NULL,
 
1348
 *                      all instances with this key will be deregistered. So in
 
1349
 *                      order to only deregister the instance associated with the
 
1350
 *                      current thread use 0.
 
1351
 */
 
1352
RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey)
 
1353
{
 
1354
    int             rc;
 
1355
    RTNATIVETHREAD  Self = RTThreadNativeSelf();
 
1356
    if (pLogger)
 
1357
    {
 
1358
        AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);
 
1359
 
 
1360
        /*
 
1361
         * Iterate the table to see if there is already an entry for this thread.
 
1362
         */
 
1363
        int32_t i = ELEMENTS(g_aPerThreadLoggers);
 
1364
        while (i-- > 0)
 
1365
            if (g_aPerThreadLoggers[i].NativeThread == Self)
 
1366
            {
 
1367
                ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
 
1368
                g_aPerThreadLoggers[i].pLogger = pLogger;
 
1369
                return VINF_SUCCESS;
 
1370
            }
 
1371
 
 
1372
        /*
 
1373
         * Allocate a new table entry.
 
1374
         */
 
1375
        i = ASMAtomicIncS32(&g_cPerThreadLoggers);
 
1376
        if (i > (int32_t)ELEMENTS(g_aPerThreadLoggers))
 
1377
        {
 
1378
            ASMAtomicDecS32(&g_cPerThreadLoggers);
 
1379
            return VERR_BUFFER_OVERFLOW; /* horrible error code! */
 
1380
        }
 
1381
 
 
1382
        for (unsigned j = 0; j < 10; j++)
 
1383
        {
 
1384
            i = ELEMENTS(g_aPerThreadLoggers);
 
1385
            while (i-- > 0)
 
1386
            {
 
1387
                AssertCompile(sizeof(RTNATIVETHREAD) == sizeof(void*));
 
1388
                if (    g_aPerThreadLoggers[i].NativeThread == NIL_RTNATIVETHREAD
 
1389
                    &&  ASMAtomicCmpXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)Self, (void *)NIL_RTNATIVETHREAD))
 
1390
                {
 
1391
                    ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);
 
1392
                    ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, pLogger);
 
1393
                    return VINF_SUCCESS;
 
1394
                }
 
1395
            }
 
1396
        }
 
1397
 
 
1398
        ASMAtomicDecS32(&g_cPerThreadLoggers);
 
1399
        rc = VERR_INTERNAL_ERROR;
 
1400
    }
 
1401
    else
 
1402
    {
 
1403
        /*
 
1404
         * Search the array for the current thread.
 
1405
         */
 
1406
        int32_t i = ELEMENTS(g_aPerThreadLoggers);
 
1407
        while (i-- > 0)
 
1408
            if (    g_aPerThreadLoggers[i].NativeThread == Self
 
1409
                ||  g_aPerThreadLoggers[i].uKey == uKey)
 
1410
            {
 
1411
                ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, NULL);
 
1412
                ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, NULL);
 
1413
                ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)NIL_RTNATIVETHREAD);
 
1414
                ASMAtomicDecS32(&g_cPerThreadLoggers);
 
1415
            }
 
1416
 
 
1417
        rc = VINF_SUCCESS;
 
1418
    }
 
1419
    return rc;
 
1420
}
 
1421
#endif
 
1422
 
 
1423
 
 
1424
/**
 
1425
 * Gets the default release logger instance.
 
1426
 *
 
1427
 * @returns Pointer to default release logger instance.
 
1428
 * @returns NULL if no default release logger instance available.
 
1429
 */
 
1430
RTDECL(PRTLOGGER)   RTLogRelDefaultInstance(void)
 
1431
{
 
1432
#ifdef IN_GC
 
1433
    return &g_RelLogger;
 
1434
#else /* !IN_GC */
 
1435
    return g_pRelLogger;
 
1436
#endif /* !IN_GC */
 
1437
}
 
1438
 
 
1439
 
 
1440
#ifndef IN_GC
 
1441
/**
 
1442
 * Sets the default logger instance.
 
1443
 *
 
1444
 * @returns iprt status code.
 
1445
 * @param   pLogger     The new default release logger instance.
 
1446
 */
 
1447
RTDECL(PRTLOGGER) RTLogRelSetDefaultInstance(PRTLOGGER pLogger)
 
1448
{
 
1449
    return (PRTLOGGER)ASMAtomicXchgPtr((void * volatile *)&g_pRelLogger, pLogger);
 
1450
}
 
1451
#endif /* !IN_GC */
 
1452
 
 
1453
 
 
1454
/**
 
1455
 * Write to a logger instance.
 
1456
 *
 
1457
 * @param   pLogger     Pointer to logger instance.
 
1458
 * @param   pvCallerRet Ignored.
 
1459
 * @param   pszFormat   Format string.
 
1460
 * @param   ...         Format arguments.
 
1461
 */
 
1462
RTDECL(void) RTLogLogger(PRTLOGGER pLogger, void *pvCallerRet, const char *pszFormat, ...)
 
1463
{
 
1464
    va_list args;
 
1465
    va_start(args, pszFormat);
 
1466
#if defined(RT_OS_DARWIN) && defined(RT_ARCH_X86) && defined(IN_RING3)
 
1467
    /* manually align the stack before doing the call.
 
1468
     * We boldly assume that there is a stack frame here! */
 
1469
    __asm__ __volatile__("andl $-32, %%esp\t\n" ::: "%esp");
 
1470
    RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
 
1471
#else
 
1472
    RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
 
1473
#endif
 
1474
    va_end(args);
 
1475
}
 
1476
 
 
1477
 
 
1478
/**
 
1479
 * Write to a logger instance.
 
1480
 *
 
1481
 * @param   pLogger     Pointer to logger instance.
 
1482
 * @param   pszFormat   Format string.
 
1483
 * @param   args        Format arguments.
 
1484
 */
 
1485
RTDECL(void) RTLogLoggerV(PRTLOGGER pLogger, const char *pszFormat, va_list args)
 
1486
{
 
1487
    RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);
 
1488
}
 
1489
 
 
1490
 
 
1491
/**
 
1492
 * Write to a logger instance.
 
1493
 *
 
1494
 * This function will check whether the instance, group and flags makes up a
 
1495
 * logging kind which is currently enabled before writing anything to the log.
 
1496
 *
 
1497
 * @param   pLogger     Pointer to logger instance. If NULL the default logger instance will be attempted.
 
1498
 * @param   fFlags      The logging flags.
 
1499
 * @param   iGroup      The group.
 
1500
 *                      The value ~0U is reserved for compatability with RTLogLogger[V] and is
 
1501
 *                      only for internal usage!
 
1502
 * @param   pszFormat   Format string.
 
1503
 * @param   ...         Format arguments.
 
1504
 * @remark  This is a worker function of LogIt.
 
1505
 */
 
1506
RTDECL(void) RTLogLoggerEx(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
 
1507
{
 
1508
    va_list args;
 
1509
    va_start(args, pszFormat);
 
1510
    RTLogLoggerExV(pLogger, fFlags, iGroup, pszFormat, args);
 
1511
    va_end(args);
 
1512
}
 
1513
 
 
1514
 
 
1515
/**
 
1516
 * Write to a logger instance.
 
1517
 *
 
1518
 * This function will check whether the instance, group and flags makes up a
 
1519
 * logging kind which is currently enabled before writing anything to the log.
 
1520
 *
 
1521
 * @param   pLogger     Pointer to logger instance. If NULL the default logger instance will be attempted.
 
1522
 * @param   fFlags      The logging flags.
 
1523
 * @param   iGroup      The group.
 
1524
 *                      The value ~0U is reserved for compatability with RTLogLogger[V] and is
 
1525
 *                      only for internal usage!
 
1526
 * @param   pszFormat   Format string.
 
1527
 * @param   args        Format arguments.
 
1528
 */
 
1529
RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
 
1530
{
 
1531
    /*
 
1532
     * A NULL logger means default instance.
 
1533
     */
 
1534
    if (!pLogger)
 
1535
    {
 
1536
        pLogger = RTLogDefaultInstance();
 
1537
        if (!pLogger)
 
1538
            return;
 
1539
    }
 
1540
    rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);
 
1541
}
 
1542
 
 
1543
 
 
1544
/**
 
1545
 * Write to a logger instance, defaulting to the release one.
 
1546
 *
 
1547
 * This function will check whether the instance, group and flags makes up a
 
1548
 * logging kind which is currently enabled before writing anything to the log.
 
1549
 *
 
1550
 * @param   pLogger     Pointer to logger instance.
 
1551
 * @param   fFlags      The logging flags.
 
1552
 * @param   iGroup      The group.
 
1553
 *                      The value ~0U is reserved for compatability with RTLogLogger[V] and is
 
1554
 *                      only for internal usage!
 
1555
 * @param   pszFormat   Format string.
 
1556
 * @param   ...         Format arguments.
 
1557
 * @remark  This is a worker function for LogRelIt.
 
1558
 */
 
1559
RTDECL(void) RTLogRelLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
 
1560
{
 
1561
    va_list args;
 
1562
    va_start(args, pszFormat);
 
1563
    RTLogRelLoggerV(pLogger, fFlags, iGroup, pszFormat, args);
 
1564
    va_end(args);
 
1565
}
 
1566
 
 
1567
 
 
1568
/**
 
1569
 * Write to a logger instance, defaulting to the release one.
 
1570
 *
 
1571
 * This function will check whether the instance, group and flags makes up a
 
1572
 * logging kind which is currently enabled before writing anything to the log.
 
1573
 *
 
1574
 * @param   pLogger     Pointer to logger instance. If NULL the default release instance is attempted.
 
1575
 * @param   fFlags      The logging flags.
 
1576
 * @param   iGroup      The group.
 
1577
 *                      The value ~0U is reserved for compatability with RTLogLogger[V] and is
 
1578
 *                      only for internal usage!
 
1579
 * @param   pszFormat   Format string.
 
1580
 * @param   args        Format arguments.
 
1581
 */
 
1582
RTDECL(void) RTLogRelLoggerV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
 
1583
{
 
1584
    /*
 
1585
     * A NULL logger means default instance.
 
1586
     */
 
1587
    if (!pLogger)
 
1588
    {
 
1589
        pLogger = RTLogRelDefaultInstance();
 
1590
        if (!pLogger)
 
1591
            return;
 
1592
    }
 
1593
    rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);
 
1594
}
 
1595
 
 
1596
 
 
1597
/**
 
1598
 * Worker for the RTLog[Rel]Logger*() functions.
 
1599
 *
 
1600
 * @param   pLogger     Pointer to logger instance.
 
1601
 * @param   fFlags      The logging flags.
 
1602
 * @param   iGroup      The group.
 
1603
 *                      The value ~0U is reserved for compatability with RTLogLogger[V] and is
 
1604
 *                      only for internal usage!
 
1605
 * @param   pszFormat   Format string.
 
1606
 * @param   args        Format arguments.
 
1607
 */
 
1608
static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
 
1609
{
 
1610
    /*
 
1611
     * Validate and correct iGroup.
 
1612
     */
 
1613
    if (iGroup != ~0U && iGroup >= pLogger->cGroups)
 
1614
        iGroup = 0;
 
1615
 
 
1616
    /*
 
1617
     * If no output, then just skip it.
 
1618
     */
 
1619
    if (    (pLogger->fFlags & RTLOGFLAGS_DISABLED)
 
1620
#ifndef IN_GC
 
1621
        || !pLogger->fDestFlags
 
1622
#endif
 
1623
        || !pszFormat || !*pszFormat)
 
1624
        return;
 
1625
    if (    iGroup != ~0U
 
1626
        &&  (pLogger->afGroups[iGroup] & (fFlags | RTLOGGRPFLAGS_ENABLED)) != (fFlags | RTLOGGRPFLAGS_ENABLED))
 
1627
        return;
 
1628
 
 
1629
    /*
 
1630
     * Acquire logger instance sem.
 
1631
     */
 
1632
    int rc = rtlogLock(pLogger);
 
1633
    if (RT_FAILURE(rc))
 
1634
        return;
 
1635
 
 
1636
    /*
 
1637
     * Format the message and perhaps flush it.
 
1638
     */
 
1639
    if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF))
 
1640
    {
 
1641
        RTLOGOUTPUTPREFIXEDARGS OutputArgs;
 
1642
        OutputArgs.pLogger = pLogger;
 
1643
        OutputArgs.iGroup = iGroup;
 
1644
        OutputArgs.fFlags = fFlags;
 
1645
        RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);
 
1646
    }
 
1647
    else
 
1648
        RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);
 
1649
    if (    !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)
 
1650
        &&  pLogger->offScratch)
 
1651
        rtlogFlush(pLogger);
 
1652
 
 
1653
    /*
 
1654
     * Release the semaphore.
 
1655
     */
 
1656
    rtlogUnlock(pLogger);
 
1657
}
 
1658
 
 
1659
 
 
1660
/**
 
1661
 * printf like function for writing to the default log.
 
1662
 *
 
1663
 * @param   pszFormat   Printf like format string.
 
1664
 * @param   ...         Optional arguments as specified in pszFormat.
 
1665
 *
 
1666
 * @remark The API doesn't support formatting of floating point numbers at the moment.
 
1667
 */
 
1668
RTDECL(void) RTLogPrintf(const char *pszFormat, ...)
 
1669
{
 
1670
    va_list args;
 
1671
    va_start(args, pszFormat);
 
1672
    RTLogPrintfV(pszFormat, args);
 
1673
    va_end(args);
 
1674
}
 
1675
 
 
1676
 
 
1677
/**
 
1678
 * vprintf like function for writing to the default log.
 
1679
 *
 
1680
 * @param   pszFormat   Printf like format string.
 
1681
 * @param   args        Optional arguments as specified in pszFormat.
 
1682
 *
 
1683
 * @remark The API doesn't support formatting of floating point numbers at the moment.
 
1684
 */
 
1685
RTDECL(void) RTLogPrintfV(const char *pszFormat, va_list args)
 
1686
{
 
1687
    RTLogLoggerV(NULL, pszFormat, args);
 
1688
}
 
1689
 
 
1690
 
 
1691
/**
 
1692
 * printf like function for writing to the default release log.
 
1693
 *
 
1694
 * @param   pszFormat   Printf like format string.
 
1695
 * @param   ...         Optional arguments as specified in pszFormat.
 
1696
 *
 
1697
 * @remark The API doesn't support formatting of floating point numbers at the moment.
 
1698
 */
 
1699
RTDECL(void) RTLogRelPrintf(const char *pszFormat, ...)
 
1700
{
 
1701
    va_list args;
 
1702
    va_start(args, pszFormat);
 
1703
    RTLogRelPrintfV(pszFormat, args);
 
1704
    va_end(args);
 
1705
}
 
1706
 
 
1707
 
 
1708
/**
 
1709
 * vprintf like function for writing to the default release log.
 
1710
 *
 
1711
 * @param   pszFormat   Printf like format string.
 
1712
 * @param   args        Optional arguments as specified in pszFormat.
 
1713
 *
 
1714
 * @remark The API doesn't support formatting of floating point numbers at the moment.
 
1715
 */
 
1716
RTDECL(void) RTLogRelPrintfV(const char *pszFormat, va_list args)
 
1717
{
 
1718
    RTLogRelLoggerV(NULL, 0, ~0U, pszFormat, args);
 
1719
}
 
1720
 
 
1721
 
 
1722
/**
 
1723
 * Writes the buffer to the given log device without checking for buffered
 
1724
 * data or anything.
 
1725
 * Used by the RTLogFlush() function.
 
1726
 *
 
1727
 * @param   pLogger     The logger instance to write to. NULL is not allowed!
 
1728
 */
 
1729
static void rtlogFlush(PRTLOGGER pLogger)
 
1730
{
 
1731
#ifndef IN_GC
 
1732
    if (pLogger->fDestFlags & RTLOGDEST_USER)
 
1733
        RTLogWriteUser(pLogger->achScratch, pLogger->offScratch);
 
1734
 
 
1735
    if (pLogger->fDestFlags & RTLOGDEST_DEBUGGER)
 
1736
        RTLogWriteDebugger(pLogger->achScratch, pLogger->offScratch);
 
1737
 
 
1738
# ifdef IN_RING3
 
1739
    if (pLogger->fDestFlags & RTLOGDEST_FILE)
 
1740
        RTFileWrite(pLogger->File, pLogger->achScratch, pLogger->offScratch, NULL);
 
1741
# endif
 
1742
 
 
1743
    if (pLogger->fDestFlags & RTLOGDEST_STDOUT)
 
1744
        RTLogWriteStdOut(pLogger->achScratch, pLogger->offScratch);
 
1745
 
 
1746
    if (pLogger->fDestFlags & RTLOGDEST_STDERR)
 
1747
        RTLogWriteStdErr(pLogger->achScratch, pLogger->offScratch);
 
1748
 
 
1749
# if (defined(IN_RING0) || defined(IN_GC)) && !defined(LOG_NO_COM)
 
1750
    if (pLogger->fDestFlags & RTLOGDEST_COM)
 
1751
        RTLogWriteCom(pLogger->achScratch, pLogger->offScratch);
 
1752
# endif
 
1753
#endif /* !IN_GC */
 
1754
 
 
1755
    if (pLogger->pfnFlush)
 
1756
        pLogger->pfnFlush(pLogger);
 
1757
 
 
1758
    /* empty the buffer. */
 
1759
    pLogger->offScratch = 0;
 
1760
}
 
1761
 
 
1762
 
 
1763
/**
 
1764
 * Callback for RTLogFormatV which writes to the com port.
 
1765
 * See PFNLOGOUTPUT() for details.
 
1766
 */
 
1767
static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars)
 
1768
{
 
1769
    PRTLOGGER pLogger = (PRTLOGGER)pv;
 
1770
    if (cbChars)
 
1771
    {
 
1772
        size_t cbRet = 0;
 
1773
        for (;;)
 
1774
        {
 
1775
#if defined(DEBUG) && defined(IN_RING3)
 
1776
            /* sanity */
 
1777
            if (pLogger->offScratch >= sizeof(pLogger->achScratch))
 
1778
            {
 
1779
                fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
 
1780
                        pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
 
1781
                AssertBreakpoint(); AssertBreakpoint();
 
1782
            }
 
1783
#endif
 
1784
 
 
1785
            /* how much */
 
1786
            size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
 
1787
            if (cb > cbChars)
 
1788
                cb = cbChars;
 
1789
 
 
1790
            /* copy */
 
1791
            memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
 
1792
 
 
1793
            /* advance */
 
1794
            pLogger->offScratch += cb;
 
1795
            cbRet += cb;
 
1796
            cbChars -= cb;
 
1797
 
 
1798
            /* done? */
 
1799
            if (cbChars <= 0)
 
1800
                return cbRet;
 
1801
 
 
1802
            pachChars += cb;
 
1803
 
 
1804
            /* flush */
 
1805
            rtlogFlush(pLogger);
 
1806
        }
 
1807
 
 
1808
        /* won't ever get here! */
 
1809
    }
 
1810
    else
 
1811
    {
 
1812
        /*
 
1813
         * Termination call.
 
1814
         * There's always space for a terminator, and it's not counted.
 
1815
         */
 
1816
        pLogger->achScratch[pLogger->offScratch] = '\0';
 
1817
        return 0;
 
1818
    }
 
1819
}
 
1820
 
 
1821
 
 
1822
 
 
1823
/**
 
1824
 * Callback for RTLogFormatV which writes to the logger instance.
 
1825
 * This version supports prefixes.
 
1826
 *
 
1827
 * See PFNLOGOUTPUT() for details.
 
1828
 */
 
1829
static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars)
 
1830
{
 
1831
    PRTLOGOUTPUTPREFIXEDARGS    pArgs = (PRTLOGOUTPUTPREFIXEDARGS)pv;
 
1832
    PRTLOGGER                   pLogger = pArgs->pLogger;
 
1833
    if (cbChars)
 
1834
    {
 
1835
        size_t cbRet = 0;
 
1836
        for (;;)
 
1837
        {
 
1838
            size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
 
1839
 
 
1840
            /*
 
1841
             * Pending prefix?
 
1842
             */
 
1843
            if (pLogger->fPendingPrefix)
 
1844
            {
 
1845
                pLogger->fPendingPrefix = false;
 
1846
 
 
1847
#if defined(DEBUG) && defined(IN_RING3)
 
1848
                /* sanity */
 
1849
                if (pLogger->offScratch >= sizeof(pLogger->achScratch))
 
1850
                {
 
1851
                    fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
 
1852
                            pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
 
1853
                    AssertBreakpoint(); AssertBreakpoint();
 
1854
                }
 
1855
#endif
 
1856
 
 
1857
                /*
 
1858
                 * Flush the buffer if there isn't enough room for the maximum prefix config.
 
1859
                 * Max is 124, add a couple of extra bytes.
 
1860
                 */
 
1861
                if (cb < 128 + 18 + 22)
 
1862
                {
 
1863
                    rtlogFlush(pLogger);
 
1864
                    cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
 
1865
                }
 
1866
 
 
1867
                /*
 
1868
                 * Write the prefixes.
 
1869
                 * psz is pointing to the current position.
 
1870
                 */
 
1871
                char *psz = &pLogger->achScratch[pLogger->offScratch];
 
1872
                if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TS)
 
1873
                {
 
1874
#if defined(IN_RING3) || defined(IN_GC)
 
1875
                    uint64_t u64 = RTTimeNanoTS();
 
1876
#else
 
1877
                    uint64_t u64 = ~0;
 
1878
#endif
 
1879
                    int          iBase  = 16;
 
1880
                    unsigned int fFlags = RTSTR_F_ZEROPAD;
 
1881
                    if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
 
1882
                    {
 
1883
                        iBase = 10;
 
1884
                        fFlags = 0;
 
1885
                    }
 
1886
                    if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
 
1887
                    {
 
1888
                        static uint64_t s_u64LastTs;
 
1889
                        uint64_t        u64DiffTs = u64 - s_u64LastTs;
 
1890
                        s_u64LastTs = u64;
 
1891
                        /* We could have been preempted just before reading of s_u64LastTs by
 
1892
                         * another thread which wrote s_u64LastTs. In that case the difference
 
1893
                         * is negative which we simply ignore. */
 
1894
                        u64         = (int64_t)u64DiffTs < 0 ? 0 : u64DiffTs;
 
1895
                    }
 
1896
                    /* 1E15 nanoseconds = 11 days */
 
1897
                    psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags);
 
1898
                    *psz++ = ' ';
 
1899
                }
 
1900
                if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TSC)
 
1901
                {
 
1902
                    uint64_t     u64    = ASMReadTSC();
 
1903
                    int          iBase  = 16;
 
1904
                    unsigned int fFlags = RTSTR_F_ZEROPAD;
 
1905
                    if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)
 
1906
                    {
 
1907
                        iBase = 10;
 
1908
                        fFlags = 0;
 
1909
                    }
 
1910
                    if (pLogger->fFlags & RTLOGFLAGS_REL_TS)
 
1911
                    {
 
1912
                        static uint64_t s_u64LastTsc;
 
1913
                        uint64_t        u64DiffTsc = u64 - s_u64LastTsc;
 
1914
                        s_u64LastTsc = u64;
 
1915
                        /* We could have been preempted just before reading of s_u64LastTsc by
 
1916
                         * another thread which wrote s_u64LastTsc. In that case the difference
 
1917
                         * is negative which we simply ignore. */
 
1918
                        u64          = u64DiffTsc < 0 ? 0 : u64DiffTsc;
 
1919
                    }
 
1920
                    /* 1E15 ticks at 4GHz = 69 hours */
 
1921
                    psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags);
 
1922
                    *psz++ = ' ';
 
1923
                }
 
1924
                if (pLogger->fFlags & RTLOGFLAGS_PREFIX_MS_PROG)
 
1925
                {
 
1926
#if defined(IN_RING3) || defined(IN_GC)
 
1927
                    uint64_t u64 = RTTimeProgramMilliTS();
 
1928
#else
 
1929
                    uint64_t u64 = 0;
 
1930
#endif
 
1931
                    /* 1E8 milliseconds = 27 hours */
 
1932
                    psz += RTStrFormatNumber(psz, u64, 10, 9, 0, RTSTR_F_ZEROPAD);
 
1933
                    *psz++ = ' ';
 
1934
                }
 
1935
                if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME)
 
1936
                {
 
1937
#ifdef IN_RING3
 
1938
                    RTTIMESPEC TimeSpec;
 
1939
                    RTTIME Time;
 
1940
                    RTTimeExplode(&Time, RTTimeNow(&TimeSpec));
 
1941
                    psz += RTStrFormatNumber(psz, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD);
 
1942
                    *psz++ = ':';
 
1943
                    psz += RTStrFormatNumber(psz, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD);
 
1944
                    *psz++ = ':';
 
1945
                    psz += RTStrFormatNumber(psz, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD);
 
1946
                    *psz++ = '.';
 
1947
                    psz += RTStrFormatNumber(psz, Time.u32Nanosecond / 1000000, 10, 3, 0, RTSTR_F_ZEROPAD);
 
1948
                    *psz++ = ' ';
 
1949
#else
 
1950
                    memset(psz, ' ', 13);
 
1951
                    psz += 13;
 
1952
#endif
 
1953
                }
 
1954
                if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME_PROG)
 
1955
                {
 
1956
#ifdef IN_RING3
 
1957
                    uint64_t u64 = RTTimeProgramMilliTS();
 
1958
                    psz += RTStrFormatNumber(psz, (uint32_t)(u64 / (60 * 60 * 1000)), 10, 2, 0, RTSTR_F_ZEROPAD);
 
1959
                    *psz++ = ':';
 
1960
                    uint32_t u32 = (uint32_t)(u64 % (60 * 60 * 1000));
 
1961
                    psz += RTStrFormatNumber(psz, u32 / (60 * 1000), 10, 2, 0, RTSTR_F_ZEROPAD);
 
1962
                    *psz++ = ':';
 
1963
                    u32 %= 60 * 1000;
 
1964
                    psz += RTStrFormatNumber(psz, u32 / 1000, 10, 2, 0, RTSTR_F_ZEROPAD);
 
1965
                    *psz++ = '.';
 
1966
                    psz += RTStrFormatNumber(psz, u32 % 1000, 10, 3, 0, RTSTR_F_ZEROPAD);
 
1967
                    *psz++ = ' ';
 
1968
#else
 
1969
                    memset(psz, ' ', 13);
 
1970
                    psz += 13;
 
1971
#endif
 
1972
                }
 
1973
# if 0
 
1974
                if (pLogger->fFlags & RTLOGFLAGS_PREFIX_DATETIME)
 
1975
                {
 
1976
                    char szDate[32];
 
1977
                    RTTIMESPEC Time;
 
1978
                    RTTimeSpecToString(RTTimeNow(&Time), szDate, sizeof(szDate));
 
1979
                    size_t cch = strlen(szDate);
 
1980
                    memcpy(psz, szDate, cch);
 
1981
                    psz += cch;
 
1982
                    *psz++ = ' ';
 
1983
                }
 
1984
# endif
 
1985
                if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TID)
 
1986
                {
 
1987
#ifdef IN_RING3
 
1988
                    RTNATIVETHREAD Thread = RTThreadNativeSelf();
 
1989
#else
 
1990
                    RTNATIVETHREAD Thread = NIL_RTNATIVETHREAD;
 
1991
#endif
 
1992
                    psz += RTStrFormatNumber(psz, Thread, 16, sizeof(RTNATIVETHREAD) * 2, 0, RTSTR_F_ZEROPAD);
 
1993
                    *psz++ = ' ';
 
1994
                }
 
1995
                if (pLogger->fFlags & RTLOGFLAGS_PREFIX_THREAD)
 
1996
                {
 
1997
#ifdef IN_RING3
 
1998
                    const char *pszName = RTThreadSelfName();
 
1999
#elif defined IN_GC
 
2000
                    const char *pszName = "EMT-GC";
 
2001
#else
 
2002
                    const char *pszName = "EMT-R0";
 
2003
#endif
 
2004
                    size_t cch = 0;
 
2005
                    if (pszName)
 
2006
                    {
 
2007
                        cch = strlen(pszName);
 
2008
                        cch = RT_MIN(cch, 16);
 
2009
                        memcpy(psz, pszName, cch);
 
2010
                        psz += cch;
 
2011
                    }
 
2012
                    do
 
2013
                        *psz++ = ' ';
 
2014
                    while (cch++ < 8);
 
2015
                }
 
2016
                if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG_NO)
 
2017
                {
 
2018
                    psz += RTStrFormatNumber(psz, pArgs->fFlags, 16, 8, 0, RTSTR_F_ZEROPAD);
 
2019
                    *psz++ = ' ';
 
2020
                }
 
2021
                if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG)
 
2022
                {
 
2023
#ifdef IN_RING3
 
2024
                    const char *pszGroup = pArgs->iGroup != ~0U ? pLogger->papszGroups[pArgs->iGroup] : NULL;
 
2025
#else
 
2026
                    const char *pszGroup = NULL;
 
2027
#endif
 
2028
                    size_t cch = 0;
 
2029
                    if (pszGroup)
 
2030
                    {
 
2031
                        cch = strlen(pszGroup);
 
2032
                        cch = RT_MIN(cch, 16);
 
2033
                        memcpy(psz, pszGroup, cch);
 
2034
                        psz += cch;
 
2035
                    }
 
2036
                    do
 
2037
                        *psz++ = ' ';
 
2038
                    while (cch++ < 8);
 
2039
                }
 
2040
                if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP_NO)
 
2041
                {
 
2042
                    if (pArgs->iGroup != ~0U)
 
2043
                    {
 
2044
                        psz += RTStrFormatNumber(psz, pArgs->iGroup, 16, 3, 0, RTSTR_F_ZEROPAD);
 
2045
                        *psz++ = ' ';
 
2046
                    }
 
2047
                    else
 
2048
                    {
 
2049
                        memcpy(psz, "-1  ", sizeof("-1  ") - 1);
 
2050
                        psz += sizeof("-1  ") - 1;
 
2051
                    }
 
2052
                }
 
2053
                if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP)
 
2054
                {
 
2055
                    const unsigned fGrp = pLogger->afGroups[pArgs->iGroup != ~0U ? pArgs->iGroup : 0];
 
2056
                    const char *pszGroup;
 
2057
                    size_t cch;
 
2058
                    switch (pArgs->fFlags & fGrp)
 
2059
                    {
 
2060
                        case 0:                         pszGroup = "--------";  cch = sizeof("--------") - 1; break;
 
2061
                        case RTLOGGRPFLAGS_ENABLED:     pszGroup = "enabled" ;  cch = sizeof("enabled" ) - 1; break;
 
2062
                        case RTLOGGRPFLAGS_LEVEL_1:     pszGroup = "level 1" ;  cch = sizeof("level 1" ) - 1; break;
 
2063
                        case RTLOGGRPFLAGS_LEVEL_2:     pszGroup = "level 2" ;  cch = sizeof("level 2" ) - 1; break;
 
2064
                        case RTLOGGRPFLAGS_LEVEL_3:     pszGroup = "level 3" ;  cch = sizeof("level 3" ) - 1; break;
 
2065
                        case RTLOGGRPFLAGS_LEVEL_4:     pszGroup = "level 4" ;  cch = sizeof("level 4" ) - 1; break;
 
2066
                        case RTLOGGRPFLAGS_LEVEL_5:     pszGroup = "level 5" ;  cch = sizeof("level 5" ) - 1; break;
 
2067
                        case RTLOGGRPFLAGS_LEVEL_6:     pszGroup = "level 6" ;  cch = sizeof("level 6" ) - 1; break;
 
2068
                        case RTLOGGRPFLAGS_FLOW:        pszGroup = "flow"    ;  cch = sizeof("flow"    ) - 1; break;
 
2069
 
 
2070
                        /* personal groups */
 
2071
                        case RTLOGGRPFLAGS_LELIK:       pszGroup = "lelik"   ;  cch = sizeof("lelik"   ) - 1; break;
 
2072
                        case RTLOGGRPFLAGS_MICHAEL:     pszGroup = "Michael" ;  cch = sizeof("Michael" ) - 1; break;
 
2073
                        case RTLOGGRPFLAGS_DMIK:        pszGroup = "dmik"    ;  cch = sizeof("dmik"    ) - 1; break;
 
2074
                        case RTLOGGRPFLAGS_SUNLOVER:    pszGroup = "sunlover";  cch = sizeof("sunlover") - 1; break;
 
2075
                        case RTLOGGRPFLAGS_ACHIM:       pszGroup = "Achim"   ;  cch = sizeof("Achim"   ) - 1; break;
 
2076
                        case RTLOGGRPFLAGS_SANDER:      pszGroup = "Sander"  ;  cch = sizeof("Sander"  ) - 1; break;
 
2077
                        case RTLOGGRPFLAGS_KLAUS:       pszGroup = "Klaus"   ;  cch = sizeof("Klaus"   ) - 1; break;
 
2078
                        case RTLOGGRPFLAGS_FRANK:       pszGroup = "Frank"   ;  cch = sizeof("Frank"   ) - 1; break;
 
2079
                        case RTLOGGRPFLAGS_BIRD:        pszGroup = "bird"    ;  cch = sizeof("bird"    ) - 1; break;
 
2080
                        case RTLOGGRPFLAGS_NONAME:      pszGroup = "noname"  ;  cch = sizeof("noname"  ) - 1; break;
 
2081
                        default:                        pszGroup = "????????";  cch = sizeof("????????") - 1; break;
 
2082
                    }
 
2083
                    if (pszGroup)
 
2084
                    {
 
2085
                        cch = RT_MIN(cch, 16);
 
2086
                        memcpy(psz, pszGroup, cch);
 
2087
                        psz += cch;
 
2088
                    }
 
2089
                    do
 
2090
                        *psz++ = ' ';
 
2091
                    while (cch++ < 8);
 
2092
                }
 
2093
 
 
2094
                /*
 
2095
                 * Done, figure what we've used and advance the buffer and free size.
 
2096
                 */
 
2097
                cb = psz - &pLogger->achScratch[pLogger->offScratch];
 
2098
                Assert(cb <= 124);
 
2099
                pLogger->offScratch += cb;
 
2100
                cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
 
2101
            }
 
2102
            else if (cb <= 0)
 
2103
            {
 
2104
                rtlogFlush(pLogger);
 
2105
                cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;
 
2106
            }
 
2107
 
 
2108
#if defined(DEBUG) && defined(IN_RING3)
 
2109
            /* sanity */
 
2110
            if (pLogger->offScratch >= sizeof(pLogger->achScratch))
 
2111
            {
 
2112
                fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",
 
2113
                        pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));
 
2114
                AssertBreakpoint(); AssertBreakpoint();
 
2115
            }
 
2116
#endif
 
2117
 
 
2118
            /* how much */
 
2119
            if (cb > cbChars)
 
2120
                cb = cbChars;
 
2121
 
 
2122
            /* have newline? */
 
2123
            const char *pszNewLine = (const char *)memchr(pachChars, '\n', cb);
 
2124
            if (pszNewLine)
 
2125
            {
 
2126
                if (pLogger->fFlags & RTLOGFLAGS_USECRLF)
 
2127
                    cb = pszNewLine - pachChars;
 
2128
                else
 
2129
                {
 
2130
                    cb = pszNewLine - pachChars + 1;
 
2131
                    pLogger->fPendingPrefix = true;
 
2132
                }
 
2133
            }
 
2134
 
 
2135
            /* copy */
 
2136
            memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);
 
2137
 
 
2138
            /* advance */
 
2139
            pLogger->offScratch += cb;
 
2140
            cbRet += cb;
 
2141
            cbChars -= cb;
 
2142
 
 
2143
            if (    pszNewLine
 
2144
                &&  (pLogger->fFlags & RTLOGFLAGS_USECRLF)
 
2145
                &&  pLogger->offScratch + 2 < sizeof(pLogger->achScratch))
 
2146
            {
 
2147
                memcpy(&pLogger->achScratch[pLogger->offScratch], "\r\n", 2);
 
2148
                pLogger->offScratch += 2;
 
2149
                cbRet++;
 
2150
                cbChars--;
 
2151
                cb++;
 
2152
                pLogger->fPendingPrefix = true;
 
2153
            }
 
2154
 
 
2155
            /* done? */
 
2156
            if (cbChars <= 0)
 
2157
                return cbRet;
 
2158
            pachChars += cb;
 
2159
        }
 
2160
 
 
2161
        /* won't ever get here! */
 
2162
    }
 
2163
    else
 
2164
    {
 
2165
        /*
 
2166
         * Termination call.
 
2167
         * There's always space for a terminator, and it's not counted.
 
2168
         */
 
2169
        pLogger->achScratch[pLogger->offScratch] = '\0';
 
2170
        return 0;
 
2171
    }
 
2172
}
 
2173