
/*******************************************************************************
*   Header Files                                                               *
*******************************************************************************/
#include "internal/iprt.h"
#include <iprt/trace.h>


#include <iprt/assert.h>
#include <iprt/asm.h>
#include <iprt/err.h>
#include <iprt/log.h>
#ifndef IN_RC
# include <iprt/mem.h>
#endif
#if defined(IN_RING0) || (!defined(RT_ARCH_AMD64) && !defined(RT_ARCH_X86))
# include <iprt/mp.h>
#else
# include <iprt/asm-amd64-x86.h>
#endif
#include <iprt/path.h>
#include <iprt/string.h>
#include <iprt/time.h>

#include "internal/magics.h"


/*******************************************************************************
*   Structures and Typedefs                                                    *
*******************************************************************************/
/** Alignment used to place the trace buffer members, this should be a multiple
 * of the cache line size if possible.  (We should dynamically determine it.) */
#define RTTRACEBUF_ALIGNMENT        64
AssertCompile(RTTRACEBUF_ALIGNMENT >= sizeof(uint64_t) * 2);

/** The maximum number of entries. */
#define RTTRACEBUF_MAX_ENTRIES      _64K
/** The minimum number of entries. */
#define RTTRACEBUF_MIN_ENTRIES      4
/** The default number of entries. */
#define RTTRACEBUF_DEF_ENTRIES      256

/** The maximum entry size. */
#define RTTRACEBUF_MAX_ENTRY_SIZE   _1M
/** The minimum entry size. */
#define RTTRACEBUF_MIN_ENTRY_SIZE   RTTRACEBUF_ALIGNMENT
/** The default entry size. */
#define RTTRACEBUF_DEF_ENTRY_SIZE   256
AssertCompile(!(RTTRACEBUF_DEF_ENTRY_SIZE & (RTTRACEBUF_DEF_ENTRY_SIZE - 1)));

/**
 * The volatile trace buffer members.
 */
typedef struct RTTRACEBUFVOLATILE
{
    /** Reference counter. */
    uint32_t volatile   cRefs;
    /** The next entry to make use of. */
    uint32_t volatile   iEntry;
} RTTRACEBUFVOLATILE;
/** Pointer to the volatile parts of a trace buffer. */
typedef RTTRACEBUFVOLATILE *PRTTRACEBUFVOLATILE;


/**
 * Trace buffer entry.
 */
typedef struct RTTRACEBUFENTRY
{
    /** The nano second entry time stamp. */
    uint64_t            NanoTS;
    /** The ID of the CPU the event was recorded.  */
    RTCPUID             idCpu;
    /** The message. */
    char                szMsg[RTTRACEBUF_ALIGNMENT - sizeof(uint64_t) - sizeof(RTCPUID)];
} RTTRACEBUFENTRY;
AssertCompile(sizeof(RTTRACEBUFENTRY) <= RTTRACEBUF_ALIGNMENT);
/** Pointer to a trace buffer entry. */
typedef RTTRACEBUFENTRY *PRTTRACEBUFENTRY;



/**
 * Trace buffer structure.
 *
 * @remarks     This structure must be context agnostic, i.e. no pointers or
 *              other types that may differ between contexts (R3/R0/RC).
 */
typedef struct RTTRACEBUFINT
{
    /** Magic value (RTTRACEBUF_MAGIC). */
    uint32_t            u32Magic;
    /** The entry size. */
    uint32_t            cbEntry;
    /** The number of entries. */
    uint32_t            cEntries;
    /** Flags (always zero for now).  */
    uint32_t            fFlags;
    /** The offset to the volatile members (RTTRACEBUFVOLATILE) (relative to
     *  the start of this structure). */
    uint32_t            offVolatile;
    /** The offset to the entries (relative to the start of this structure). */
    uint32_t            offEntries;
    /** Reserved entries. */
    uint32_t            au32Reserved[2];
} RTTRACEBUFINT;
/** Pointer to a const trace buffer. */
typedef RTTRACEBUFINT const *PCRTTRACEBUFINT;


/*******************************************************************************
*   Defined Constants And Macros                                               *
*******************************************************************************/
/**
 * Get the current CPU Id.
 */
