/* $Id: VBoxDTraceR0.cpp $ */
/** @file
 * VBoxDTraceR0.
 *
 * Contributed by: bird
 */

/*
 * Copyright (C) 2012-2020 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it 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.
 * VirtualBox OSE is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY of any kind.
 */


/*********************************************************************************************************************************
*   Header Files                                                                                                                 *
*********************************************************************************************************************************/
#include <VBox/sup.h>
#include <VBox/log.h>

#include <iprt/asm-amd64-x86.h>
#include <iprt/assert.h>
#include <iprt/ctype.h>
#include <iprt/err.h>
#include <iprt/mem.h>
#include <iprt/mp.h>
#include <iprt/process.h>
#include <iprt/semaphore.h>
#include <iprt/spinlock.h>
#include <iprt/string.h>
#include <iprt/thread.h>
#include <iprt/time.h>

#include <sys/dtrace_impl.h>

#include <VBox/VBoxTpG.h>


/*********************************************************************************************************************************
*   Defined Constants And Macros                                                                                                 *
*********************************************************************************************************************************/
//#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
//# define HAVE_RTMEMALLOCEX_FEATURES
//#endif


/*********************************************************************************************************************************
*   Structures and Typedefs                                                                                                      *
*********************************************************************************************************************************/

/** Caller indicator. */
typedef enum VBOXDTCALLER
{
    kVBoxDtCaller_Invalid = 0,
    kVBoxDtCaller_Generic,
    kVBoxDtCaller_ProbeFireUser,
    kVBoxDtCaller_ProbeFireKernel
} VBOXDTCALLER;

/**
 * Stack data used for thread structure and such.
 *
 * This is planted in every external entry point and used to emulate solaris
 * curthread, CRED, curproc and similar.  It is also used to get at the
 * uncached probe arguments.
 */
typedef struct VBoxDtStackData
{
    /** Eyecatcher no. 1 (VBDT_STACK_DATA_MAGIC2). */
    uint32_t                u32Magic1;
    /** Eyecatcher no. 2 (VBDT_STACK_DATA_MAGIC2). */
    uint32_t                u32Magic2;
    /** The format of the caller specific data. */
    VBOXDTCALLER            enmCaller;
    /** Caller specific data.  */
    union
    {
        /** kVBoxDtCaller_ProbeFireKernel. */
        struct
        {
            /** The caller. */
            uintptr_t               uCaller;
            /** Pointer to the stack arguments of a probe function call. */
            uintptr_t              *pauStackArgs;
        } ProbeFireKernel;
        /** kVBoxDtCaller_ProbeFireUser. */
        struct
        {
            /** The user context.  */
            PCSUPDRVTRACERUSRCTX    pCtx;
            /** The argument displacement caused by 64-bit arguments passed directly to
             *  dtrace_probe. */
            int                     offArg;
        } ProbeFireUser;
    } u;
    /** Credentials allocated by VBoxDtGetCurrentCreds. */
    struct VBoxDtCred      *pCred;
    /** Thread structure currently being held by this thread. */
    struct VBoxDtThread    *pThread;
    /** Pointer to this structure.
     * This is the final bit of integrity checking. */
    struct VBoxDtStackData *pSelf;
} VBDTSTACKDATA;
/** Pointer to the on-stack thread specific data. */
typedef VBDTSTACKDATA *PVBDTSTACKDATA;

/** The first magic value. */
#define VBDT_STACK_DATA_MAGIC1      RT_MAKE_U32_FROM_U8('V', 'B', 'o', 'x')
/** The second magic value. */
#define VBDT_STACK_DATA_MAGIC2      RT_MAKE_U32_FROM_U8('D', 'T', 'r', 'c')

/** The alignment of the stack data.
 * The data doesn't require more than sizeof(uintptr_t) alignment, but the
 * greater alignment the quicker lookup. */
#define VBDT_STACK_DATA_ALIGN       32

/** Plants the stack data. */
#define VBDT_SETUP_STACK_DATA(a_enmCaller) \
    uint8_t abBlob[sizeof(VBDTSTACKDATA) + VBDT_STACK_DATA_ALIGN - 1]; \
    PVBDTSTACKDATA pStackData = (PVBDTSTACKDATA)(    (uintptr_t)&abBlob[VBDT_STACK_DATA_ALIGN - 1] \
                                                 &  ~(uintptr_t)(VBDT_STACK_DATA_ALIGN - 1)); \
    pStackData->u32Magic1   = VBDT_STACK_DATA_MAGIC1; \
    pStackData->u32Magic2   = VBDT_STACK_DATA_MAGIC2; \
    pStackData->enmCaller   = a_enmCaller; \
    pStackData->pCred       = NULL; \
    pStackData->pThread     = NULL; \
    pStackData->pSelf       = pStackData

/** Passifies the stack data and frees up resource held within it. */
#define VBDT_CLEAR_STACK_DATA() \
    do \
    { \
        pStackData->u32Magic1   = 0; \
        pStackData->u32Magic2   = 0; \
        pStackData->pSelf       = NULL; \
        if (pStackData->pCred) \
            crfree(pStackData->pCred); \
        if (pStackData->pThread) \
            VBoxDtReleaseThread(pStackData->pThread); \
    } while (0)


/** Simple SUPR0Printf-style logging.  */
#if 0 /*def DEBUG_bird*/
# define LOG_DTRACE(a) SUPR0Printf a
#else
# define LOG_DTRACE(a) do { } while (0)
#endif


/*********************************************************************************************************************************
*   Global Variables                                                                                                             *
*********************************************************************************************************************************/
/** Per CPU information */
cpucore_t                       g_aVBoxDtCpuCores[RTCPUSET_MAX_CPUS];
/** Dummy mutex. */
struct VBoxDtMutex              g_DummyMtx;
/** Pointer to the tracer helpers provided by VBoxDrv. */
static PCSUPDRVTRACERHLP        g_pVBoxDTraceHlp;

dtrace_cacheid_t dtrace_predcache_id = DTRACE_CACHEIDNONE + 1;

#if 0
void           (*dtrace_cpu_init)(processorid_t);
void           (*dtrace_modload)(struct modctl *);
void           (*dtrace_modunload)(struct modctl *);
void           (*dtrace_helpers_cleanup)(void);
void           (*dtrace_helpers_fork)(proc_t *, proc_t *);
void           (*dtrace_cpustart_init)(void);
void           (*dtrace_cpustart_fini)(void);
void           (*dtrace_cpc_fire)(uint64_t);
void           (*dtrace_debugger_init)(void);
void           (*dtrace_debugger_fini)(void);
#endif


/**
 * Gets the stack data.
 *
 * @returns Pointer to the stack data.  Never NULL.
 */
static PVBDTSTACKDATA vboxDtGetStackData(void)
{
    int volatile    iDummy = 1; /* use this to get the stack address. */
    PVBDTSTACKDATA  pData = (PVBDTSTACKDATA)(  ((uintptr_t)&iDummy + VBDT_STACK_DATA_ALIGN - 1)
                                             & ~(uintptr_t)(VBDT_STACK_DATA_ALIGN - 1));
    for (;;)
    {
        if (   pData->u32Magic1 == VBDT_STACK_DATA_MAGIC1
            && pData->u32Magic2 == VBDT_STACK_DATA_MAGIC2
            && pData->pSelf     == pData)
            return pData;
        pData = (PVBDTSTACKDATA)((uintptr_t)pData + VBDT_STACK_DATA_ALIGN);
    }
}


void dtrace_toxic_ranges(void (*pfnAddOne)(uintptr_t uBase, uintptr_t cbRange))
{
    /** @todo ? */
    RT_NOREF_PV(pfnAddOne);
}



/**
 * Dummy callback used by dtrace_sync.
 */
static DECLCALLBACK(void) vboxDtSyncCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
{
    NOREF(idCpu); NOREF(pvUser1); NOREF(pvUser2);
}


/**
 * Synchronzie across all CPUs (expensive).
 */
void    dtrace_sync(void)
{
    int rc = RTMpOnAll(vboxDtSyncCallback, NULL, NULL);
    AssertRC(rc);
}


/**
 * Fetch a 8-bit "word" from userland.
 *
 * @return  The byte value.
 * @param   pvUserAddr      The userland address.
 */
