diff options
Diffstat (limited to 'src/VBox/ExtPacks/VBoxDTrace/VBoxDTraceR0.cpp')
-rw-r--r-- | src/VBox/ExtPacks/VBoxDTrace/VBoxDTraceR0.cpp | 2173 |
1 files changed, 2173 insertions, 0 deletions
diff --git a/src/VBox/ExtPacks/VBoxDTrace/VBoxDTraceR0.cpp b/src/VBox/ExtPacks/VBoxDTrace/VBoxDTraceR0.cpp new file mode 100644 index 00000000..741998c8 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/VBoxDTraceR0.cpp @@ -0,0 +1,2173 @@ +/* $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; +} + |