#if defined(IN_RING0) || (!defined(RT_ARCH_AMD64) && !defined(RT_ARCH_X86))
# define RTTRACEBUF_CUR_CPU()   RTMpCpuId()
#else
# define RTTRACEBUF_CUR_CPU()   ASMGetApicId()
#endif

/** Calculates the address of the volatile trace buffer members. */
#define RTTRACEBUF_TO_VOLATILE(a_pThis)     ((PRTTRACEBUFVOLATILE)((uint8_t *)(a_pThis) + (a_pThis)->offVolatile))

/** Calculates the address of a trace buffer entry. */
#define RTTRACEBUF_TO_ENTRY(a_pThis, a_iEntry) \
    ((PRTTRACEBUFENTRY)( (uint8_t *)(a_pThis) + (a_pThis)->offEntries + (a_iEntry) * (a_pThis)->cbEntry ))

/** Validates a trace buffer handle and returns rc if not valid. */
#define RTTRACEBUF_VALID_RETURN_RC(a_pThis, a_rc) \
    do { \
        AssertPtrReturn((a_pThis), (a_rc)); \
        AssertReturn((a_pThis)->u32Magic == RTTRACEBUF_MAGIC, (a_rc)); \
        AssertReturn((a_pThis)->offVolatile < RTTRACEBUF_ALIGNMENT * 2, (a_rc)); \
        AssertReturn(RTTRACEBUF_TO_VOLATILE(a_pThis)->cRefs > 0, (a_rc)); \
    } while (0)

/**
 * Resolves and validates a trace buffer handle and returns rc if not valid.
 *
 * @param   a_hTraceBuf     The trace buffer handle passed by the user.
 * @param   a_pThis         Where to store the trace buffer pointer.
 */
#define RTTRACEBUF_RESOLVE_VALIDATE_RETAIN_RETURN(a_hTraceBuf, a_pThis) \
    do { \
        uint32_t cRefs; \
        if ((a_hTraceBuf) == RTTRACEBUF_DEFAULT) \
        { \
            (a_pThis) = RTTraceGetDefaultBuf(); \
            if (!RT_VALID_PTR(a_pThis)) \
                return VERR_NOT_FOUND; \
        } \
        else \
        { \
            (a_pThis) = (a_hTraceBuf); \
            AssertPtrReturn((a_pThis), VERR_INVALID_HANDLE); \
        } \
        AssertReturn((a_pThis)->u32Magic == RTTRACEBUF_MAGIC, VERR_INVALID_HANDLE); \
        AssertReturn((a_pThis)->offVolatile < RTTRACEBUF_ALIGNMENT * 2, VERR_INVALID_HANDLE); \
        \
        cRefs = ASMAtomicIncU32(&RTTRACEBUF_TO_VOLATILE(a_pThis)->cRefs); \
        if (RT_UNLIKELY(cRefs < 1 || cRefs >= _1M)) \
        { \
            ASMAtomicDecU32(&RTTRACEBUF_TO_VOLATILE(a_pThis)->cRefs); \
            AssertFailedReturn(VERR_INVALID_HANDLE); \
        } \
    } while (0)


/**
 * Drops a trace buffer reference.
 *
 * @param   a_pThis     Pointer to the trace buffer.
 */
#define RTTRACEBUF_DROP_REFERENCE(a_pThis) \
    do { \
        uint32_t cRefs = ASMAtomicDecU32(&RTTRACEBUF_TO_VOLATILE(a_pThis)->cRefs); \
        if (!cRefs) \
            rtTraceBufDestroy((RTTRACEBUFINT *)a_pThis); \
    } while (0)


/**
 * The prologue code for a RTTraceAddSomething function.
 *
 * Resolves a trace buffer handle, grabs a reference to it and allocates the
 * next entry.  Return with an appropriate error status on failure.
 *
 * @param   a_hTraceBuf     The trace buffer handle passed by the user.
 *
 * @remarks This is kind of ugly, sorry.
 */