uint8_t  dtrace_fuword8( void *pvUserAddr)
{
    uint8_t u8;
    int rc = RTR0MemUserCopyFrom(&u8, (uintptr_t)pvUserAddr, sizeof(u8));
    if (RT_FAILURE(rc))
    {
        RTCPUID iCpu = VBDT_GET_CPUID();
        cpu_core[iCpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
        cpu_core[iCpu].cpuc_dtrace_illval = (uintptr_t)pvUserAddr;
        u8 = 0;
    }
    return u8;
}


/**
 * Fetch a 16-bit word from userland.
 *
 * @return  The word value.
 * @param   pvUserAddr      The userland address.
 */
uint16_t dtrace_fuword16(void *pvUserAddr)
{
    uint16_t u16;
    int rc = RTR0MemUserCopyFrom(&u16, (uintptr_t)pvUserAddr, sizeof(u16));
    if (RT_FAILURE(rc))
    {
        RTCPUID iCpu = VBDT_GET_CPUID();
        cpu_core[iCpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
        cpu_core[iCpu].cpuc_dtrace_illval = (uintptr_t)pvUserAddr;
        u16 = 0;
    }
    return u16;
}


/**
 * Fetch a 32-bit word from userland.
 *
 * @return  The dword value.
 * @param   pvUserAddr      The userland address.
 */
uint32_t dtrace_fuword32(void *pvUserAddr)
{
    uint32_t u32;
    int rc = RTR0MemUserCopyFrom(&u32, (uintptr_t)pvUserAddr, sizeof(u32));
    if (RT_FAILURE(rc))
    {
        RTCPUID iCpu = VBDT_GET_CPUID();
        cpu_core[iCpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
        cpu_core[iCpu].cpuc_dtrace_illval = (uintptr_t)pvUserAddr;
        u32 = 0;
    }
    return u32;
}


/**
 * Fetch a 64-bit word from userland.
 *
 * @return  The qword value.
 * @param   pvUserAddr      The userland address.
 */
uint64_t dtrace_fuword64(void *pvUserAddr)
{
    uint64_t u64;
    int rc = RTR0MemUserCopyFrom(&u64, (uintptr_t)pvUserAddr, sizeof(u64));
    if (RT_FAILURE(rc))
    {
        RTCPUID iCpu = VBDT_GET_CPUID();
        cpu_core[iCpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
        cpu_core[iCpu].cpuc_dtrace_illval = (uintptr_t)pvUserAddr;
        u64 = 0;
    }
    return u64;
}


/** copyin implementation */
int  VBoxDtCopyIn(void const *pvUser, void *pvDst, size_t cb)
{
    int rc = RTR0MemUserCopyFrom(pvDst, (uintptr_t)pvUser, cb);
    return RT_SUCCESS(rc) ? 0 : -1;
}


/** copyout implementation */
int  VBoxDtCopyOut(void const *pvSrc, void *pvUser, size_t cb)
{
    int rc = RTR0MemUserCopyTo((uintptr_t)pvUser, pvSrc, cb);
    return RT_SUCCESS(rc) ? 0 : -1;
}


/**
 * Copy data from userland into the kernel.
 *
 * @param   uUserAddr           The userland address.
 * @param   uKrnlAddr           The kernel buffer address.
 * @param   cb                  The number of bytes to copy.
 * @param   pfFlags             Pointer to the relevant cpuc_dtrace_flags.
 */
void dtrace_copyin(    uintptr_t uUserAddr, uintptr_t uKrnlAddr, size_t cb, volatile uint16_t *pfFlags)
{
    int rc = RTR0MemUserCopyFrom((void *)uKrnlAddr, uUserAddr, cb);
    if (RT_FAILURE(rc))
    {
        *pfFlags |= CPU_DTRACE_BADADDR;
        cpu_core[VBDT_GET_CPUID()].cpuc_dtrace_illval = uUserAddr;
    }
}


/**
 * Copy data from the kernel into userland.
 *
 * @param   uKrnlAddr           The kernel buffer address.
 * @param   uUserAddr           The userland address.
 * @param   cb                  The number of bytes to copy.
 * @param   pfFlags             Pointer to the relevant cpuc_dtrace_flags.
 */
void dtrace_copyout(   uintptr_t uKrnlAddr, uintptr_t uUserAddr, size_t cb, volatile uint16_t *pfFlags)
{
    int rc = RTR0MemUserCopyTo(uUserAddr, (void const *)uKrnlAddr, cb);
    if (RT_FAILURE(rc))
    {
        *pfFlags |= CPU_DTRACE_BADADDR;
        cpu_core[VBDT_GET_CPUID()].cpuc_dtrace_illval = uUserAddr;
    }
}


/**
 * Copy a string from userland into the kernel.
 *
 * @param   uUserAddr           The userland address.
 * @param   uKrnlAddr           The kernel buffer address.
 * @param   cbMax               The maximum number of bytes to copy. May stop
 *                              earlier if zero byte is encountered.
 * @param   pfFlags             Pointer to the relevant cpuc_dtrace_flags.
 */
void dtrace_copyinstr( uintptr_t uUserAddr, uintptr_t uKrnlAddr, size_t cbMax, volatile uint16_t *pfFlags)
{
    if (!cbMax)
        return;

    char   *pszDst = (char *)uKrnlAddr;
    int     rc = RTR0MemUserCopyFrom(pszDst, uUserAddr, cbMax);
    if (RT_FAILURE(rc))
    {
        /* Byte by byte - lazy bird! */
        size_t off = 0;
        while (off < cbMax)
        {
            rc = RTR0MemUserCopyFrom(&pszDst[off], uUserAddr + off, 1);
            if (RT_FAILURE(rc))
            {
                *pfFlags |= CPU_DTRACE_BADADDR;
                cpu_core[VBDT_GET_CPUID()].cpuc_dtrace_illval = uUserAddr;
                pszDst[off] = '\0';
                return;
            }
            if (!pszDst[off])
                return;
            off++;
        }
    }

    pszDst[cbMax - 1] = '\0';
}


/**
 * Copy a string from the kernel and into user land.
 *
 * @param   uKrnlAddr           The kernel string address.
 * @param   uUserAddr           The userland address.
 * @param   cbMax               The maximum number of bytes to copy.  Will stop
 *                              earlier if zero byte is encountered.
 * @param   pfFlags             Pointer to the relevant cpuc_dtrace_flags.
 */
void dtrace_copyoutstr(uintptr_t uKrnlAddr, uintptr_t uUserAddr, size_t cbMax, volatile uint16_t *pfFlags)
{
    const char *pszSrc   = (const char *)uKrnlAddr;
    size_t      cbActual = RTStrNLen(pszSrc, cbMax);
    cbActual += cbActual < cbMax;
    dtrace_copyout(uKrnlAddr,uUserAddr, cbActual, pfFlags);
}


/**
 * Get the caller @a cCallFrames call frames up the stack.
 *
 * @returns The caller's return address or ~(uintptr_t)0.
 * @param   cCallFrames         The number of frames.
 */
uintptr_t dtrace_caller(int cCallFrames)
{
    PVBDTSTACKDATA pData = vboxDtGetStackData();
    if (pData->enmCaller == kVBoxDtCaller_ProbeFireKernel)
        return pData->u.ProbeFireKernel.uCaller;
    RT_NOREF_PV(cCallFrames);
    return ~(uintptr_t)0;
}


/**
 * Get argument number @a iArg @a cCallFrames call frames up the stack.
 *
 * @returns The caller's return address or ~(uintptr_t)0.
 * @param   iArg                The argument to get.
 * @param   cCallFrames         The number of frames.
 */
uint64_t dtrace_getarg(int iArg, int cCallFrames)
{
    PVBDTSTACKDATA pData = vboxDtGetStackData();
    AssertReturn(iArg >= 5, UINT64_MAX);

    if (pData->enmCaller == kVBoxDtCaller_ProbeFireKernel)
        return pData->u.ProbeFireKernel.pauStackArgs[iArg - 5];
    RT_NOREF_PV(cCallFrames);
    return UINT64_MAX;
}


/**
 * Produce a traceback of the kernel stack.
 *
 * @param   paPcStack           Where to return the program counters.
 * @param   cMaxFrames          The maximum number of PCs to return.
 * @param   cSkipFrames         The number of artificial callstack frames to
 *                              skip at the top.
 * @param   pIntr               Not sure what this is...
 */
void dtrace_getpcstack(pc_t *paPcStack, int cMaxFrames, int cSkipFrames, uint32_t *pIntr)
{
    int iFrame = 0;
    while (iFrame < cMaxFrames)
    {
        paPcStack[iFrame] = NULL;
        iFrame++;
    }
    RT_NOREF_PV(pIntr);
    RT_NOREF_PV(cSkipFrames);
}


/**
 * Get the number of call frames on the stack.
 *
 * @returns The stack depth.
 * @param   cSkipFrames         The number of artificial callstack frames to
 *                              skip at the top.
 */
int dtrace_getstackdepth(int cSkipFrames)
{
    RT_NOREF_PV(cSkipFrames);
    return 1;
}


/**
 * Produce a traceback of the userland stack.
 *
 * @param   paPcStack           Where to return the program counters.
 * @param   paFpStack           Where to return the frame pointers.
 * @param   cMaxFrames          The maximum number of frames to return.
 */
void dtrace_getufpstack(uint64_t *paPcStack, uint64_t *paFpStack, int cMaxFrames)
{
    int iFrame = 0;
    while (iFrame < cMaxFrames)
    {
        paPcStack[iFrame] = 0;
        paFpStack[iFrame] = 0;
        iFrame++;
    }
}


/**
 * Produce a traceback of the userland stack.
 *
 * @param   paPcStack           Where to return the program counters.
 * @param   cMaxFrames          The maximum number of frames to return.
 */
void dtrace_getupcstack(uint64_t *paPcStack, int cMaxFrames)
{
    int iFrame = 0;
    while (iFrame < cMaxFrames)
    {
        paPcStack[iFrame] = 0;
        iFrame++;
    }
}


/**
 * Computes the depth of the userland stack.
 */
int dtrace_getustackdepth(void)
{
    return 0;
}


/**
 * Get the current IPL/IRQL.
 *
 * @returns Current level.
 */
int dtrace_getipl(void)
{
#ifdef RT_ARCH_AMD64
    /* CR8 is normally the same as IRQL / IPL on AMD64. */
    return ASMGetCR8();
#else
    /* Just fake it on x86. */
    return !ASMIntAreEnabled();
#endif
}


/**
 * Get current monotonic timestamp.
 *
 * @returns Timestamp, nano seconds.
 */
hrtime_t dtrace_gethrtime(void)
{
    return RTTimeNanoTS();
}


/**
 * Get current walltime.
 *
 * @returns Timestamp, nano seconds.
 */
hrtime_t dtrace_gethrestime(void)
{
    /** @todo try get better resolution here somehow ... */
    RTTIMESPEC Now;
    return RTTimeSpecGetNano(RTTimeNow(&Now));
}


/**
 * DTrace panic routine.
 *
 * @param   pszFormat           Panic message.
 * @param   va                  Arguments to the panic message.
 */
void dtrace_vpanic(const char *pszFormat, va_list va)
{
    RTAssertMsg1(NULL, __LINE__, __FILE__, __FUNCTION__);
    RTAssertMsg2WeakV(pszFormat, va);
    RTR0AssertPanicSystem();
    for (;;)
    {
        ASMBreakpoint();
        volatile char *pchCrash = (volatile char *)~(uintptr_t)0;
        *pchCrash = '\0';
    }
}


/**
 * DTrace panic routine.
 *
 * @param   pszFormat           Panic message.
 * @param   ...                 Arguments to the panic message.
 */
void VBoxDtPanic(const char *pszFormat, ...)
{
    va_list va;
    va_start(va, pszFormat);
    dtrace_vpanic(pszFormat, va);
    /*va_end(va); - unreachable */
}


/**
 * DTrace kernel message routine.
 *
 * @param   pszFormat           Kernel message.
 * @param   ...                 Arguments to the panic message.
 */
void VBoxDtCmnErr(int iLevel, const char *pszFormat, ...)
{
    va_list va;
    va_start(va, pszFormat);
    SUPR0Printf("%N", pszFormat, va);
    va_end(va);
    RT_NOREF_PV(iLevel);
}


/** uprintf implementation */
void VBoxDtUPrintf(const char *pszFormat, ...)
{
    va_list va;
    va_start(va, pszFormat);
    VBoxDtUPrintfV(pszFormat, va);
    va_end(va);
}


/** vuprintf implementation */
void VBoxDtUPrintfV(const char *pszFormat, va_list va)
{
    SUPR0Printf("%N", pszFormat, va);
}


/* CRED implementation. */
cred_t *VBoxDtGetCurrentCreds(void)
{
    PVBDTSTACKDATA pData = vboxDtGetStackData();
    if (!pData->pCred)
    {
        struct VBoxDtCred *pCred;
#ifdef HAVE_RTMEMALLOCEX_FEATURES
        int rc = RTMemAllocEx(sizeof(*pCred), 0, RTMEMALLOCEX_FLAGS_ANY_CTX, (void **)&pCred);
#else
        int rc = RTMemAllocEx(sizeof(*pCred), 0, 0, (void **)&pCred);
#endif
        AssertFatalRC(rc);
        pCred->cr_refs  = 1;
        /** @todo get the right creds on unix systems. */
        pCred->cr_uid   = 0;
        pCred->cr_ruid  = 0;
        pCred->cr_suid  = 0;
        pCred->cr_gid   = 0;
        pCred->cr_rgid  = 0;
        pCred->cr_sgid  = 0;
        pCred->cr_zone  = 0;
        pData->pCred = pCred;
    }

    return pData->pCred;
}


/* crhold implementation */
void VBoxDtCredHold(struct VBoxDtCred *pCred)
{
    int32_t cRefs = ASMAtomicIncS32(&pCred->cr_refs);
    Assert(cRefs > 1); NOREF(cRefs);
}


/* crfree implementation */
void VBoxDtCredFree(struct VBoxDtCred *pCred)
{
    int32_t cRefs = ASMAtomicDecS32(&pCred->cr_refs);
    Assert(cRefs >= 0);
    if (!cRefs)
        RTMemFreeEx(pCred, sizeof(*pCred));
}

/** Spinlock protecting the thread structures. */
static RTSPINLOCK           g_hThreadSpinlock = NIL_RTSPINLOCK;
/** List of threads by usage age. */
static RTLISTANCHOR         g_ThreadAgeList;
/** Hash table for looking up thread structures.  */
static struct VBoxDtThread *g_apThreadsHash[16384];
/** Fake kthread_t structures.
 * The size of this array is making horrible ASSUMPTIONS about the number of
 * thread in the system that will be subjected to DTracing. */
static struct VBoxDtThread  g_aThreads[8192];


static int vboxDtInitThreadDb(void)
{
    int rc = RTSpinlockCreate(&g_hThreadSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxDtThreadDb");
    if (RT_FAILURE(rc))
        return rc;

    RTListInit(&g_ThreadAgeList);
    for (uint32_t i = 0; i < RT_ELEMENTS(g_aThreads); i++)
    {
        g_aThreads[i].hNative = NIL_RTNATIVETHREAD;
        g_aThreads[i].uPid    = NIL_RTPROCESS;
        RTListPrepend(&g_ThreadAgeList, &g_aThreads[i].AgeEntry);
    }

    return VINF_SUCCESS;
}


static void vboxDtTermThreadDb(void)
{
    RTSpinlockDestroy(g_hThreadSpinlock);
    g_hThreadSpinlock = NIL_RTSPINLOCK;
    RTListInit(&g_ThreadAgeList);
}


/* curthread implementation, providing a fake kthread_t. */
struct VBoxDtThread *VBoxDtGetCurrentThread(void)
{
    /*
     * Once we've retrieved a thread, we hold on to it until the thread exits
     * the VBoxDTrace module.
     */
    PVBDTSTACKDATA  pData       = vboxDtGetStackData();
    if (pData->pThread)
    {
        AssertPtr(pData->pThread);
        Assert(pData->pThread->hNative   == RTThreadNativeSelf());
        Assert(pData->pThread->uPid      == RTProcSelf());
        Assert(RTListIsEmpty(&pData->pThread->AgeEntry));
        return pData->pThread;
    }

    /*
     * Lookup the thread in the hash table.
     */
    RTNATIVETHREAD  hNativeSelf = RTThreadNativeSelf();
    RTPROCESS       uPid        = RTProcSelf();
    uintptr_t       iHash       = (hNativeSelf * 2654435761U) % RT_ELEMENTS(g_apThreadsHash);

    RTSpinlockAcquire(g_hThreadSpinlock);

    struct VBoxDtThread *pThread = g_apThreadsHash[iHash];
    while (pThread)
    {
        if (pThread->hNative == hNativeSelf)
        {
            if (pThread->uPid != uPid)
            {
                /* Re-initialize the reused thread. */
                pThread->uPid           = uPid;
                pThread->t_dtrace_vtime = 0;
                pThread->t_dtrace_start = 0;
                pThread->t_dtrace_stop  = 0;
                pThread->t_dtrace_scrpc = 0;
                pThread->t_dtrace_astpc = 0;
                pThread->t_predcache    = 0;
            }

            /* Hold the thread in the on-stack data, making sure it does not
               get reused till the thread leaves VBoxDTrace. */
            RTListNodeRemove(&pThread->AgeEntry);
            pData->pThread = pThread;

            RTSpinlockRelease(g_hThreadSpinlock);
            return pThread;
        }

        pThread = pThread->pNext;
    }

    /*
     * Unknown thread.  Allocate a new entry, recycling unused or old ones.
     */
    pThread = RTListGetLast(&g_ThreadAgeList, struct VBoxDtThread, AgeEntry);
    AssertFatal(pThread);
    RTListNodeRemove(&pThread->AgeEntry);
    if (pThread->hNative != NIL_RTNATIVETHREAD)
    {
        uintptr_t   iHash2 = (pThread->hNative * 2654435761U) % RT_ELEMENTS(g_apThreadsHash);
        if (g_apThreadsHash[iHash2] == pThread)
            g_apThreadsHash[iHash2] = pThread->pNext;
        else
        {
            for (struct VBoxDtThread *pPrev = g_apThreadsHash[iHash2]; ; pPrev = pPrev->pNext)
            {
                AssertPtr(pPrev);
                if (pPrev->pNext == pThread)
                {
                    pPrev->pNext = pThread->pNext;
                    break;
                }
            }
        }
    }

    /*
     * Initialize the data.
     */
    pThread->t_dtrace_vtime = 0;
    pThread->t_dtrace_start = 0;
    pThread->t_dtrace_stop  = 0;
    pThread->t_dtrace_scrpc = 0;
    pThread->t_dtrace_astpc = 0;
    pThread->t_predcache    = 0;
    pThread->hNative        = hNativeSelf;
    pThread->uPid           = uPid;

    /*
     * Add it to the hash as well as the on-stack data.
     */
    pThread->pNext = g_apThreadsHash[iHash];
    g_apThreadsHash[iHash] = pThread->pNext;

    pData->pThread = pThread;

    RTSpinlockRelease(g_hThreadSpinlock);
    return pThread;
}


/**
 * Called by the stack data destructor.
 *
 * @param   pThread         The thread to release.
 *
 */
static void VBoxDtReleaseThread(struct VBoxDtThread *pThread)
{
    RTSpinlockAcquire(g_hThreadSpinlock);

    RTListAppend(&g_ThreadAgeList, &pThread->AgeEntry);

    RTSpinlockRelease(g_hThreadSpinlock);
}




/*
 *
 * Virtual Memory / Resource Allocator.
 * Virtual Memory / Resource Allocator.
 * Virtual Memory / Resource Allocator.
 *
 */


/** The number of bits per chunk.
 * @remarks The 32 bytes are for heap headers and such like.  */
#define VBOXDTVMEMCHUNK_BITS    ( ((_64K - 32 - sizeof(uint32_t) * 2) / sizeof(uint32_t)) * 32)

/**
 * Resource allocator chunk.
 */
typedef struct  VBoxDtVMemChunk
{
    /** The ordinal (unbased) of the first item. */
    uint32_t            iFirst;
    /** The current number of free items in this chunk. */
    uint32_t            cCurFree;
    /** The allocation bitmap. */
    uint32_t            bm[VBOXDTVMEMCHUNK_BITS / 32];
} VBOXDTVMEMCHUNK;
/** Pointer to a resource allocator chunk. */
typedef VBOXDTVMEMCHUNK *PVBOXDTVMEMCHUNK;



/**
 * Resource allocator instance.
 */
typedef struct VBoxDtVMem
{
    /** Spinlock protecting the data (interrupt safe). */
    RTSPINLOCK          hSpinlock;
    /** Magic value. */
    uint32_t            u32Magic;
    /** The current number of free items in the chunks. */
    uint32_t            cCurFree;
    /** The current number of chunks that we have allocated. */
    uint32_t            cCurChunks;
    /** The configured resource base. */
    uint32_t            uBase;
    /** The configured max number of items. */
    uint32_t            cMaxItems;
    /** The size of the apChunks array. */
    uint32_t            cMaxChunks;
    /** Array of chunk pointers.
     * (The size is determined at creation.) */
    PVBOXDTVMEMCHUNK    apChunks[1];
} VBOXDTVMEM;
/** Pointer to a resource allocator instance. */
typedef VBOXDTVMEM *PVBOXDTVMEM;

/** Magic value for the VBOXDTVMEM structure. */
#define VBOXDTVMEM_MAGIC        RT_MAKE_U32_FROM_U8('V', 'M',  'e',  'm')


/* vmem_create implementation */
struct VBoxDtVMem *VBoxDtVMemCreate(const char *pszName, void *pvBase, size_t cb, size_t cbUnit,
                                    PFNRT pfnAlloc, PFNRT pfnFree, struct VBoxDtVMem *pSrc,
                                    size_t cbQCacheMax, uint32_t fFlags)
{
    /*
     * Assert preconditions of this implementation.
     */
    AssertMsgReturn((uintptr_t)pvBase <= UINT32_MAX, ("%p\n", pvBase), NULL);
    AssertMsgReturn(cb <= UINT32_MAX, ("%zu\n", cb), NULL);
    AssertMsgReturn((uintptr_t)pvBase + cb - 1 <= UINT32_MAX, ("%p %zu\n", pvBase, cb), NULL);
    AssertMsgReturn(cbUnit == 1, ("%zu\n", cbUnit), NULL);
    AssertReturn(!pfnAlloc, NULL);
    AssertReturn(!pfnFree, NULL);
    AssertReturn(!pSrc, NULL);
    AssertReturn(!cbQCacheMax, NULL);
    AssertReturn(fFlags & VM_SLEEP, NULL);
    AssertReturn(fFlags & VMC_IDENTIFIER, NULL);
    RT_NOREF_PV(pszName);

    /*
     * Allocate the instance.
     */
    uint32_t cChunks = (uint32_t)cb / VBOXDTVMEMCHUNK_BITS;
    if (cb % VBOXDTVMEMCHUNK_BITS)
        cChunks++;
    PVBOXDTVMEM pThis = (PVBOXDTVMEM)RTMemAllocZ(RT_UOFFSETOF_DYN(VBOXDTVMEM, apChunks[cChunks]));
    if (!pThis)
        return NULL;
    int rc = RTSpinlockCreate(&pThis->hSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxDtVMem");
    if (RT_FAILURE(rc))
    {
        RTMemFree(pThis);
        return NULL;
    }
    pThis->u32Magic     = VBOXDTVMEM_MAGIC;
    pThis->cCurFree     = 0;
    pThis->cCurChunks   = 0;
    pThis->uBase        = (uint32_t)(uintptr_t)pvBase;
    pThis->cMaxItems    = (uint32_t)cb;
    pThis->cMaxChunks   = cChunks;

    return pThis;
}


/* vmem_destroy implementation */
void  VBoxDtVMemDestroy(struct VBoxDtVMem *pThis)
{
    if (!pThis)
        return;
    AssertPtrReturnVoid(pThis);
    AssertReturnVoid(pThis->u32Magic == VBOXDTVMEM_MAGIC);

    /*
     * Invalidate the instance.
     */
    RTSpinlockAcquire(pThis->hSpinlock); /* paranoia */
    pThis->u32Magic = 0;
    RTSpinlockRelease(pThis->hSpinlock);
    RTSpinlockDestroy(pThis->hSpinlock);

    /*
     * Free the chunks, then the instance.
     */
    uint32_t iChunk = pThis->cCurChunks;
    while (iChunk-- > 0)
    {
        RTMemFree(pThis->apChunks[iChunk]);
        pThis->apChunks[iChunk] = NULL;
    }
    RTMemFree(pThis);
}


/* vmem_alloc implementation */
void *VBoxDtVMemAlloc(struct VBoxDtVMem *pThis, size_t cbMem, uint32_t fFlags)
{
    /*
     * Validate input.
     */
    AssertReturn(fFlags & VM_BESTFIT, NULL);
    AssertReturn(fFlags & VM_SLEEP, NULL);
    AssertReturn(cbMem == 1, NULL);
    AssertPtrReturn(pThis, NULL);
    AssertReturn(pThis->u32Magic == VBOXDTVMEM_MAGIC, NULL);

    /*
     * Allocation loop.
     */
    RTSpinlockAcquire(pThis->hSpinlock);
    for (;;)
    {
        PVBOXDTVMEMCHUNK pChunk;
        uint32_t const   cChunks = pThis->cCurChunks;

        if (RT_LIKELY(pThis->cCurFree > 0))
        {
            for (uint32_t iChunk = 0; iChunk < cChunks; iChunk++)
            {
                pChunk = pThis->apChunks[iChunk];
                if (pChunk->cCurFree > 0)
                {
                    int iBit = ASMBitFirstClear(pChunk->bm, VBOXDTVMEMCHUNK_BITS);
                    AssertMsgReturnStmt(iBit >= 0 && (unsigned)iBit < VBOXDTVMEMCHUNK_BITS, ("%d\n", iBit),
                                        RTSpinlockRelease(pThis->hSpinlock),
                                        NULL);

                    ASMBitSet(pChunk->bm, iBit);
                    pChunk->cCurFree--;
                    pThis->cCurFree--;

                    uint32_t iRet = (uint32_t)iBit + pChunk->iFirst + pThis->uBase;
                    RTSpinlockRelease(pThis->hSpinlock);
                    return (void *)(uintptr_t)iRet;
                }
            }
            AssertFailedBreak();
        }

        /* Out of resources? */
        if (cChunks >= pThis->cMaxChunks)
            break;

        /*
         * Allocate another chunk.
         */
        uint32_t const  iFirstBit = cChunks > 0 ? pThis->apChunks[cChunks - 1]->iFirst + VBOXDTVMEMCHUNK_BITS : 0;
        uint32_t const  cFreeBits = cChunks + 1 == pThis->cMaxChunks
                                  ? pThis->cMaxItems - (iFirstBit - pThis->uBase)
                                  : VBOXDTVMEMCHUNK_BITS;
        Assert(cFreeBits <= VBOXDTVMEMCHUNK_BITS);

        RTSpinlockRelease(pThis->hSpinlock);

        pChunk = (PVBOXDTVMEMCHUNK)RTMemAllocZ(sizeof(*pChunk));
        if (!pChunk)
            return NULL;

        pChunk->iFirst   = iFirstBit;
        pChunk->cCurFree = cFreeBits;
        if (cFreeBits != VBOXDTVMEMCHUNK_BITS)
        {
            /* lazy bird. */
            uint32_t iBit = cFreeBits;
            while (iBit < VBOXDTVMEMCHUNK_BITS)
            {
                ASMBitSet(pChunk->bm, iBit);
                iBit++;
            }
        }

        RTSpinlockAcquire(pThis->hSpinlock);

        /*
         * Insert the new chunk.  If someone raced us here, we'll drop it to
         * avoid wasting resources.
         */
        if (pThis->cCurChunks == cChunks)
        {
            pThis->apChunks[cChunks] = pChunk;
            pThis->cCurFree   += pChunk->cCurFree;
            pThis->cCurChunks += 1;
        }
        else
        {
            RTSpinlockRelease(pThis->hSpinlock);
            RTMemFree(pChunk);
            RTSpinlockAcquire(pThis->hSpinlock);
        }
    }
    RTSpinlockRelease(pThis->hSpinlock);

    return NULL;
}

/* vmem_free implementation */
void VBoxDtVMemFree(struct VBoxDtVMem *pThis, void *pvMem, size_t cbMem)
{
    /*
     * Validate input.
     */
    AssertReturnVoid(cbMem == 1);
    AssertPtrReturnVoid(pThis);
    AssertReturnVoid(pThis->u32Magic == VBOXDTVMEM_MAGIC);

    AssertReturnVoid((uintptr_t)pvMem < UINT32_MAX);
    uint32_t uMem = (uint32_t)(uintptr_t)pvMem;
    AssertReturnVoid(uMem >= pThis->uBase);
    uMem -= pThis->uBase;
    AssertReturnVoid(uMem < pThis->cMaxItems);


    /*
     * Free it.
     */
    RTSpinlockAcquire(pThis->hSpinlock);
    uint32_t const iChunk = uMem / VBOXDTVMEMCHUNK_BITS;
    if (iChunk < pThis->cCurChunks)
    {
        PVBOXDTVMEMCHUNK pChunk = pThis->apChunks[iChunk];
        uint32_t iBit = uMem - pChunk->iFirst;
        AssertReturnVoidStmt(iBit < VBOXDTVMEMCHUNK_BITS, RTSpinlockRelease(pThis->hSpinlock));
        AssertReturnVoidStmt(ASMBitTestAndClear(pChunk->bm, iBit), RTSpinlockRelease(pThis->hSpinlock));

        pChunk->cCurFree++;
        pThis->cCurFree++;
    }

    RTSpinlockRelease(pThis->hSpinlock);
}


/*
 *
 * Memory Allocators.
 * Memory Allocators.
 * Memory Allocators.
 *
 */


/* kmem_alloc implementation */
void *VBoxDtKMemAlloc(size_t cbMem, uint32_t fFlags)
{
    void    *pvMem;
#ifdef HAVE_RTMEMALLOCEX_FEATURES
    uint32_t fMemAllocFlags = fFlags & KM_NOSLEEP ? RTMEMALLOCEX_FLAGS_ANY_CTX : 0;
#else
    uint32_t fMemAllocFlags = 0;
    RT_NOREF_PV(fFlags);
#endif
    int rc = RTMemAllocEx(cbMem, 0, fMemAllocFlags, &pvMem);
    AssertRCReturn(rc, NULL);
    AssertPtr(pvMem);
    return pvMem;
}


/* kmem_zalloc implementation */
void *VBoxDtKMemAllocZ(size_t cbMem, uint32_t fFlags)
{
    void    *pvMem;
#ifdef HAVE_RTMEMALLOCEX_FEATURES
    uint32_t fMemAllocFlags = (fFlags & KM_NOSLEEP ? RTMEMALLOCEX_FLAGS_ANY_CTX : 0) | RTMEMALLOCEX_FLAGS_ZEROED;
#else
    uint32_t fMemAllocFlags = RTMEMALLOCEX_FLAGS_ZEROED;
    RT_NOREF_PV(fFlags);
#endif
    int rc = RTMemAllocEx(cbMem, 0, fMemAllocFlags, &pvMem);
    AssertRCReturn(rc, NULL);
    AssertPtr(pvMem);
    return pvMem;
}


/* kmem_free implementation */
void  VBoxDtKMemFree(void *pvMem, size_t cbMem)
{
    RTMemFreeEx(pvMem, cbMem);
}


/**
 * Memory cache mockup structure.
 * No slab allocator here!
 */
struct VBoxDtMemCache
{
    uint32_t u32Magic;
    size_t cbBuf;
    size_t cbAlign;
};


/* Limited kmem_cache_create implementation. */
struct VBoxDtMemCache *VBoxDtKMemCacheCreate(const char *pszName, size_t cbBuf, size_t cbAlign,
                                             PFNRT pfnCtor, PFNRT pfnDtor, PFNRT pfnReclaim,
                                             void *pvUser, void *pvVM, uint32_t fFlags)
{
    /*
     * Check the input.
     */
    AssertReturn(cbBuf > 0 && cbBuf < _1G, NULL);
    AssertReturn(RT_IS_POWER_OF_TWO(cbAlign), NULL);
    AssertReturn(!pfnCtor, NULL);
    AssertReturn(!pfnDtor, NULL);
    AssertReturn(!pfnReclaim, NULL);
    AssertReturn(!pvUser, NULL);
    AssertReturn(!pvVM, NULL);
    AssertReturn(!fFlags, NULL);
    RT_NOREF_PV(pszName);

    /*
     * Create a parameter container. Don't bother with anything fancy here yet,
     * just get something working.
     */
    struct VBoxDtMemCache *pThis = (struct VBoxDtMemCache *)RTMemAlloc(sizeof(*pThis));
    if (!pThis)
        return NULL;

    pThis->cbAlign = cbAlign;
    pThis->cbBuf   = cbBuf;
    return pThis;
}


/* Limited kmem_cache_destroy implementation. */
void  VBoxDtKMemCacheDestroy(struct VBoxDtMemCache *pThis)
{
    RTMemFree(pThis);
}


/* kmem_cache_alloc implementation. */
void *VBoxDtKMemCacheAlloc(struct VBoxDtMemCache *pThis, uint32_t fFlags)
{
    void    *pvMem;
#ifdef HAVE_RTMEMALLOCEX_FEATURES
    uint32_t fMemAllocFlags = (fFlags & KM_NOSLEEP ? RTMEMALLOCEX_FLAGS_ANY_CTX : 0) | RTMEMALLOCEX_FLAGS_ZEROED;
#else
    uint32_t fMemAllocFlags = RTMEMALLOCEX_FLAGS_ZEROED;
    RT_NOREF_PV(fFlags);
#endif
    int rc = RTMemAllocEx(pThis->cbBuf, /*pThis->cbAlign*/0, fMemAllocFlags, &pvMem);
    AssertRCReturn(rc, NULL);
    AssertPtr(pvMem);
    return pvMem;
}


/* kmem_cache_free implementation. */
void  VBoxDtKMemCacheFree(struct VBoxDtMemCache *pThis, void *pvMem)
{
    RTMemFreeEx(pvMem, pThis->cbBuf);
}


/*
 *
 * Mutex Semaphore Wrappers.
 *
 */


/** Initializes a mutex. */
int VBoxDtMutexInit(struct VBoxDtMutex *pMtx)
{
    AssertReturn(pMtx != &g_DummyMtx, -1);
    AssertPtr(pMtx);

    pMtx->hOwner = NIL_RTNATIVETHREAD;
    pMtx->hMtx   = NIL_RTSEMMUTEX;
    int rc = RTSemMutexCreate(&pMtx->hMtx);
    if (RT_SUCCESS(rc))
        return 0;
    return -1;
}


/** Deletes a mutex. */
void VBoxDtMutexDelete(struct VBoxDtMutex *pMtx)
{
    AssertReturnVoid(pMtx != &g_DummyMtx);
    AssertPtr(pMtx);
    if (pMtx->hMtx == NIL_RTSEMMUTEX)
        return;

    Assert(pMtx->hOwner == NIL_RTNATIVETHREAD);
    int rc = RTSemMutexDestroy(pMtx->hMtx); AssertRC(rc);
    pMtx->hMtx = NIL_RTSEMMUTEX;
}


/* mutex_enter implementation */
void VBoxDtMutexEnter(struct VBoxDtMutex *pMtx)
{
    AssertPtr(pMtx);
    if (pMtx == &g_DummyMtx)
        return;

    RTNATIVETHREAD hSelf = RTThreadNativeSelf();

    int rc = RTSemMutexRequest(pMtx->hMtx, RT_INDEFINITE_WAIT);
    AssertFatalRC(rc);

    Assert(pMtx->hOwner == NIL_RTNATIVETHREAD);
    pMtx->hOwner = hSelf;
}


/* mutex_exit implementation */
void VBoxDtMutexExit(struct VBoxDtMutex *pMtx)
{
    AssertPtr(pMtx);
    if (pMtx == &g_DummyMtx)
        return;

    Assert(pMtx->hOwner == RTThreadNativeSelf());

    pMtx->hOwner = NIL_RTNATIVETHREAD;
    int rc = RTSemMutexRelease(pMtx->hMtx);
    AssertFatalRC(rc);
}


/* MUTEX_HELD implementation */
bool VBoxDtMutexIsOwner(struct VBoxDtMutex *pMtx)
{
    AssertPtrReturn(pMtx, false);
    if (pMtx == &g_DummyMtx)
        return true;
    return pMtx->hOwner == RTThreadNativeSelf();
}



/*
 *
 * Helpers for handling VTG structures.
 * Helpers for handling VTG structures.
 * Helpers for handling VTG structures.
 *
 */



/**
 * Converts an attribute from VTG description speak to DTrace.
 *
 * @param   pDtAttr             The DTrace attribute (dst).
 * @param   pVtgAttr            The VTG attribute descriptor (src).
 */
static void vboxDtVtgConvAttr(dtrace_attribute_t *pDtAttr, PCVTGDESCATTR pVtgAttr)
{
    pDtAttr->dtat_name  = pVtgAttr->u8Code - 1;
    pDtAttr->dtat_data  = pVtgAttr->u8Data - 1;
    pDtAttr->dtat_class = pVtgAttr->u8DataDep - 1;
}

/**
 * Gets a string from the string table.
 *
 * @returns Pointer to the string.
 * @param   pVtgHdr             The VTG object header.
 * @param   offStrTab           The string table offset.
 */
static const char *vboxDtVtgGetString(PVTGOBJHDR pVtgHdr, uint32_t offStrTab)
{
    Assert(offStrTab < pVtgHdr->cbStrTab);
    return (const char *)pVtgHdr + pVtgHdr->offStrTab + offStrTab;
}



/*
 *
 * DTrace Provider Interface.
 * DTrace Provider Interface.
 * DTrace Provider Interface.
 *
 */


/**
 * @callback_method_impl{dtrace_pops_t,dtps_provide}
 */
static void     vboxDtPOps_Provide(void *pvProv, const dtrace_probedesc_t *pDtProbeDesc)
{
    PSUPDRVVDTPROVIDERCORE  pProv = (PSUPDRVVDTPROVIDERCORE)pvProv;
    AssertPtrReturnVoid(pProv);
    LOG_DTRACE(("%s: %p / %p pDtProbeDesc=%p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, pDtProbeDesc));

    if (pDtProbeDesc)
        return;  /* We don't generate probes, so never mind these requests. */

    if (pProv->TracerData.DTrace.fZombie)
        return;

    dtrace_provider_id_t const idProvider = pProv->TracerData.DTrace.idProvider;
    AssertPtrReturnVoid(idProvider);

    AssertPtrReturnVoid(pProv->pHdr);
    AssertReturnVoid(pProv->pHdr->offProbeLocs != 0);
    uint32_t const  cProbeLocs    = pProv->pHdr->cbProbeLocs / sizeof(VTGPROBELOC);

    /* Need a buffer for extracting the function names and mangling them in
       case of collision. */
    size_t const cbFnNmBuf = _4K + _1K;
    char *pszFnNmBuf = (char *)RTMemAlloc(cbFnNmBuf);
    if (!pszFnNmBuf)
         return;

    /*
     * Itereate the probe location list and register all probes related to
     * this provider.
     */
    uint16_t const idxProv = (uint16_t)((PVTGDESCPROVIDER)((uintptr_t)pProv->pHdr + pProv->pHdr->offProviders) - pProv->pDesc);
    for (uint32_t idxProbeLoc = 0; idxProbeLoc < cProbeLocs; idxProbeLoc++)
    {
        /* Skip probe location belonging to other providers or once that
           we've already reported. */
        PCVTGPROBELOC pProbeLocRO = &pProv->paProbeLocsRO[idxProbeLoc];
        PVTGDESCPROBE pProbeDesc  = pProbeLocRO->pProbe;
        if (pProbeDesc->idxProvider != idxProv)
            continue;

        uint32_t *pidProbe;
        if (!pProv->fUmod)
            pidProbe = (uint32_t *)&pProbeLocRO->idProbe;
        else
            pidProbe = &pProv->paR0ProbeLocs[idxProbeLoc].idProbe;
        if (*pidProbe != 0)
            continue;

         /* The function name may need to be stripped since we're using C++
            compilers for most of the code.  ASSUMES nobody are brave/stupid
            enough to use function pointer returns without typedef'ing
            properly them (e.g. signal). */
         const char *pszPrbName = vboxDtVtgGetString(pProv->pHdr, pProbeDesc->offName);
         const char *pszFunc    = pProbeLocRO->pszFunction;
         const char *psz        = strchr(pProbeLocRO->pszFunction, '(');
         size_t      cch;
         if (psz)
         {
             /* skip blanks preceeding the parameter parenthesis. */
             while (   (uintptr_t)psz > (uintptr_t)pProbeLocRO->pszFunction
                    && RT_C_IS_BLANK(psz[-1]))
                 psz--;

             /* Find the start of the function name. */
             pszFunc = psz - 1;
             while ((uintptr_t)pszFunc > (uintptr_t)pProbeLocRO->pszFunction)
             {
                 char ch = pszFunc[-1];
                 if (!RT_C_IS_ALNUM(ch) && ch != '_' && ch != ':')
                     break;
                 pszFunc--;
             }
             cch = psz - pszFunc;
         }
         else
             cch = strlen(pszFunc);
         RTStrCopyEx(pszFnNmBuf, cbFnNmBuf, pszFunc, cch);

         /* Look up the probe, if we have one in the same function, mangle
            the function name a little to avoid having to deal with having
            multiple location entries with the same probe ID. (lazy bird) */
         Assert(!*pidProbe);
         if (dtrace_probe_lookup(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName) != DTRACE_IDNONE)
         {
             RTStrPrintf(pszFnNmBuf+cch, cbFnNmBuf - cch, "-%u", pProbeLocRO->uLine);
             if (dtrace_probe_lookup(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName) != DTRACE_IDNONE)
             {
                 unsigned iOrd = 2;
                 while (iOrd < 128)
                 {
                     RTStrPrintf(pszFnNmBuf+cch, cbFnNmBuf - cch, "-%u-%u", pProbeLocRO->uLine, iOrd);
                     if (dtrace_probe_lookup(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName) == DTRACE_IDNONE)
                         break;
                     iOrd++;
                 }
                 if (iOrd >= 128)
                 {
                     LogRel(("VBoxDrv: More than 128 duplicate probe location instances at line %u in function %s [%s], probe %s\n",
                             pProbeLocRO->uLine, pProbeLocRO->pszFunction, pszFnNmBuf, pszPrbName));
                     continue;
                 }
             }
         }

         /* Create the probe. */
         AssertCompile(sizeof(*pidProbe) == sizeof(dtrace_id_t));
         *pidProbe = dtrace_probe_create(idProvider, pProv->pszModName, pszFnNmBuf, pszPrbName,
                                         1 /*aframes*/, (void *)(uintptr_t)idxProbeLoc);
         pProv->TracerData.DTrace.cProvidedProbes++;
     }

     RTMemFree(pszFnNmBuf);
     LOG_DTRACE(("%s: returns\n", __FUNCTION__));
}


/**
 * @callback_method_impl{dtrace_pops_t,dtps_enable}
 */
static int      vboxDtPOps_Enable(void *pvProv, dtrace_id_t idProbe, void *pvProbe)
{
    PSUPDRVVDTPROVIDERCORE  pProv   = (PSUPDRVVDTPROVIDERCORE)pvProv;
    LOG_DTRACE(("%s: %p / %p - %#x / %p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe));
    AssertPtrReturn(pProv->TracerData.DTrace.idProvider, EINVAL);
    RT_NOREF_PV(idProbe);

    if (!pProv->TracerData.DTrace.fZombie)
    {
        uint32_t        idxProbeLoc = (uint32_t)(uintptr_t)pvProbe;
        PVTGPROBELOC32  pProbeLocEn = (PVTGPROBELOC32)(  (uintptr_t)pProv->pvProbeLocsEn + idxProbeLoc * pProv->cbProbeLocsEn);
        PCVTGPROBELOC   pProbeLocRO = (PVTGPROBELOC)&pProv->paProbeLocsRO[idxProbeLoc];
        PCVTGDESCPROBE  pProbeDesc  = pProbeLocRO->pProbe;
        uint32_t const  idxProbe    = pProbeDesc->idxEnabled;

        if (!pProv->fUmod)
        {
            if (!pProbeLocEn->fEnabled)
            {
                pProbeLocEn->fEnabled = 1;
                ASMAtomicIncU32(&pProv->pacProbeEnabled[idxProbe]);
                ASMAtomicIncU32(&pProv->pDesc->cProbesEnabled);
                ASMAtomicIncU32(&pProv->pDesc->uSettingsSerialNo);
            }
        }
        else
        {
            /* Update kernel mode structure */
            if (!pProv->paR0ProbeLocs[idxProbeLoc].fEnabled)
            {
                pProv->paR0ProbeLocs[idxProbeLoc].fEnabled = 1;
                ASMAtomicIncU32(&pProv->paR0Probes[idxProbe].cEnabled);
                ASMAtomicIncU32(&pProv->pDesc->cProbesEnabled);
                ASMAtomicIncU32(&pProv->pDesc->uSettingsSerialNo);
            }

            /* Update user mode structure. */
            pProbeLocEn->fEnabled = 1;
            pProv->pacProbeEnabled[idxProbe] = pProv->paR0Probes[idxProbe].cEnabled;
        }
    }

    return 0;
}


/**
 * @callback_method_impl{dtrace_pops_t,dtps_disable}
 */
static void     vboxDtPOps_Disable(void *pvProv, dtrace_id_t idProbe, void *pvProbe)
{
    PSUPDRVVDTPROVIDERCORE  pProv  = (PSUPDRVVDTPROVIDERCORE)pvProv;
    AssertPtrReturnVoid(pProv);
    LOG_DTRACE(("%s: %p / %p - %#x / %p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe));
    AssertPtrReturnVoid(pProv->TracerData.DTrace.idProvider);
    RT_NOREF_PV(idProbe);

    if (!pProv->TracerData.DTrace.fZombie)
    {
        uint32_t        idxProbeLoc = (uint32_t)(uintptr_t)pvProbe;
        PVTGPROBELOC32  pProbeLocEn = (PVTGPROBELOC32)(  (uintptr_t)pProv->pvProbeLocsEn + idxProbeLoc * pProv->cbProbeLocsEn);
        PCVTGPROBELOC   pProbeLocRO = (PVTGPROBELOC)&pProv->paProbeLocsRO[idxProbeLoc];
        PCVTGDESCPROBE  pProbeDesc  = pProbeLocRO->pProbe;
        uint32_t const  idxProbe    = pProbeDesc->idxEnabled;

        if (!pProv->fUmod)
        {
            if (pProbeLocEn->fEnabled)
            {
                pProbeLocEn->fEnabled = 0;
                ASMAtomicDecU32(&pProv->pacProbeEnabled[idxProbe]);
                ASMAtomicDecU32(&pProv->pDesc->cProbesEnabled);
                ASMAtomicIncU32(&pProv->pDesc->uSettingsSerialNo);
            }
        }
        else
        {
            /* Update kernel mode structure */
            if (pProv->paR0ProbeLocs[idxProbeLoc].fEnabled)
            {
                pProv->paR0ProbeLocs[idxProbeLoc].fEnabled = 0;
                ASMAtomicDecU32(&pProv->paR0Probes[idxProbe].cEnabled);
                ASMAtomicDecU32(&pProv->pDesc->cProbesEnabled);
                ASMAtomicIncU32(&pProv->pDesc->uSettingsSerialNo);
            }

            /* Update user mode structure. */
            pProbeLocEn->fEnabled = 0;
            pProv->pacProbeEnabled[idxProbe] = pProv->paR0Probes[idxProbe].cEnabled;
        }
    }
}


/**
 * @callback_method_impl{dtrace_pops_t,dtps_getargdesc}
 */
static void     vboxDtPOps_GetArgDesc(void *pvProv, dtrace_id_t idProbe, void *pvProbe,
                                      dtrace_argdesc_t *pArgDesc)
{
    PSUPDRVVDTPROVIDERCORE  pProv  = (PSUPDRVVDTPROVIDERCORE)pvProv;
    unsigned                uArg   = pArgDesc->dtargd_ndx;
    RT_NOREF_PV(idProbe);

    pArgDesc->dtargd_ndx = DTRACE_ARGNONE;
    AssertPtrReturnVoid(pProv);
    LOG_DTRACE(("%s: %p / %p - %#x / %p uArg=%d\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe, uArg));
    AssertPtrReturnVoid(pProv->TracerData.DTrace.idProvider);

    if (!pProv->TracerData.DTrace.fZombie)
    {
        uint32_t         idxProbeLoc = (uint32_t)(uintptr_t)pvProbe;
        PCVTGPROBELOC    pProbeLocRO = (PVTGPROBELOC)&pProv->paProbeLocsRO[idxProbeLoc];
        PCVTGDESCPROBE   pProbeDesc  = pProbeLocRO->pProbe;
        PCVTGDESCARGLIST pArgList    = (PCVTGDESCARGLIST)(  (uintptr_t)pProv->pHdr
                                                          + pProv->pHdr->offArgLists
                                                          + pProbeDesc->offArgList);
        AssertReturnVoid(pProbeDesc->offArgList < pProv->pHdr->cbArgLists);

        if (uArg < pArgList->cArgs)
        {
            const char *pszType = vboxDtVtgGetString(pProv->pHdr, pArgList->aArgs[uArg].offType);
            size_t      cchType = strlen(pszType);
            if (cchType < sizeof(pArgDesc->dtargd_native))
            {
                memcpy(pArgDesc->dtargd_native, pszType, cchType + 1);
                /** @todo mapping? */
                pArgDesc->dtargd_ndx = uArg;
                LOG_DTRACE(("%s: returns dtargd_native = %s\n", __FUNCTION__, pArgDesc->dtargd_native));
                return;
            }
        }
    }
}


/**
 * @callback_method_impl{dtrace_pops_t,dtps_getargval}
 */
static uint64_t vboxDtPOps_GetArgVal(void *pvProv, dtrace_id_t idProbe, void *pvProbe,
                                     int iArg, int cFrames)
{
    PSUPDRVVDTPROVIDERCORE  pProv = (PSUPDRVVDTPROVIDERCORE)pvProv;
    AssertPtrReturn(pProv, UINT64_MAX);
    LOG_DTRACE(("%s: %p / %p - %#x / %p iArg=%d cFrames=%u\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe, iArg, cFrames));
    AssertReturn(iArg >= 5, UINT64_MAX);
    RT_NOREF_PV(idProbe); RT_NOREF_PV(cFrames);

    if (pProv->TracerData.DTrace.fZombie)
        return UINT64_MAX;

    uint32_t                idxProbeLoc = (uint32_t)(uintptr_t)pvProbe;
    PCVTGPROBELOC           pProbeLocRO = (PVTGPROBELOC)&pProv->paProbeLocsRO[idxProbeLoc];
    PCVTGDESCPROBE          pProbeDesc  = pProbeLocRO->pProbe;
    PCVTGDESCARGLIST        pArgList    = (PCVTGDESCARGLIST)(  (uintptr_t)pProv->pHdr
                                                             + pProv->pHdr->offArgLists
                                                             + pProbeDesc->offArgList);
    AssertReturn(pProbeDesc->offArgList < pProv->pHdr->cbArgLists, UINT64_MAX);

    PVBDTSTACKDATA          pData = vboxDtGetStackData();

    /*
     * Get the stack data. This is a wee bit complicated on 32-bit systems
     * since we want to support 64-bit integer arguments.
     */
    uint64_t u64Ret;
    if (iArg >= 20)
        u64Ret = UINT64_MAX;
    else if (pData->enmCaller == kVBoxDtCaller_ProbeFireKernel)
    {
#if ARCH_BITS == 64
        u64Ret = pData->u.ProbeFireKernel.pauStackArgs[iArg - 5];
#else
        if (   !pArgList->fHaveLargeArgs
            || iArg >= pArgList->cArgs)
            u64Ret = pData->u.ProbeFireKernel.pauStackArgs[iArg - 5];
        else
        {
            /* Similar to what we did for mac in when calling dtrace_probe(). */
            uint32_t offArg = 0;
            for (int i = 5; i < iArg; i++)
                if (VTG_TYPE_IS_LARGE(pArgList->aArgs[iArg].fType))
                    offArg++;
            u64Ret = pData->u.ProbeFireKernel.pauStackArgs[iArg - 5 + offArg];
            if (VTG_TYPE_IS_LARGE(pArgList->aArgs[iArg].fType))
                u64Ret |= (uint64_t)pData->u.ProbeFireKernel.pauStackArgs[iArg - 5 + offArg + 1] << 32;
        }
#endif
    }
    else if (pData->enmCaller == kVBoxDtCaller_ProbeFireUser)
    {
        int                     offArg    = pData->u.ProbeFireUser.offArg;
        PCSUPDRVTRACERUSRCTX    pCtx      = pData->u.ProbeFireUser.pCtx;
        AssertPtrReturn(pCtx, UINT64_MAX);

        if (pCtx->cBits == 32)
        {
            if (   !pArgList->fHaveLargeArgs
                || iArg >= pArgList->cArgs)
            {
                if (iArg + offArg < (int)RT_ELEMENTS(pCtx->u.X86.aArgs))
                    u64Ret = pCtx->u.X86.aArgs[iArg + offArg];
                else
                    u64Ret = UINT64_MAX;
            }
            else
            {
                for (int i = 5; i < iArg; i++)
                    if (VTG_TYPE_IS_LARGE(pArgList->aArgs[iArg].fType))
                        offArg++;
                if (offArg + iArg < (int)RT_ELEMENTS(pCtx->u.X86.aArgs))
                {
                    u64Ret = pCtx->u.X86.aArgs[iArg + offArg];
                    if (   VTG_TYPE_IS_LARGE(pArgList->aArgs[iArg].fType)
                        && offArg + iArg + 1 < (int)RT_ELEMENTS(pCtx->u.X86.aArgs))
                        u64Ret |= (uint64_t)pCtx->u.X86.aArgs[iArg + offArg + 1] << 32;
                }
                else
                    u64Ret = UINT64_MAX;
            }
        }
        else
        {
            if (iArg + offArg < (int)RT_ELEMENTS(pCtx->u.Amd64.aArgs))
                u64Ret = pCtx->u.Amd64.aArgs[iArg + offArg];
            else
                u64Ret = UINT64_MAX;
        }
    }
    else
        AssertFailedReturn(UINT64_MAX);

    LOG_DTRACE(("%s: returns %#llx\n", __FUNCTION__, u64Ret));
    return u64Ret;
}


/**
 * @callback_method_impl{dtrace_pops_t,dtps_destroy}
 */
static void    vboxDtPOps_Destroy(void *pvProv, dtrace_id_t idProbe, void *pvProbe)
{
    PSUPDRVVDTPROVIDERCORE  pProv  = (PSUPDRVVDTPROVIDERCORE)pvProv;
    AssertPtrReturnVoid(pProv);
    LOG_DTRACE(("%s: %p / %p - %#x / %p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe));
    AssertReturnVoid(pProv->TracerData.DTrace.cProvidedProbes > 0);
    AssertPtrReturnVoid(pProv->TracerData.DTrace.idProvider);

    if (!pProv->TracerData.DTrace.fZombie)
    {
        uint32_t        idxProbeLoc = (uint32_t)(uintptr_t)pvProbe;
        PCVTGPROBELOC   pProbeLocRO = (PVTGPROBELOC)&pProv->paProbeLocsRO[idxProbeLoc];
        uint32_t       *pidProbe;
        if (!pProv->fUmod)
        {
            pidProbe = (uint32_t *)&pProbeLocRO->idProbe;
            Assert(!pProbeLocRO->fEnabled);
            Assert(*pidProbe == idProbe);
        }
        else
        {
            pidProbe = &pProv->paR0ProbeLocs[idxProbeLoc].idProbe;
            Assert(!pProv->paR0ProbeLocs[idxProbeLoc].fEnabled);
            Assert(*pidProbe == idProbe); NOREF(idProbe);
        }
        *pidProbe = 0;
    }
    pProv->TracerData.DTrace.cProvidedProbes--;
}



/**
 * DTrace provider method table.
 */
static const dtrace_pops_t g_vboxDtVtgProvOps =
{
    /* .dtps_provide         = */ vboxDtPOps_Provide,
    /* .dtps_provide_module  = */ NULL,
    /* .dtps_enable          = */ vboxDtPOps_Enable,
    /* .dtps_disable         = */ vboxDtPOps_Disable,
    /* .dtps_suspend         = */ NULL,
    /* .dtps_resume          = */ NULL,
    /* .dtps_getargdesc      = */ vboxDtPOps_GetArgDesc,
    /* .dtps_getargval       = */ vboxDtPOps_GetArgVal,
    /* .dtps_usermode        = */ NULL,
    /* .dtps_destroy         = */ vboxDtPOps_Destroy
};




/*
 *
 * Support Driver Tracer Interface.
 * Support Driver Tracer Interface.
 * Support Driver Tracer Interface.
 *
 */



/**
 * interface_method_impl{SUPDRVTRACERREG,pfnProbeFireKernel}
 */
static DECLCALLBACK(void) vboxDtTOps_ProbeFireKernel(struct VTGPROBELOC *pVtgProbeLoc, uintptr_t uArg0, uintptr_t uArg1, uintptr_t uArg2,
                                                     uintptr_t uArg3, uintptr_t uArg4)
{
    AssertPtrReturnVoid(pVtgProbeLoc);
    LOG_DTRACE(("%s: %p / %p\n", __FUNCTION__, pVtgProbeLoc, pVtgProbeLoc->idProbe));
    AssertPtrReturnVoid(pVtgProbeLoc->pProbe);
    AssertPtrReturnVoid(pVtgProbeLoc->pszFunction);

    VBDT_SETUP_STACK_DATA(kVBoxDtCaller_ProbeFireKernel);

    pStackData->u.ProbeFireKernel.pauStackArgs  = &uArg4 + 1;

#if defined(RT_OS_DARWIN) && ARCH_BITS == 32
    /*
     * Convert arguments from uintptr_t to uint64_t.
     */
    PVTGDESCPROBE   pProbe   = pVtgProbeLoc->pProbe;
    AssertPtrReturnVoid(pProbe);
    PVTGOBJHDR      pVtgHdr  = (PVTGOBJHDR)((uintptr_t)pProbe + pProbe->offObjHdr);
    AssertPtrReturnVoid(pVtgHdr);
    PVTGDESCARGLIST pArgList = (PVTGDESCARGLIST)((uintptr_t)pVtgHdr + pVtgHdr->offArgLists + pProbe->offArgList);
    AssertPtrReturnVoid(pArgList);
    if (!pArgList->fHaveLargeArgs)
        dtrace_probe(pVtgProbeLoc->idProbe, uArg0, uArg1, uArg2, uArg3, uArg4);
    else
    {
        uintptr_t *auSrcArgs = &uArg0;
        uint32_t   iSrcArg   = 0;
        uint32_t   iDstArg   = 0;
        uint64_t   au64DstArgs[5];

        while (   iDstArg < RT_ELEMENTS(au64DstArgs)
               && iSrcArg < pArgList->cArgs)
        {
            au64DstArgs[iDstArg] = auSrcArgs[iSrcArg];
            if (VTG_TYPE_IS_LARGE(pArgList->aArgs[iDstArg].fType))
                au64DstArgs[iDstArg] |= (uint64_t)auSrcArgs[++iSrcArg] << 32;
            iSrcArg++;
            iDstArg++;
        }
        while (iDstArg < RT_ELEMENTS(au64DstArgs))
            au64DstArgs[iDstArg++] = auSrcArgs[iSrcArg++];

        pStackData->u.ProbeFireKernel.pauStackArgs = &auSrcArgs[iSrcArg];
        dtrace_probe(pVtgProbeLoc->idProbe, au64DstArgs[0], au64DstArgs[1], au64DstArgs[2], au64DstArgs[3], au64DstArgs[4]);
    }
#else
    dtrace_probe(pVtgProbeLoc->idProbe, uArg0, uArg1, uArg2, uArg3, uArg4);
#endif

    VBDT_CLEAR_STACK_DATA();
    LOG_DTRACE(("%s: returns\n", __FUNCTION__));
}


/**
 * interface_method_impl{SUPDRVTRACERREG,pfnProbeFireUser}
 */
static DECLCALLBACK(void) vboxDtTOps_ProbeFireUser(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, PCSUPDRVTRACERUSRCTX pCtx,
                                                   PCVTGOBJHDR pVtgHdr, PCVTGPROBELOC pProbeLocRO)
{
    LOG_DTRACE(("%s: %p / %p\n", __FUNCTION__, pCtx, pCtx->idProbe));
    AssertPtrReturnVoid(pProbeLocRO);
    AssertPtrReturnVoid(pVtgHdr);
    RT_NOREF_PV(pThis);
    RT_NOREF_PV(pSession);
    VBDT_SETUP_STACK_DATA(kVBoxDtCaller_ProbeFireUser);

    if (pCtx->cBits == 32)
    {
        pStackData->u.ProbeFireUser.pCtx   = pCtx;
        pStackData->u.ProbeFireUser.offArg = 0;

#if ARCH_BITS == 64 || defined(RT_OS_DARWIN)
        /*
         * Combine two 32-bit arguments into one 64-bit argument where needed.
         */
        PVTGDESCPROBE   pProbeDesc = pProbeLocRO->pProbe;
        AssertPtrReturnVoid(pProbeDesc);
        PVTGDESCARGLIST pArgList   = (PVTGDESCARGLIST)((uintptr_t)pVtgHdr + pVtgHdr->offArgLists + pProbeDesc->offArgList);
        AssertPtrReturnVoid(pArgList);

        if (!pArgList->fHaveLargeArgs)
            dtrace_probe(pCtx->idProbe,
                         pCtx->u.X86.aArgs[0],
                         pCtx->u.X86.aArgs[1],
                         pCtx->u.X86.aArgs[2],
                         pCtx->u.X86.aArgs[3],
                         pCtx->u.X86.aArgs[4]);
        else
        {
            uint32_t const *auSrcArgs = &pCtx->u.X86.aArgs[0];
            uint32_t        iSrcArg   = 0;
            uint32_t        iDstArg   = 0;
            uint64_t        au64DstArgs[5];

            while (   iDstArg < RT_ELEMENTS(au64DstArgs)
                   && iSrcArg < pArgList->cArgs)
            {
                au64DstArgs[iDstArg] = auSrcArgs[iSrcArg];
                if (VTG_TYPE_IS_LARGE(pArgList->aArgs[iDstArg].fType))
                    au64DstArgs[iDstArg] |= (uint64_t)auSrcArgs[++iSrcArg] << 32;
                iSrcArg++;
                iDstArg++;
            }
            while (iDstArg < RT_ELEMENTS(au64DstArgs))
                au64DstArgs[iDstArg++] = auSrcArgs[iSrcArg++];

            pStackData->u.ProbeFireUser.offArg = iSrcArg - RT_ELEMENTS(au64DstArgs);
            dtrace_probe(pCtx->idProbe, au64DstArgs[0], au64DstArgs[1], au64DstArgs[2], au64DstArgs[3], au64DstArgs[4]);
        }
#else
        dtrace_probe(pCtx->idProbe,
                     pCtx->u.X86.aArgs[0],
                     pCtx->u.X86.aArgs[1],
                     pCtx->u.X86.aArgs[2],
                     pCtx->u.X86.aArgs[3],
                     pCtx->u.X86.aArgs[4]);
#endif
    }
    else if (pCtx->cBits == 64)
    {
        pStackData->u.ProbeFireUser.pCtx   = pCtx;
        pStackData->u.ProbeFireUser.offArg = 0;
        dtrace_probe(pCtx->idProbe,
                     pCtx->u.Amd64.aArgs[0],
                     pCtx->u.Amd64.aArgs[1],
                     pCtx->u.Amd64.aArgs[2],
                     pCtx->u.Amd64.aArgs[3],
                     pCtx->u.Amd64.aArgs[4]);
    }
    else
        AssertFailed();

    VBDT_CLEAR_STACK_DATA();
    LOG_DTRACE(("%s: returns\n", __FUNCTION__));
}


/**
 * interface_method_impl{SUPDRVTRACERREG,pfnTracerOpen}
 */
static DECLCALLBACK(int) vboxDtTOps_TracerOpen(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, uint32_t uCookie,
                                               uintptr_t uArg, uintptr_t *puSessionData)
{
    if (uCookie != RT_MAKE_U32_FROM_U8('V', 'B', 'D', 'T'))
        return VERR_INVALID_MAGIC;
    if (uArg)
        return VERR_INVALID_PARAMETER;
    RT_NOREF_PV(pThis); RT_NOREF_PV(pSession);
    VBDT_SETUP_STACK_DATA(kVBoxDtCaller_Generic);

    int rc = dtrace_open((dtrace_state_t **)puSessionData, VBoxDtGetCurrentCreds());

    VBDT_CLEAR_STACK_DATA();
    return RTErrConvertFromErrno(rc);
}


/**
 * interface_method_impl{SUPDRVTRACERREG,pfnTracerClose}
 */
static DECLCALLBACK(int) vboxDtTOps_TracerIoCtl(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, uintptr_t uSessionData,
                                                uintptr_t uCmd, uintptr_t uArg, int32_t *piRetVal)
{
    AssertPtrReturn(uSessionData, VERR_INVALID_POINTER);
    RT_NOREF_PV(pThis); RT_NOREF_PV(pSession);
    VBDT_SETUP_STACK_DATA(kVBoxDtCaller_Generic);

    int rc = dtrace_ioctl((dtrace_state_t *)uSessionData, (intptr_t)uCmd, (intptr_t)uArg, piRetVal);

    VBDT_CLEAR_STACK_DATA();
    return RTErrConvertFromErrno(rc);
}


/**
 * interface_method_impl{SUPDRVTRACERREG,pfnTracerClose}
 */
static DECLCALLBACK(void) vboxDtTOps_TracerClose(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, uintptr_t uSessionData)
{
    AssertPtrReturnVoid(uSessionData);
    RT_NOREF_PV(pThis); RT_NOREF_PV(pSession);
    VBDT_SETUP_STACK_DATA(kVBoxDtCaller_Generic);

    dtrace_close((dtrace_state_t *)uSessionData);

    VBDT_CLEAR_STACK_DATA();
}


/**
 * interface_method_impl{SUPDRVTRACERREG,pfnProviderRegister}
 */
static DECLCALLBACK(int) vboxDtTOps_ProviderRegister(PCSUPDRVTRACERREG pThis, PSUPDRVVDTPROVIDERCORE pCore)
{
    LOG_DTRACE(("%s: %p %s/%s\n", __FUNCTION__, pThis, pCore->pszModName, pCore->pszName));
    AssertReturn(pCore->TracerData.DTrace.idProvider == 0, VERR_INTERNAL_ERROR_3);
    RT_NOREF_PV(pThis);
    VBDT_SETUP_STACK_DATA(kVBoxDtCaller_Generic);

    PVTGDESCPROVIDER    pDesc = pCore->pDesc;
    dtrace_pattr_t      DtAttrs;
    vboxDtVtgConvAttr(&DtAttrs.dtpa_provider, &pDesc->AttrSelf);
    vboxDtVtgConvAttr(&DtAttrs.dtpa_mod,      &pDesc->AttrModules);
    vboxDtVtgConvAttr(&DtAttrs.dtpa_func,     &pDesc->AttrFunctions);
    vboxDtVtgConvAttr(&DtAttrs.dtpa_name,     &pDesc->AttrNames);
    vboxDtVtgConvAttr(&DtAttrs.dtpa_args,     &pDesc->AttrArguments);

    /* Note! DTrace may call us back before dtrace_register returns, so we
             have to point it to pCore->TracerData.DTrace.idProvider. */
    AssertCompile(sizeof(dtrace_provider_id_t) == sizeof(pCore->TracerData.DTrace.idProvider));
    int rc = dtrace_register(pCore->pszName,
                             &DtAttrs,
                             DTRACE_PRIV_KERNEL,
                             NULL /* cred */,
                             &g_vboxDtVtgProvOps,
                             pCore,
                             &pCore->TracerData.DTrace.idProvider);
    if (!rc)
    {
        LOG_DTRACE(("%s: idProvider=%p\n", __FUNCTION__, pCore->TracerData.DTrace.idProvider));
        AssertPtr(pCore->TracerData.DTrace.idProvider);
        rc = VINF_SUCCESS;
    }
    else
    {
        pCore->TracerData.DTrace.idProvider = 0;
        rc = RTErrConvertFromErrno(rc);
    }

    VBDT_CLEAR_STACK_DATA();
    LOG_DTRACE(("%s: returns %Rrc\n", __FUNCTION__, rc));
    return rc;
}


/**
 * interface_method_impl{SUPDRVTRACERREG,pfnProviderDeregister}
 */
static DECLCALLBACK(int) vboxDtTOps_ProviderDeregister(PCSUPDRVTRACERREG pThis, PSUPDRVVDTPROVIDERCORE pCore)
{
    uintptr_t idProvider = pCore->TracerData.DTrace.idProvider;
    LOG_DTRACE(("%s: %p / %p\n", __FUNCTION__, pThis, idProvider));
    AssertPtrReturn(idProvider, VERR_INTERNAL_ERROR_3);
    RT_NOREF_PV(pThis);
    VBDT_SETUP_STACK_DATA(kVBoxDtCaller_Generic);

    dtrace_invalidate(idProvider);
    int rc = dtrace_unregister(idProvider);
    if (!rc)
    {
        pCore->TracerData.DTrace.idProvider = 0;
        rc = VINF_SUCCESS;
    }
    else
    {
        AssertMsg(rc == EBUSY, ("%d\n", rc));
        pCore->TracerData.DTrace.fZombie = true;
        rc = VERR_TRY_AGAIN;
    }

    VBDT_CLEAR_STACK_DATA();
    LOG_DTRACE(("%s: returns %Rrc\n", __FUNCTION__, rc));
    return rc;
}


/**
 * interface_method_impl{SUPDRVTRACERREG,pfnProviderDeregisterZombie}
 */
static DECLCALLBACK(int) vboxDtTOps_ProviderDeregisterZombie(PCSUPDRVTRACERREG pThis, PSUPDRVVDTPROVIDERCORE pCore)
{
    uintptr_t idProvider = pCore->TracerData.DTrace.idProvider;
    LOG_DTRACE(("%s: %p / %p\n", __FUNCTION__, pThis, idProvider));
    AssertPtrReturn(idProvider, VERR_INTERNAL_ERROR_3);
    Assert(pCore->TracerData.DTrace.fZombie);
    RT_NOREF_PV(pThis);
    VBDT_SETUP_STACK_DATA(kVBoxDtCaller_Generic);

    int rc = dtrace_unregister(idProvider);
    if (!rc)
    {
        pCore->TracerData.DTrace.idProvider = 0;
        rc = VINF_SUCCESS;
    }
    else
    {
        AssertMsg(rc == EBUSY, ("%d\n", rc));
        rc = VERR_TRY_AGAIN;
    }

    VBDT_CLEAR_STACK_DATA();
    LOG_DTRACE(("%s: returns %Rrc\n", __FUNCTION__, rc));
    return rc;
}



/**
 * The tracer registration record of the VBox DTrace implementation
 */
static SUPDRVTRACERREG g_VBoxDTraceReg =
{
    SUPDRVTRACERREG_MAGIC,
    SUPDRVTRACERREG_VERSION,
    vboxDtTOps_ProbeFireKernel,
    vboxDtTOps_ProbeFireUser,
    vboxDtTOps_TracerOpen,
    vboxDtTOps_TracerIoCtl,
    vboxDtTOps_TracerClose,
    vboxDtTOps_ProviderRegister,
    vboxDtTOps_ProviderDeregister,
    vboxDtTOps_ProviderDeregisterZombie,
    SUPDRVTRACERREG_MAGIC
};



/**
 * Module termination code.
 *
 * @param   hMod            Opque module handle.
 */
DECLEXPORT(void) ModuleTerm(void *hMod)
{
    SUPR0TracerDeregisterImpl(hMod, NULL);
    dtrace_detach();
    vboxDtTermThreadDb();
}


/**
 * Module initialization code.
 *
 * @param   hMod            Opque module handle.
 */
DECLEXPORT(int)  ModuleInit(void *hMod)
{
    int rc = vboxDtInitThreadDb();
    if (RT_SUCCESS(rc))
    {
        rc = dtrace_attach();
        if (rc == DDI_SUCCESS)
        {
            rc = SUPR0TracerRegisterImpl(hMod, NULL, &g_VBoxDTraceReg, &g_pVBoxDTraceHlp);
            if (RT_SUCCESS(rc))
                return rc;

            dtrace_detach();
        }
        else
        {
            SUPR0Printf("dtrace_attach -> %d\n", rc);
            rc = VERR_INTERNAL_ERROR_5;
        }
        vboxDtTermThreadDb();
    }
    else
        SUPR0Printf("vboxDtInitThreadDb -> %d\n", rc);

    return rc;
}

