log.cpp revision a8f8bfc5b86b83e4a09803f606d678ccf4935afb
/* $Id$ */
/** @file
* Runtime VBox - Logger.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#ifndef IN_GC
# include <iprt/semaphore.h>
#endif
#ifdef IN_RING3
#endif
#ifdef IN_RING3
# include <stdio.h>
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Arguments passed to the output function.
*/
typedef struct RTLOGOUTPUTPREFIXEDARGS
{
/** The logger instance. */
/** The flags. (used for prefixing.) */
unsigned fFlags;
/** The group. (used for prefixing.) */
unsigned iGroup;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
#ifndef IN_GC
static unsigned rtlogGroupFlags(const char *psz);
#endif
static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args);
/*******************************************************************************
* Global Variables *
*******************************************************************************/
#ifdef IN_GC
/** Default logger instance. */
/** Default relese logger instance. */
#else /* !IN_GC */
/** Default logger instance. */
/** Default release logger instance. */
static PRTLOGGER g_pRelLogger;
#endif /* !IN_GC */
#ifdef IN_RING3
/** The RTThreadGetWriteLockCount() change caused by the logger mutex semaphore. */
static uint32_t volatile g_cLoggerLockCount;
#endif
#ifdef IN_RING0
/** Number of per-thread loggers. */
static int32_t volatile g_cPerThreadLoggers;
/** Per-thread loggers.
* This is just a quick TLS hack suitable for debug logging only.
* If we run out of entries, just unload and reload the driver. */
static struct RTLOGGERPERTHREAD
{
/** The thread. */
RTNATIVETHREAD volatile NativeThread;
/** The (process / session) key. */
/** The logger instance.*/
} g_aPerThreadLoggers[8] =
{ { NIL_RTNATIVETHREAD, 0, 0},
{ NIL_RTNATIVETHREAD, 0, 0},
{ NIL_RTNATIVETHREAD, 0, 0},
{ NIL_RTNATIVETHREAD, 0, 0},
{ NIL_RTNATIVETHREAD, 0, 0},
{ NIL_RTNATIVETHREAD, 0, 0},
{ NIL_RTNATIVETHREAD, 0, 0},
{ NIL_RTNATIVETHREAD, 0, 0}
};
#endif /* IN_RING0 */
/**
* Locks the logger instance.
*
* @returns See RTSemFastMutexRequest().
* @param pLogger The logger instance.
*/
{
#ifndef IN_GC
{
}
#endif
return VINF_SUCCESS;
}
/**
* Unlocks the logger instance.
* @param pLogger The logger instance.
*/
{
#ifndef IN_GC
#endif
return;
}
#ifndef IN_GC
/**
* Create a logger instance, comprehensive version.
*
* @returns iprt status code.
*
* @param ppLogger Where to store the logger instance.
* @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
* @param pszGroupSettings The initial group settings.
* @param pszEnvVarBase Base name for the environment variables for this instance.
* @param cGroups Number of groups in the array.
* @param papszGroups Pointer to array of groups. This must stick around for the life of the
* logger instance.
* @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
* @param pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.
* @param cchErrorMsg The size of the error message buffer.
* @param pszFilenameFmt Log filename format string. Standard RTStrFormat().
* @param ... Format arguments.
*/
{
int rc;
/*
* Validate input.
*/
if ( (cGroups && !papszGroups)
)
{
AssertMsgFailed(("Invalid parameters!\n"));
return VERR_INVALID_PARAMETER;
}
if (pszErrorMsg)
/*
* Allocate a logger instance.
*/
if (pLogger)
{
pLogger->fPendingPrefix = true;
if (pszGroupSettings)
/*
* Emit wrapper code.
*/
if (pu8Code)
{
#ifdef RT_ARCH_AMD64
/* this wrapper will not be used on AMD64, we will be requiring C99 compilers there. */
*pu8Code++ = 0xcc;
#else
pu8Code += sizeof(void *);
*pu8Code++ = 0x64;
*pu8Code++ = 0x24;
*pu8Code++ = 0x04;
#endif
#ifdef IN_RING3 /* files and env.vars. are only accessible when in R3 at the present time. */
/*
* Format the filename.
*/
if (pszFilenameFmt)
{
}
/*
* Parse the environment variables.
*/
if (pszEnvVarBase)
{
/* make temp copy of environment variable base. */
/*
* Destination.
*/
if (pszVar)
{
while (*pszVar)
{
/* skip blanks. */
while (RT_C_IS_SPACE(*pszVar))
pszVar++;
if (!*pszVar)
break;
/* parse instruction. */
static struct
{
const char *pszInstr;
unsigned fFlag;
} const aDest[] =
{
{ "stdout", RTLOGDEST_STDOUT },
{ "stderr", RTLOGDEST_STDERR },
{ "debugger", RTLOGDEST_DEBUGGER },
{ "com", RTLOGDEST_COM },
{ "user", RTLOGDEST_USER },
};
/* check no prefix. */
bool fNo = false;
{
fNo = true;
pszVar += 2;
}
/* instruction. */
unsigned i;
{
{
if (!fNo)
else
/* check for value. */
while (RT_C_IS_SPACE(*pszVar))
pszVar++;
{
pszVar++;
if (!pszEnd)
/* log file name */
if (i == 0 /* file */ && !fNo)
{
}
/* log directory */
{
char szTmp[RTPATH_MAX];
if (pszFile)
else
}
else
AssertMsgFailed(("Invalid %s_DEST! %s%s doesn't take a value!\n", pszEnvVarBase, fNo ? "no" : "", aDest[i].pszInstr));
}
break;
}
}
/* unknown instruction? */
{
pszVar++;
}
/* skip blanks and delimiters. */
pszVar++;
} /* while more environment variable value left */
}
/*
* The flags.
*/
if (pszVar)
/*
* The group settings.
*/
if (pszVar)
}
#endif /* IN_RING3 */
/*
* Open the destination(s).
*/
rc = VINF_SUCCESS;
#ifdef IN_RING3
{
}
#endif /* IN_RING3 */
/*
* Create mutex and check how much it counts when entering the lock
* so that we can report the values for RTLOGFLAGS_PREFIX_LOCK_COUNTS.
*/
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
#ifdef IN_RING3 /** @todo do counters in ring-0 too? */
if (Thread != NIL_RTTHREAD)
{
c = RTThreadGetWriteLockCount(Thread) - c;
}
#endif
return VINF_SUCCESS;
}
if (pszErrorMsg)
}
#ifdef IN_RING3
#endif
}
else
{
#ifdef RT_OS_LINUX
/*
* RTMemAlloc() succeeded but RTMemExecAlloc() failed -- most probably an SELinux problem.
*/
if (pszErrorMsg)
#endif /* RT_OS_LINUX */
rc = VERR_NO_MEMORY;
}
}
else
rc = VERR_NO_MEMORY;
return rc;
}
/**
* Create a logger instance.
*
* @returns iprt status code.
*
* @param ppLogger Where to store the logger instance.
* @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
* @param pszGroupSettings The initial group settings.
* @param pszEnvVarBase Base name for the environment variables for this instance.
* @param cGroups Number of groups in the array.
* @param papszGroups Pointer to array of groups. This must stick around for the life of the
* logger instance.
* @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
* @param pszFilenameFmt Log filename format string. Standard RTStrFormat().
* @param ... Format arguments.
*/
{
int rc;
rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, NULL, 0, pszFilenameFmt, args);
return rc;
}
/**
* Create a logger instance.
*
* @returns iprt status code.
*
* @param ppLogger Where to store the logger instance.
* @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
* @param pszGroupSettings The initial group settings.
* @param pszEnvVarBase Base name for the environment variables for this instance.
* @param cGroups Number of groups in the array.
* @param papszGroups Pointer to array of groups. This must stick around for the life of the
* logger instance.
* @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.
* @param pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.
* @param cchErrorMsg The size of the error message buffer.
* @param pszFilenameFmt Log filename format string. Standard RTStrFormat().
* @param ... Format arguments.
*/
{
int rc;
rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, pszErrorMsg, cchErrorMsg, pszFilenameFmt, args);
return rc;
}
/**
* Destroys a logger instance.
*
* The instance is flushed and all output destinations closed (where applicable).
*
* @returns iprt status code.
* @param pLogger The logger instance which close destroyed.
*/
{
int rc;
/*
* Validate input.
*/
/*
* Acquire logger instance sem and disable all logging. (paranoia)
*/
if (RT_FAILURE(rc))
return rc;
while (iGroup-- > 0)
/*
* Flush it.
*/
/*
* Close output stuffs.
*/
#ifdef IN_RING3
{
}
#endif
/*
* Free the mutex and the instance memory.
*/
if (MutexSem != NIL_RTSEMFASTMUTEX)
{
}
return rc;
}
/**
* Create a logger instance clone for RC usage.
*
* @returns iprt status code.
*
* @param pLogger The logger instance to be cloned.
* @param pLoggerGC Where to create the GC logger instance.
* @param cbLoggerGC Amount of memory allocated to for the GC logger instance clone.
* @param pfnLoggerGCPtr Pointer to logger wrapper function for this instance (GC Ptr).
* @param pfnFlushGCPtr Pointer to flush function (GC Ptr).
* @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.
*/
{
/*
* Validate input.
*/
if ( !pLoggerGC
|| !pfnFlushGCPtr
|| !pfnLoggerGCPtr)
{
AssertMsgFailed(("Invalid parameters!\n"));
return VERR_INVALID_PARAMETER;
}
if (cbLoggerGC < sizeof(*pLoggerGC))
{
return VERR_INVALID_PARAMETER;
}
/*
* Initialize GC instance.
*/
pLoggerGC->offScratch = 0;
pLoggerGC->fPendingPrefix = false;
/*
* Resolve defaults.
*/
if (!pLogger)
{
if (!pLogger)
return VINF_SUCCESS;
}
/*
* Check if there's enough space for the groups.
*/
{
AssertMsgFailed(("%d req=%d cGroups=%d\n", cbLoggerGC, RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]), pLogger->cGroups));
return VERR_INVALID_PARAMETER;
}
memcpy(&pLoggerGC->afGroups[0], &pLogger->afGroups[0], pLogger->cGroups * sizeof(pLoggerGC->afGroups[0]));
/*
* Copy bits from the HC instance.
*/
/*
* Check if we can remove the disabled flag.
*/
if ( pLogger->fDestFlags
return VINF_SUCCESS;
}
/**
* Flushes a GC logger instance to a HC logger.
*
*
* @returns iprt status code.
* @param pLogger The HC logger instance to flush pLoggerGC to.
* If NULL the default logger is used.
* @param pLoggerGC The GC logger instance to flush.
*/
{
/*
* Resolve defaults.
*/
if (!pLogger)
{
if (!pLogger)
{
pLoggerGC->offScratch = 0;
return;
}
}
/*
* Any thing to flush?
*/
if ( pLogger->offScratch
|| pLoggerGC->offScratch)
{
/*
* Acquire logger instance sem.
*/
if (RT_FAILURE(rc))
return;
/*
* Write whatever the GC instance contains to the HC one, and then
* flush the HC instance.
*/
if (pLoggerGC->offScratch)
{
pLoggerGC->offScratch = 0;
}
/*
* Release the semaphore.
*/
}
}
#ifdef IN_RING3
/**
* Create a logger instance for singled threaded ring-0 usage.
*
* @returns iprt status code.
*
* @param pLogger Where to create the logger instance.
* @param cbLogger The amount of memory available for the logger instance.
* @param pfnLogger Pointer to logger wrapper function for the clone.
* @param pfnFlush Pointer to flush function for the clone.
* @param fFlags Logger instance flags for the clone, a combination of the RTLOGFLAGS_* values.
* @param fDestFlags The destination flags.
*/
RTDECL(int) RTLogCreateForR0(PRTLOGGER pLogger, size_t cbLogger, PFNRTLOGGER pfnLogger, PFNRTLOGFLUSH pfnFlush, RTUINT fFlags, RTUINT fDestFlags)
{
/*
* Validate input.
*/
/*
* Initialize the ring-0 instance.
*/
pLogger->offScratch = 0;
pLogger->fPendingPrefix = false;
pLogger->cMaxGroups = (cbLogger - RT_OFFSETOF(RTLOGGER, afGroups[0])) / sizeof(pLogger->afGroups[0]);
return VINF_SUCCESS;
}
#endif /* IN_RING3 */
/**
* Copies the group settings and flags from logger instance to another.
*
* @returns IPRT status code.
* @param pDstLogger The destination logger instance.
* @param pSrcLogger The source logger instance. If NULL the default one is used.
* @param fFlagsOr OR mask for the flags.
* @param fFlagsAnd AND mask for the flags.
*/
RTDECL(int) RTLogCopyGroupsAndFlags(PRTLOGGER pDstLogger, PCRTLOGGER pSrcLogger, unsigned fFlagsOr, unsigned fFlagsAnd)
{
int rc;
unsigned cGroups;
/*
* Validate input.
*/
/*
* Resolve defaults.
*/
if (!pSrcLogger)
{
if (!pSrcLogger)
{
pDstLogger->afGroups[0] = 0;
return VINF_SUCCESS;
}
}
/*
* Copy flags and group settings.
*/
rc = VINF_SUCCESS;
{
}
memcpy(&pDstLogger->afGroups[0], &pSrcLogger->afGroups[0], cGroups * sizeof(pDstLogger->afGroups[0]));
return rc;
}
/**
* Flushes the buffer in one logger instance onto another logger.
*
* @returns iprt status code.
*
* @param pSrcLogger The logger instance to flush.
* @param pDstLogger The logger instance to flush onto.
* If NULL the default logger will be used.
*/
{
/*
* Resolve defaults.
*/
if (!pDstLogger)
{
if (!pDstLogger)
{
if (pSrcLogger->offScratch)
{
if (RT_SUCCESS(rc))
{
pSrcLogger->offScratch = 0;
}
}
return;
}
}
/*
* Any thing to flush?
*/
if ( pSrcLogger->offScratch
|| pDstLogger->offScratch)
{
/*
* Acquire logger semaphores.
*/
if (RT_FAILURE(rc))
return;
if (RT_SUCCESS(rc))
{
/*
* Write whatever the GC instance contains to the HC one, and then
* flush the HC instance.
*/
if (pSrcLogger->offScratch)
{
pSrcLogger->offScratch = 0;
}
/*
* Release the semaphores.
*/
}
}
}
/**
* Matches a group name with a pattern mask in an case insensitive manner (ASCII).
*
* @returns true if matching and *ppachMask set to the end of the pattern.
* @returns false if no match.
* @param pszGrp The group name.
* @param ppachMask Pointer to the pointer to the mask. Only wildcard supported is '*'.
* @param cchMask The length of the mask, including modifiers. The modifiers is why
* we update *ppachMask on match.
*/
{
const char *pachMask;
return false;
for (;;)
{
{
const char *pszTmp;
/*
* Check for wildcard and do a minimal match if found.
*/
if (*pachMask != '*')
return false;
/* eat '*'s. */
do pachMask++;
/* is there more to match? */
if ( !cchMask
|| *pachMask == '.'
|| *pachMask == '=')
break; /* we're good */
/* do extremely minimal matching (fixme) */
if (!pszTmp)
if (!pszTmp)
return false;
continue;
}
/* done? */
if (!*++pszGrp)
{
/* trailing wildcard is ok. */
do
{
pachMask++;
cchMask--;
if ( !cchMask
|| *pachMask == '.'
|| *pachMask == '=')
break; /* we're good */
return false;
}
if (!--cchMask)
return false;
pachMask++;
}
/* match */
return true;
}
/**
* Updates the group settings for the logger instance using the specified
* specification string.
*
* @returns iprt status code.
* Failures can safely be ignored.
* @param pLogger Logger instance.
* @param pszVar Value to parse.
*/
{
/*
* Resolve defaults.
*/
if (!pLogger)
{
if (!pLogger)
return VINF_SUCCESS;
}
/*
* Iterate the string.
*/
while (*pszVar)
{
/*
* Skip prefixes (blanks, ;, + and -).
*/
bool fEnabled = true;
char ch;
const char *pszStart;
unsigned i;
{
pszVar++;
}
if (!*pszVar)
break;
/*
* Find end.
*/
pszVar++;
/*
* Find the group (ascii case insensitive search).
* Special group 'all'.
*/
if ( cch >= 3
{
/*
* All.
*/
{
if (fEnabled)
else
}
}
else
{
/*
* Specific group(s).
*/
{
{
if (fEnabled)
else
}
} /* for each group */
}
} /* parse specification */
return VINF_SUCCESS;
}
/**
* Interprets the group flags suffix.
*
* @returns Flags specified. (0 is possible!)
* @param psz Start of Suffix. (Either dot or equal sign.)
*/
static unsigned rtlogGroupFlags(const char *psz)
{
unsigned fFlags = 0;
/*
* Litteral flags.
*/
while (*psz == '.')
{
static struct
{
const char *pszFlag; /* lowercase!! */
unsigned fFlag;
} aFlags[] =
{
{ "eo", RTLOGGRPFLAGS_ENABLED },
{ "enabledonly",RTLOGGRPFLAGS_ENABLED },
{ "l1", RTLOGGRPFLAGS_LEVEL_1 },
{ "level1", RTLOGGRPFLAGS_LEVEL_1 },
{ "l", RTLOGGRPFLAGS_LEVEL_2 },
{ "l2", RTLOGGRPFLAGS_LEVEL_2 },
{ "level2", RTLOGGRPFLAGS_LEVEL_2 },
{ "l3", RTLOGGRPFLAGS_LEVEL_3 },
{ "level3", RTLOGGRPFLAGS_LEVEL_3 },
{ "l4", RTLOGGRPFLAGS_LEVEL_4 },
{ "level4", RTLOGGRPFLAGS_LEVEL_4 },
{ "l5", RTLOGGRPFLAGS_LEVEL_5 },
{ "level5", RTLOGGRPFLAGS_LEVEL_5 },
{ "l6", RTLOGGRPFLAGS_LEVEL_6 },
{ "level6", RTLOGGRPFLAGS_LEVEL_6 },
{ "f", RTLOGGRPFLAGS_FLOW },
{ "flow", RTLOGGRPFLAGS_FLOW },
{ "lelik", RTLOGGRPFLAGS_LELIK },
{ "michael", RTLOGGRPFLAGS_MICHAEL },
{ "dmik", RTLOGGRPFLAGS_DMIK },
{ "sunlover", RTLOGGRPFLAGS_SUNLOVER },
{ "achim", RTLOGGRPFLAGS_ACHIM },
{ "achimha", RTLOGGRPFLAGS_ACHIM },
{ "s", RTLOGGRPFLAGS_SANDER },
{ "sander", RTLOGGRPFLAGS_SANDER },
{ "sandervl", RTLOGGRPFLAGS_SANDER },
{ "klaus", RTLOGGRPFLAGS_KLAUS },
{ "frank", RTLOGGRPFLAGS_FRANK },
{ "b", RTLOGGRPFLAGS_BIRD },
{ "bird", RTLOGGRPFLAGS_BIRD },
{ "n", RTLOGGRPFLAGS_NONAME },
{ "noname", RTLOGGRPFLAGS_NONAME }
};
unsigned i;
bool fFound = false;
psz++;
{
{
psz1++;
psz2++;
if (!*psz1)
{
break;
fFound = true;
break;
}
} /* strincmp */
} /* for each flags */
}
/*
* Flag value.
*/
if (*psz == '=')
{
psz++;
if (*psz == '~')
else
}
return fFlags;
}
#endif /* !IN_GC */
/**
* Updates the flags for the logger instance using the specified
* specification string.
*
* @returns iprt status code.
* Failures can safely be ignored.
* @param pLogger Logger instance (NULL for default logger).
* @param pszVar Value to parse.
*/
{
int rc = VINF_SUCCESS;
/*
* Resolve defaults.
*/
if (!pLogger)
{
if (!pLogger)
return VINF_SUCCESS;
}
/*
* Iterate the string.
*/
while (*pszVar)
{
/* parse instruction. */
static struct
{
const char *pszInstr;
unsigned fFlag;
bool fInverted;
} const aDest[] =
{
};
/* check no prefix. */
bool fNo = false;
char ch;
unsigned i;
/* skip blanks. */
while (RT_C_IS_SPACE(*pszVar))
pszVar++;
if (!*pszVar)
return rc;
{
{
pszVar += 2;
}
else if (ch == '+')
{
pszVar++;
fNo = true;
}
{
pszVar++;
}
else
break;
}
/* instruction. */
{
{
else
break;
}
}
/* unknown instruction? */
{
pszVar++;
}
/* skip blanks and delimiters. */
pszVar++;
} /* while more environment variable value left */
return rc;
}
/**
* Flushes the specified logger.
*
* @param pLogger The logger instance to flush.
* If NULL the default instance is used. The default instance
* will not be initialized by this call.
*/
{
/*
* Resolve defaults.
*/
if (!pLogger)
{
#ifdef IN_GC
#else
#endif
if (!pLogger)
return;
}
/*
* Any thing to flush?
*/
if (pLogger->offScratch)
{
#ifndef IN_GC
/*
* Acquire logger instance sem.
*/
if (RT_FAILURE(rc))
return;
#endif
/*
* Call worker.
*/
#ifndef IN_GC
/*
* Release the semaphore.
*/
#endif
}
}
/**
* Gets the default logger instance.
*
* @returns Pointer to default logger instance.
* @returns NULL if no default logger instance available.
*/
{
#ifdef IN_GC
return &g_Logger;
#else /* !IN_GC */
# ifdef IN_RING0
/*
* Check per thread loggers first.
*/
if (g_cPerThreadLoggers)
{
while (i-- > 0)
return g_aPerThreadLoggers[i].pLogger;
}
# endif /* IN_RING0 */
/*
* If no per thread logger, use the default one.
*/
if (!g_pLogger)
return g_pLogger;
#endif /* !IN_GC */
}
#ifndef IN_GC
/**
* Sets the default logger instance.
*
* @returns iprt status code.
* @param pLogger The new default logger instance.
*/
{
}
#endif /* !IN_GC */
#ifdef IN_RING0
/**
* Changes the default logger instance for the current thread.
*
* @returns IPRT status code.
* @param pLogger The logger instance. Pass NULL for deregistration.
* @param uKey Associated key for cleanup purposes. If pLogger is NULL,
* all instances with this key will be deregistered. So in
* order to only deregister the instance associated with the
* current thread use 0.
*/
{
int rc;
if (pLogger)
{
int32_t i;
unsigned j;
/*
* Iterate the table to see if there is already an entry for this thread.
*/
i = ELEMENTS(g_aPerThreadLoggers);
while (i-- > 0)
{
return VINF_SUCCESS;
}
/*
* Allocate a new table entry.
*/
{
return VERR_BUFFER_OVERFLOW; /* horrible error code! */
}
for (j = 0; j < 10; j++)
{
i = ELEMENTS(g_aPerThreadLoggers);
while (i-- > 0)
{
AssertCompile(sizeof(RTNATIVETHREAD) == sizeof(void*));
&& ASMAtomicCmpXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)Self, (void *)NIL_RTNATIVETHREAD))
{
return VINF_SUCCESS;
}
}
}
}
else
{
/*
* Search the array for the current thread.
*/
while (i-- > 0)
{
ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)NIL_RTNATIVETHREAD);
}
rc = VINF_SUCCESS;
}
return rc;
}
#endif
/**
* Gets the default release logger instance.
*
* @returns Pointer to default release logger instance.
* @returns NULL if no default release logger instance available.
*/
{
#ifdef IN_GC
return &g_RelLogger;
#else /* !IN_GC */
return g_pRelLogger;
#endif /* !IN_GC */
}
#ifndef IN_GC
/**
* Sets the default logger instance.
*
* @returns iprt status code.
* @param pLogger The new default release logger instance.
*/
{
}
#endif /* !IN_GC */
/**
* Write to a logger instance.
*
* @param pLogger Pointer to logger instance.
* @param pvCallerRet Ignored.
* @param pszFormat Format string.
* @param ... Format arguments.
*/
{
/* manually align the stack before doing the call.
* We boldly assume that there is a stack frame here! */
#else
#endif
}
/**
* Write to a logger instance.
*
* @param pLogger Pointer to logger instance.
* @param pszFormat Format string.
* @param args Format arguments.
*/
{
}
/**
* Write to a logger instance.
*
* This function will check whether the instance, group and flags makes up a
* logging kind which is currently enabled before writing anything to the log.
*
* @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.
* @param fFlags The logging flags.
* @param iGroup The group.
* The value ~0U is reserved for compatability with RTLogLogger[V] and is
* only for internal usage!
* @param pszFormat Format string.
* @param ... Format arguments.
* @remark This is a worker function of LogIt.
*/
RTDECL(void) RTLogLoggerEx(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
{
}
/**
* Write to a logger instance.
*
* This function will check whether the instance, group and flags makes up a
* logging kind which is currently enabled before writing anything to the log.
*
* @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.
* @param fFlags The logging flags.
* @param iGroup The group.
* The value ~0U is reserved for compatability with RTLogLogger[V] and is
* only for internal usage!
* @param pszFormat Format string.
* @param args Format arguments.
*/
RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
{
/*
* A NULL logger means default instance.
*/
if (!pLogger)
{
if (!pLogger)
return;
}
}
/**
* Write to a logger instance, defaulting to the release one.
*
* This function will check whether the instance, group and flags makes up a
* logging kind which is currently enabled before writing anything to the log.
*
* @param pLogger Pointer to logger instance.
* @param fFlags The logging flags.
* @param iGroup The group.
* The value ~0U is reserved for compatability with RTLogLogger[V] and is
* only for internal usage!
* @param pszFormat Format string.
* @param ... Format arguments.
* @remark This is a worker function for LogRelIt.
*/
RTDECL(void) RTLogRelLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)
{
}
/**
* Write to a logger instance, defaulting to the release one.
*
* This function will check whether the instance, group and flags makes up a
* logging kind which is currently enabled before writing anything to the log.
*
* @param pLogger Pointer to logger instance. If NULL the default release instance is attempted.
* @param fFlags The logging flags.
* @param iGroup The group.
* The value ~0U is reserved for compatability with RTLogLogger[V] and is
* only for internal usage!
* @param pszFormat Format string.
* @param args Format arguments.
*/
RTDECL(void) RTLogRelLoggerV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
{
/*
* A NULL logger means default instance.
*/
if (!pLogger)
{
if (!pLogger)
return;
}
}
/**
* Worker for the RTLog[Rel]Logger*() functions.
*
* @param pLogger Pointer to logger instance.
* @param fFlags The logging flags.
* @param iGroup The group.
* The value ~0U is reserved for compatability with RTLogLogger[V] and is
* only for internal usage!
* @param pszFormat Format string.
* @param args Format arguments.
*/
static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)
{
int rc;
/*
* Validate and correct iGroup.
*/
iGroup = 0;
/*
* If no output, then just skip it.
*/
#ifndef IN_GC
|| !pLogger->fDestFlags
#endif
return;
if ( iGroup != ~0U
&& (pLogger->afGroups[iGroup] & (fFlags | RTLOGGRPFLAGS_ENABLED)) != (fFlags | RTLOGGRPFLAGS_ENABLED))
return;
/*
* Acquire logger instance sem.
*/
if (RT_FAILURE(rc))
return;
/*
* Format the message and perhaps flush it.
*/
{
}
else
&& pLogger->offScratch)
/*
* Release the semaphore.
*/
}
/**
* printf like function for writing to the default log.
*
* @param pszFormat Printf like format string.
* @param ... Optional arguments as specified in pszFormat.
*
* @remark The API doesn't support formatting of floating point numbers at the moment.
*/
{
}
/**
* vprintf like function for writing to the default log.
*
* @param pszFormat Printf like format string.
* @param args Optional arguments as specified in pszFormat.
*
* @remark The API doesn't support formatting of floating point numbers at the moment.
*/
{
}
/**
* printf like function for writing to the default release log.
*
* @param pszFormat Printf like format string.
* @param ... Optional arguments as specified in pszFormat.
*
* @remark The API doesn't support formatting of floating point numbers at the moment.
*/
{
}
/**
* vprintf like function for writing to the default release log.
*
* @param pszFormat Printf like format string.
* @param args Optional arguments as specified in pszFormat.
*
* @remark The API doesn't support formatting of floating point numbers at the moment.
*/
{
}
/**
* Writes the buffer to the given log device without checking for buffered
* data or anything.
* Used by the RTLogFlush() function.
*
* @param pLogger The logger instance to write to. NULL is not allowed!
*/
{
#ifndef IN_GC
# ifdef IN_RING3
# endif
# endif
#endif /* !IN_GC */
/* empty the buffer. */
pLogger->offScratch = 0;
}
/**
* Callback for RTLogFormatV which writes to the com port.
* See PFNLOGOUTPUT() for details.
*/
{
if (cbChars)
{
for (;;)
{
/* sanity */
{
}
#endif
/* how much */
/* copy */
/* advance */
/* done? */
if (cbChars <= 0)
return cbRet;
/* flush */
}
/* won't ever get here! */
}
else
{
/*
* Termination call.
* There's always space for a terminator, and it's not counted.
*/
return 0;
}
}
/**
* Callback for RTLogFormatV which writes to the logger instance.
* This version supports prefixes.
*
* See PFNLOGOUTPUT() for details.
*/
{
if (cbChars)
{
for (;;)
{
char *psz;
const char *pszNewLine;
/*
* Pending prefix?
*/
if (pLogger->fPendingPrefix)
{
pLogger->fPendingPrefix = false;
/* sanity */
{
}
#endif
/*
* Flush the buffer if there isn't enough room for the maximum prefix config.
* Max is 224, add a couple of extra bytes.
*/
{
}
/*
* Write the prefixes.
* psz is pointing to the current position.
*/
{
#else
#endif
int iBase = 16;
unsigned int fFlags = RTSTR_F_ZEROPAD;
{
iBase = 10;
fFlags = 0;
}
{
static volatile uint64_t s_u64LastTs;
s_u64LastTs = u64;
/* We could have been preempted just before reading of s_u64LastTs by
* another thread which wrote s_u64LastTs. In that case the difference
* is negative which we simply ignore. */
}
/* 1E15 nanoseconds = 11 days */
*psz++ = ' ';
}
{
int iBase = 16;
unsigned int fFlags = RTSTR_F_ZEROPAD;
{
iBase = 10;
fFlags = 0;
}
{
static volatile uint64_t s_u64LastTsc;
s_u64LastTsc = u64;
/* We could have been preempted just before reading of s_u64LastTsc by
* another thread which wrote s_u64LastTsc. In that case the difference
* is negative which we simply ignore. */
}
/* 1E15 ticks at 4GHz = 69 hours */
*psz++ = ' ';
}
{
#else
#endif
/* 1E8 milliseconds = 27 hours */
*psz++ = ' ';
}
{
#ifdef IN_RING3
*psz++ = ':';
*psz++ = ':';
*psz++ = '.';
#else
psz += 13;
#endif
}
{
#ifdef IN_RING3
*psz++ = ':';
*psz++ = ':';
*psz++ = '.';
#else
psz += 13;
#endif
}
# if 0
{
char szDate[32];
}
# endif
{
#ifndef IN_GC
#else
#endif
}
{
#ifndef IN_GC
#else
#endif
}
{
#ifdef IN_RING3
const char *pszName = RTThreadSelfName();
const char *pszName = "EMT-GC";
#else
const char *pszName = "EMT-R0";
#endif
if (pszName)
{
}
do
*psz++ = ' ';
}
{
#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
#else
#endif
}
{
#ifdef IN_RING3 /** @todo implement these counters in ring-0 too? */
if (Thread != NIL_RTTHREAD)
{
*psz++ = '/';
}
else
#endif
{
*psz++ = '?';
*psz++ = '/';
*psz++ = '?';
}
}
{
}
{
#ifdef IN_RING3
#else
#endif
if (pszGroup)
{
}
do
*psz++ = ' ';
}
{
{
*psz++ = ' ';
}
else
{
} /* +9 */
}
{
const char *pszGroup;
{
/* personal groups */
}
if (pszGroup)
{
}
do
*psz++ = ' ';
}
/*
* Done, figure what we've used and advance the buffer and free size.
*/
}
else if (cb <= 0)
{
}
/* sanity */
{
}
#endif
/* how much */
/* have newline? */
if (pszNewLine)
{
else
{
pLogger->fPendingPrefix = true;
}
}
/* copy */
/* advance */
if ( pszNewLine
{
cbRet++;
cbChars--;
cb++;
pLogger->fPendingPrefix = true;
}
/* done? */
if (cbChars <= 0)
return cbRet;
}
/* won't ever get here! */
}
else
{
/*
* Termination call.
* There's always space for a terminator, and it's not counted.
*/
return 0;
}
}