#define RTTRACEBUF_ADD_PROLOGUE(a_hTraceBuf) \
    int                 rc; \
    uint32_t            cRefs; \
    uint32_t            iEntry; \
    PCRTTRACEBUFINT     pThis; \
    PRTTRACEBUFVOLATILE pVolatile; \
    PRTTRACEBUFENTRY    pEntry; \
    char               *pszBuf; \
    size_t              cchBuf; \
    \
    /* Resolve and validate the handle. */ \
    if ((a_hTraceBuf) == RTTRACEBUF_DEFAULT) \
    { \
        pThis = RTTraceGetDefaultBuf(); \
        if (!RT_VALID_PTR(pThis)) \
            return VERR_NOT_FOUND; \
    } \
    else if ((a_hTraceBuf) != NIL_RTTRACEBUF) \
    { \
        pThis = (a_hTraceBuf); \
        AssertPtrReturn(pThis, VERR_INVALID_HANDLE); \
    } \
    else \
        return VERR_INVALID_HANDLE; \
    \
    AssertReturn(pThis->u32Magic == RTTRACEBUF_MAGIC, VERR_INVALID_HANDLE); \
    if (pThis->fFlags & RTTRACEBUF_FLAGS_DISABLED) \
        return VINF_SUCCESS; \
    AssertReturn(pThis->offVolatile < RTTRACEBUF_ALIGNMENT * 2, VERR_INVALID_HANDLE); \
    pVolatile = RTTRACEBUF_TO_VOLATILE(pThis); \
    \
    /* Grab a reference. */ \
    cRefs = ASMAtomicIncU32(&pVolatile->cRefs); \
    if (RT_UNLIKELY(cRefs < 1 || cRefs >= _1M)) \
    { \
        ASMAtomicDecU32(&pVolatile->cRefs); \
        AssertFailedReturn(VERR_INVALID_HANDLE); \
    } \
    \
    /* Grab the next entry and set the time stamp. */ \
    iEntry  = ASMAtomicIncU32(&pVolatile->iEntry) - 1; \
    iEntry %= pThis->cEntries; \
    pEntry  = RTTRACEBUF_TO_ENTRY(pThis, iEntry); \
    pEntry->NanoTS = RTTimeNanoTS(); \
    pEntry->idCpu  = RTTRACEBUF_CUR_CPU(); \
    pszBuf  = &pEntry->szMsg[0]; \
    *pszBuf = '\0'; \
    cchBuf  = pThis->cbEntry - RT_OFFSETOF(RTTRACEBUFENTRY, szMsg) - 1; \
    rc      = VINF_SUCCESS


/**
 * Used by a RTTraceAddPosSomething to store the source position in the entry
 * prior to adding the actual trace message text.
 *
 * Both pszBuf and cchBuf will be adjusted such that pszBuf points and the zero
 * terminator after the source position part.
 */
#define RTTRACEBUF_ADD_STORE_SRC_POS() \
    do { \
        /* file(line): - no path */ \
        size_t cchPos = RTStrPrintf(pszBuf, cchBuf, "%s(%d): ", RTPathFilename(pszFile), iLine); \
        pszBuf += cchPos; \
        cchBuf -= cchPos; \
        NOREF(pszFunction); \
    } while (0)


/**
 * The epilogue code for a RTTraceAddSomething function.
 *
 * This will release the trace buffer reference.
 */
#define RTTRACEBUF_ADD_EPILOGUE() \
    cRefs = ASMAtomicDecU32(&pVolatile->cRefs); \
    if (!cRefs) \
        rtTraceBufDestroy((RTTRACEBUFINT *)pThis); \
    return rc


#ifndef IN_RC /* Drop this in RC context (too lazy to split the file). */

RTDECL(int) RTTraceBufCreate(PRTTRACEBUF phTraceBuf, uint32_t cEntries, uint32_t cbEntry, uint32_t fFlags)
{
    AssertPtrReturn(phTraceBuf, VERR_INVALID_POINTER);
    AssertReturn(!(fFlags & ~(RTTRACEBUF_FLAGS_MASK & ~ RTTRACEBUF_FLAGS_FREE_ME)), VERR_INVALID_PARAMETER);
    AssertMsgReturn(cbEntry  <= RTTRACEBUF_MAX_ENTRIES,    ("%#x\n", cbEntry),  VERR_OUT_OF_RANGE);
    AssertMsgReturn(cEntries <= RTTRACEBUF_MAX_ENTRY_SIZE, ("%#x\n", cEntries), VERR_OUT_OF_RANGE);

    /*
     * Apply default and alignment adjustments.
     */
    if (!cbEntry)
        cbEntry = RTTRACEBUF_DEF_ENTRY_SIZE;
    else
        cbEntry = RT_ALIGN_32(cbEntry, RTTRACEBUF_ALIGNMENT);

    if (!cEntries)
        cEntries = RTTRACEBUF_DEF_ENTRIES;
    else if (cEntries < RTTRACEBUF_MIN_ENTRIES)
        cEntries = RTTRACEBUF_MIN_ENTRIES;

    /*
     * Calculate the required buffer size, allocte it and hand it on to the
     * carver API.
     */
    size_t  cbBlock = cbEntry * cEntries
                    + RT_ALIGN_Z(sizeof(RTTRACEBUFINT),      RTTRACEBUF_ALIGNMENT)
                    + RT_ALIGN_Z(sizeof(RTTRACEBUFVOLATILE), RTTRACEBUF_ALIGNMENT);
    void   *pvBlock = RTMemAlloc(cbBlock);
    if (!((uintptr_t)pvBlock & (RTTRACEBUF_ALIGNMENT - 1)))
    {
        RTMemFree(pvBlock);
        cbBlock += RTTRACEBUF_ALIGNMENT - 1;
        pvBlock = RTMemAlloc(cbBlock);
    }
    int rc;
    if (pvBlock)
    {
        rc = RTTraceBufCarve(phTraceBuf, cEntries, cbEntry, fFlags, pvBlock, &cbBlock);
        if (RT_FAILURE(rc))
            RTMemFree(pvBlock);
    }
    else
        rc = VERR_NO_MEMORY;
    return rc;
}


RTDECL(int) RTTraceBufCarve(PRTTRACEBUF phTraceBuf, uint32_t cEntries, uint32_t cbEntry, uint32_t fFlags,
                            void *pvBlock, size_t *pcbBlock)
{
    AssertPtrReturn(phTraceBuf, VERR_INVALID_POINTER);
    AssertReturn(!(fFlags & ~RTTRACEBUF_FLAGS_MASK), VERR_INVALID_PARAMETER);
    AssertMsgReturn(cbEntry  <= RTTRACEBUF_MAX_ENTRIES,    ("%#x\n", cbEntry),  VERR_OUT_OF_RANGE);
    AssertMsgReturn(cEntries <= RTTRACEBUF_MAX_ENTRY_SIZE, ("%#x\n", cEntries), VERR_OUT_OF_RANGE);
    AssertPtrReturn(pcbBlock, VERR_INVALID_POINTER);
    size_t const cbBlock = *pcbBlock;
    AssertReturn(RT_VALID_PTR(pvBlock) || !cbBlock, VERR_INVALID_POINTER);

    /*
     * Apply defaults, align sizes and check against available buffer space.
     * This code can be made a bit more clever, if someone feels like it.
     */
    size_t const cbHdr      = RT_ALIGN_Z(sizeof(RTTRACEBUFINT),      RTTRACEBUF_ALIGNMENT)
                            + RT_ALIGN_Z(sizeof(RTTRACEBUFVOLATILE), RTTRACEBUF_ALIGNMENT);
    size_t const cbEntryBuf = cbBlock > cbHdr ? cbBlock - cbHdr : 0;
    if (cbEntry)
        cbEntry = RT_ALIGN_32(cbEntry, RTTRACEBUF_ALIGNMENT);
    else
    {
        if (!cbEntryBuf)
        {
            cbEntry  = RTTRACEBUF_DEF_ENTRY_SIZE;
            cEntries = RTTRACEBUF_DEF_ENTRIES;
        }
        else if (cEntries)
        {
            size_t cbEntryZ = cbBlock / cEntries;
            cbEntryZ &= ~(RTTRACEBUF_ALIGNMENT - 1);
            if (cbEntryZ > RTTRACEBUF_MAX_ENTRIES)
                cbEntryZ = RTTRACEBUF_MAX_ENTRIES;
            cbEntry = (uint32_t)cbEntryZ;
        }
        else if (cbBlock >= RT_ALIGN_32(512, RTTRACEBUF_ALIGNMENT) * 256)
            cbEntry = RT_ALIGN_32(512, RTTRACEBUF_ALIGNMENT);
        else if (cbBlock >= RT_ALIGN_32(256, RTTRACEBUF_ALIGNMENT) * 64)
            cbEntry = RT_ALIGN_32(256, RTTRACEBUF_ALIGNMENT);
        else if (cbBlock >= RT_ALIGN_32(128, RTTRACEBUF_ALIGNMENT) * 32)
            cbEntry = RT_ALIGN_32(128, RTTRACEBUF_ALIGNMENT);
        else
            cbEntry = sizeof(RTTRACEBUFENTRY);
    }
    Assert(RT_ALIGN_32(cbEntry, RTTRACEBUF_ALIGNMENT) == cbEntry);

    if (!cEntries)
    {
        size_t cEntriesZ = cbEntryBuf / cbEntry;
        if (cEntriesZ > RTTRACEBUF_MAX_ENTRIES)
            cEntriesZ = RTTRACEBUF_MAX_ENTRIES;
        cEntries = (uint32_t)cEntriesZ;
    }
    if (cEntries < RTTRACEBUF_MIN_ENTRIES)
        cEntries = RTTRACEBUF_MIN_ENTRIES;

    uint32_t offVolatile = RTTRACEBUF_ALIGNMENT - ((uintptr_t)pvBlock & (RTTRACEBUF_ALIGNMENT - 1));
    if (offVolatile < sizeof(RTTRACEBUFINT))
        offVolatile += RTTRACEBUF_ALIGNMENT;
    size_t cbReqBlock = offVolatile
                      + RT_ALIGN_Z(sizeof(RTTRACEBUFVOLATILE), RTTRACEBUF_ALIGNMENT)
                      + cbEntry * cEntries;
    if (*pcbBlock < cbReqBlock)
    {
        *pcbBlock = cbReqBlock;
        return VERR_BUFFER_OVERFLOW;
    }

    /*
     * Do the carving.
     */
    memset(pvBlock, 0, cbBlock);

    RTTRACEBUFINT *pThis = (RTTRACEBUFINT *)pvBlock;
    pThis->u32Magic         = RTTRACEBUF_MAGIC;
    pThis->cbEntry          = cbEntry;
    pThis->cEntries         = cEntries;
    pThis->fFlags           = fFlags;
    pThis->offVolatile      = offVolatile;
    pThis->offEntries       = offVolatile + RT_ALIGN_Z(sizeof(RTTRACEBUFVOLATILE), RTTRACEBUF_ALIGNMENT);

    PRTTRACEBUFVOLATILE pVolatile = (PRTTRACEBUFVOLATILE)((uint8_t *)pThis + offVolatile);
    pVolatile->cRefs        = 1;
    pVolatile->iEntry       = 0;

    *pcbBlock   = cbBlock - cbReqBlock;
    *phTraceBuf = pThis;
    return VINF_SUCCESS;
}

#endif /* !IN_RC */


/**
 * Destructor.
 *
 * @param   pThis               The trace buffer to destroy.
 */
static void rtTraceBufDestroy(RTTRACEBUFINT *pThis)
{
    AssertReturnVoid(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTTRACEBUF_MAGIC_DEAD, RTTRACEBUF_MAGIC));
    if (pThis->fFlags & RTTRACEBUF_FLAGS_FREE_ME)
    {
#ifdef IN_RC
        AssertReleaseFailed();
#else
        RTMemFree(pThis);
#endif
    }
}


RTDECL(uint32_t) RTTraceBufRetain(RTTRACEBUF hTraceBuf)
{
    PCRTTRACEBUFINT pThis = hTraceBuf;
    RTTRACEBUF_VALID_RETURN_RC(pThis, UINT32_MAX);
    return ASMAtomicIncU32(&RTTRACEBUF_TO_VOLATILE(pThis)->cRefs);
}


RTDECL(uint32_t) RTTraceBufRelease(RTTRACEBUF hTraceBuf)
{
    if (hTraceBuf == NIL_RTTRACEBUF)
        return 0;

    PCRTTRACEBUFINT pThis = hTraceBuf;
    RTTRACEBUF_VALID_RETURN_RC(pThis, UINT32_MAX);

    uint32_t cRefs = ASMAtomicDecU32(&RTTRACEBUF_TO_VOLATILE(pThis)->cRefs);
    if (!cRefs)
        rtTraceBufDestroy((RTTRACEBUFINT *)pThis);
    return cRefs;
}


RTDECL(int) RTTraceBufAddMsg(RTTRACEBUF hTraceBuf, const char *pszMsg)
{
    RTTRACEBUF_ADD_PROLOGUE(hTraceBuf);
    RTStrCopy(pszBuf, cchBuf, pszMsg);
    RTTRACEBUF_ADD_EPILOGUE();
}


RTDECL(int) RTTraceBufAddMsgEx(  RTTRACEBUF hTraceBuf, const char *pszMsg, size_t cbMaxMsg)
{
    RTTRACEBUF_ADD_PROLOGUE(hTraceBuf);
    RTStrCopyEx(pszBuf, cchBuf, pszMsg, cbMaxMsg);
    RTTRACEBUF_ADD_EPILOGUE();
}


RTDECL(int) RTTraceBufAddMsgF(RTTRACEBUF hTraceBuf, const char *pszMsgFmt, ...)
{
    int         rc;
    va_list     va;
    va_start(va, pszMsgFmt);
    rc = RTTraceBufAddMsgV(hTraceBuf, pszMsgFmt, va);
    va_end(va);
    return rc;
}


RTDECL(int) RTTraceBufAddMsgV(RTTRACEBUF hTraceBuf, const char *pszMsgFmt, va_list va)
{
    RTTRACEBUF_ADD_PROLOGUE(hTraceBuf);
    RTStrPrintfV(pszBuf, cchBuf, pszMsgFmt, va);
    RTTRACEBUF_ADD_EPILOGUE();
}


RTDECL(int) RTTraceBufAddPos(RTTRACEBUF hTraceBuf, RT_SRC_POS_DECL)
{
    RTTRACEBUF_ADD_PROLOGUE(hTraceBuf);
    RTTRACEBUF_ADD_STORE_SRC_POS();
    RTTRACEBUF_ADD_EPILOGUE();
}


RTDECL(int) RTTraceBufAddPosMsg(RTTRACEBUF hTraceBuf, RT_SRC_POS_DECL, const char *pszMsg)
{
    RTTRACEBUF_ADD_PROLOGUE(hTraceBuf);
    RTTRACEBUF_ADD_STORE_SRC_POS();
    RTStrCopy(pszBuf, cchBuf, pszMsg);
    RTTRACEBUF_ADD_EPILOGUE();
}


RTDECL(int) RTTraceBufAddPosMsgEx(RTTRACEBUF hTraceBuf, RT_SRC_POS_DECL, const char *pszMsg, size_t cbMaxMsg)
{
    RTTRACEBUF_ADD_PROLOGUE(hTraceBuf);
    RTTRACEBUF_ADD_STORE_SRC_POS();
    RTStrCopyEx(pszBuf, cchBuf, pszMsg, cbMaxMsg);
    RTTRACEBUF_ADD_EPILOGUE();
}


RTDECL(int) RTTraceBufAddPosMsgF(RTTRACEBUF hTraceBuf, RT_SRC_POS_DECL, const char *pszMsgFmt, ...)
{
    int         rc;
    va_list     va;
    va_start(va, pszMsgFmt);
    rc = RTTraceBufAddPosMsgV(hTraceBuf, RT_SRC_POS_ARGS, pszMsgFmt, va);
    va_end(va);
    return rc;
}


RTDECL(int) RTTraceBufAddPosMsgV(RTTRACEBUF hTraceBuf, RT_SRC_POS_DECL, const char *pszMsgFmt, va_list va)
{
    RTTRACEBUF_ADD_PROLOGUE(hTraceBuf);
    RTTRACEBUF_ADD_STORE_SRC_POS();
    RTStrPrintfV(pszBuf, cchBuf, pszMsgFmt, va);
    RTTRACEBUF_ADD_EPILOGUE();
}


RTDECL(int) RTTraceBufEnumEntries(RTTRACEBUF hTraceBuf, PFNRTTRACEBUFCALLBACK pfnCallback, void *pvUser)
{
    int                 rc = VINF_SUCCESS;
    uint32_t            iBase;
    uint32_t            cLeft;
    PCRTTRACEBUFINT     pThis;
    RTTRACEBUF_RESOLVE_VALIDATE_RETAIN_RETURN(hTraceBuf, pThis);

    iBase = ASMAtomicReadU32(&RTTRACEBUF_TO_VOLATILE(pThis)->iEntry);
    cLeft = pThis->cEntries;
    while (cLeft--)
    {
        PRTTRACEBUFENTRY pEntry;

        iBase %= pThis->cEntries;
        pEntry = RTTRACEBUF_TO_ENTRY(pThis, iBase);
        if (pEntry->NanoTS)
        {
            rc = pfnCallback((RTTRACEBUF)pThis, cLeft, pEntry->NanoTS, pEntry->idCpu, pEntry->szMsg, pvUser);
            if (rc != VINF_SUCCESS)
                break;
        }

        /* next */
        iBase += 1;
    }

    RTTRACEBUF_DROP_REFERENCE(pThis);
    return rc;
}


RTDECL(uint32_t) RTTraceBufGetEntrySize(RTTRACEBUF hTraceBuf)
{
    PCRTTRACEBUFINT pThis = hTraceBuf;
    RTTRACEBUF_VALID_RETURN_RC(pThis, 0);
    return pThis->cbEntry;
}


RTDECL(uint32_t) RTTraceBufGetEntryCount(RTTRACEBUF hTraceBuf)
{
    PCRTTRACEBUFINT pThis = hTraceBuf;
    RTTRACEBUF_VALID_RETURN_RC(pThis, 0);
    return pThis->cEntries;
}


RTDECL(bool) RTTraceBufDisable(RTTRACEBUF hTraceBuf)
{
    PCRTTRACEBUFINT pThis = hTraceBuf;
    RTTRACEBUF_VALID_RETURN_RC(pThis, false);
    return !ASMAtomicBitTestAndSet((void volatile *)&pThis->fFlags, RTTRACEBUF_FLAGS_DISABLED_BIT);
}


RTDECL(bool) RTTraceBufEnable(RTTRACEBUF hTraceBuf)
{
    PCRTTRACEBUFINT pThis = hTraceBuf;
    RTTRACEBUF_VALID_RETURN_RC(pThis, false);
    return !ASMAtomicBitTestAndClear((void volatile *)&pThis->fFlags, RTTRACEBUF_FLAGS_DISABLED_BIT);
}


/*
 *
 * Move the following to a separate file, consider using the enumerator.
 *
 */

RTDECL(int) RTTraceBufDumpToLog(RTTRACEBUF hTraceBuf)
{
    uint32_t            iBase;
    uint32_t            cLeft;
    PCRTTRACEBUFINT     pThis;
    RTTRACEBUF_RESOLVE_VALIDATE_RETAIN_RETURN(hTraceBuf, pThis);

    iBase = ASMAtomicReadU32(&RTTRACEBUF_TO_VOLATILE(pThis)->iEntry);
    cLeft = pThis->cEntries;
    while (cLeft--)
    {
        PRTTRACEBUFENTRY pEntry;

        iBase %= pThis->cEntries;
        pEntry = RTTRACEBUF_TO_ENTRY(pThis, iBase);
        if (pEntry->NanoTS)
            RTLogPrintf("%04u/%'llu/%02x: %s\n", cLeft, pEntry->NanoTS, pEntry->idCpu, pEntry->szMsg);

        /* next */
        iBase += 1;
    }

    RTTRACEBUF_DROP_REFERENCE(pThis);
    return VINF_SUCCESS;
}


RTDECL(int) RTTraceBufDumpToAssert(RTTRACEBUF hTraceBuf)
{
    uint32_t            iBase;
    uint32_t            cLeft;
    PCRTTRACEBUFINT     pThis;
    RTTRACEBUF_RESOLVE_VALIDATE_RETAIN_RETURN(hTraceBuf, pThis);

    iBase = ASMAtomicReadU32(&RTTRACEBUF_TO_VOLATILE(pThis)->iEntry);
    cLeft = pThis->cEntries;
    while (cLeft--)
    {
        PRTTRACEBUFENTRY pEntry;

        iBase %= pThis->cEntries;
        pEntry = RTTRACEBUF_TO_ENTRY(pThis, iBase);
        if (pEntry->NanoTS)
            RTAssertMsg2AddWeak("%u/%'llu/%02x: %s\n", cLeft, pEntry->NanoTS, pEntry->idCpu, pEntry->szMsg);

        /* next */
        iBase += 1;
    }

    RTTRACEBUF_DROP_REFERENCE(pThis);
    return VINF_SUCCESS;
}

