summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostDrivers/Support/SUPDrv-dtrace.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostDrivers/Support/SUPDrv-dtrace.cpp')
-rw-r--r--src/VBox/HostDrivers/Support/SUPDrv-dtrace.cpp1201
1 files changed, 1201 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/Support/SUPDrv-dtrace.cpp b/src/VBox/HostDrivers/Support/SUPDrv-dtrace.cpp
new file mode 100644
index 00000000..e33f244a
--- /dev/null
+++ b/src/VBox/HostDrivers/Support/SUPDrv-dtrace.cpp
@@ -0,0 +1,1201 @@
+/* $Id: SUPDrv-dtrace.cpp $ */
+/** @file
+ * VBoxDrv - The VirtualBox Support Driver - DTrace Provider.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SUP_DRV
+#include "SUPDrvInternal.h"
+
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <VBox/VBoxTpG.h>
+
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/mem.h>
+#include <iprt/errno.h>
+#ifdef RT_OS_DARWIN
+# include <iprt/dbg.h>
+#endif
+
+#ifdef RT_OS_DARWIN
+# include VBOX_PATH_MACOSX_DTRACE_H
+#elif defined(RT_OS_LINUX)
+/* Avoid type and define conflicts. */
+# undef UINT8_MAX
+# undef UINT16_MAX
+# undef UINT32_MAX
+# undef UINT64_MAX
+# undef INT64_MAX
+# undef INT64_MIN
+# define intptr_t dtrace_intptr_t
+
+# if 0
+/* DTrace experiments with the Unbreakable Enterprise Kernel (UEK2)
+ (Oracle Linux).
+ 1. The dtrace.h here is from the dtrace module source, not
+ /usr/include/sys/dtrace.h nor /usr/include/dtrace.h.
+ 2. To generate the missing entries for the dtrace module in Module.symvers
+ of UEK:
+ nm /lib/modules/....../kernel/drivers/dtrace/dtrace.ko \
+ | grep _crc_ \
+ | sed -e 's/^......../0x/' -e 's/ A __crc_/\t/' \
+ -e 's/$/\tdrivers\/dtrace\/dtrace\tEXPORT_SYMBOL/' \
+ >> Module.symvers
+ Update: Althernative workaround (active), resolve symbols dynamically.
+ 3. No tracepoints in vboxdrv, vboxnet* or vboxpci yet. This requires yasm
+ and VBoxTpG and build time. */
+# include "dtrace.h"
+# else
+/* DTrace experiments with the Unbreakable Enterprise Kernel (UEKR3)
+ (Oracle Linux).
+ 1. To generate the missing entries for the dtrace module in Module.symvers
+ of UEK:
+ nm /lib/modules/....../kernel/drivers/dtrace/dtrace.ko \
+ | grep _crc_ \
+ | sed -e 's/^......../0x/' -e 's/ A __crc_/\t/' \
+ -e 's/$/\tdrivers\/dtrace\/dtrace\tEXPORT_SYMBOL/' \
+ >> Module.symvers
+ Update: Althernative workaround (active), resolve symbols dynamically.
+ 2. No tracepoints in vboxdrv, vboxnet* or vboxpci yet. This requires yasm
+ and VBoxTpG and build time. */
+# include <dtrace/provider.h>
+# include <dtrace/enabling.h> /* Missing from provider.h. */
+# include <dtrace/arg.h> /* Missing from provider.h. */
+# endif
+# include <linux/module.h>
+/** Status code fixer (UEK uses linux convension unlike the others). */
+# define FIX_UEK_RC(a_rc) (-(a_rc))
+#else
+# include <sys/dtrace.h>
+# undef u
+#endif
+
+
+/**
+ * The UEK DTrace port is trying to be smart and seems to have turned all
+ * errno return codes negative. While this conforms to the linux kernel way of
+ * doing things, it breaks with the way the interfaces work on Solaris and
+ * Mac OS X.
+ */
+#ifndef FIX_UEK_RC
+# define FIX_UEK_RC(a_rc) (a_rc)
+#endif
+
+
+/** @name Macros for preserving EFLAGS.AC (despair / paranoid)
+ * @remarks We have to restore it unconditionally on darwin.
+ * @{ */
+#if defined(RT_OS_DARWIN) \
+ || ( defined(RT_OS_LINUX) \
+ && (defined(CONFIG_X86_SMAP) || defined(RT_STRICT) || defined(IPRT_WITH_EFLAGS_AC_PRESERVING) ) )
+# include <iprt/asm-amd64-x86.h>
+# include <iprt/x86.h>
+# define SUPDRV_SAVE_EFL_AC() RTCCUINTREG const fSavedEfl = ASMGetFlags();
+# define SUPDRV_RESTORE_EFL_AC() ASMSetFlags(fSavedEfl)
+# define SUPDRV_RESTORE_EFL_ONLY_AC() ASMChangeFlags(~X86_EFL_AC, fSavedEfl & X86_EFL_AC)
+#else
+# define SUPDRV_SAVE_EFL_AC() do { } while (0)
+# define SUPDRV_RESTORE_EFL_AC() do { } while (0)
+# define SUPDRV_RESTORE_EFL_ONLY_AC() do { } while (0)
+#endif
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/* Seems there is some return code difference here. Keep the return code and
+ case it to whatever the host desires. */
+#ifdef RT_OS_DARWIN
+# if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
+typedef void FNPOPS_ENABLE(void *, dtrace_id_t, void *);
+# else
+typedef int FNPOPS_ENABLE(void *, dtrace_id_t, void *);
+# endif
+#else
+# if !defined(RT_OS_SOLARIS) || defined(DOF_SEC_ISLOADABLE) /* DOF_SEC_ISLOADABLE was added in the next commit after dtps_enable change signature, but it's the simplest check we can do. */
+typedef int FNPOPS_ENABLE(void *, dtrace_id_t, void *);
+# else
+typedef void FNPOPS_ENABLE(void *, dtrace_id_t, void *);
+# endif
+#endif
+
+/** Caller indicator. */
+typedef enum VBOXDTCALLER
+{
+ kVBoxDtCaller_Invalid = 0,
+ kVBoxDtCaller_Generic,
+ kVBoxDtCaller_ProbeFireUser,
+ kVBoxDtCaller_ProbeFireKernel
+} VBOXDTCALLER;
+
+
+/**
+ * Stack data planted before calling dtrace_probe so that we can easily find the
+ * stack argument later.
+ */
+typedef struct VBDTSTACKDATA
+{
+ /** Eyecatcher no. 1 (SUPDRVDT_STACK_DATA_MAGIC2). */
+ uint32_t u32Magic1;
+ /** Eyecatcher no. 2 (SUPDRVDT_STACK_DATA_MAGIC2). */
+ uint32_t u32Magic2;
+ /** The format of the caller specific data. */
+ VBOXDTCALLER enmCaller;
+ /** Caller specific data. */
+ union
+ {
+ /** kVBoxDtCaller_ProbeFireKernel. */
+ struct
+ {
+ /** 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;
+ /** Pointer to this structure.
+ * This is the final bit of integrity checking. */
+ struct VBDTSTACKDATA *pSelf;
+} VBDTSTACKDATA;
+/** Pointer to the on-stack thread specific data. */
+typedef VBDTSTACKDATA *PVBDTSTACKDATA;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The first magic value. */
+#define SUPDRVDT_STACK_DATA_MAGIC1 RT_MAKE_U32_FROM_U8('S', 'U', 'P', 'D')
+/** The second magic value. */
+#define SUPDRVDT_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 SUPDRVDT_STACK_DATA_ALIGN 32
+
+/** Plants the stack data. */
+#define VBDT_SETUP_STACK_DATA(a_enmCaller) \
+ uint8_t abBlob[sizeof(VBDTSTACKDATA) + SUPDRVDT_STACK_DATA_ALIGN - 1]; \
+ PVBDTSTACKDATA pStackData = (PVBDTSTACKDATA)( (uintptr_t)&abBlob[SUPDRVDT_STACK_DATA_ALIGN - 1] \
+ & ~(uintptr_t)(SUPDRVDT_STACK_DATA_ALIGN - 1)); \
+ pStackData->u32Magic1 = SUPDRVDT_STACK_DATA_MAGIC1; \
+ pStackData->u32Magic2 = SUPDRVDT_STACK_DATA_MAGIC2; \
+ pStackData->enmCaller = a_enmCaller; \
+ 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; \
+ } 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 *
+*********************************************************************************************************************************/
+#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
+/** @name DTrace kernel interface used on Darwin and Linux.
+ * @{ */
+static DECLCALLBACKPTR_EX(void, RT_NOTHING, g_pfnDTraceProbeFire,(dtrace_id_t, uint64_t, uint64_t, uint64_t, uint64_t,
+ uint64_t));
+static DECLCALLBACKPTR_EX(dtrace_id_t, RT_NOTHING, g_pfnDTraceProbeCreate,(dtrace_provider_id_t, const char *, const char *,
+ const char *, int, void *));
+static DECLCALLBACKPTR_EX(dtrace_id_t, RT_NOTHING, g_pfnDTraceProbeLookup,(dtrace_provider_id_t, const char *, const char *,
+ const char *));
+static DECLCALLBACKPTR_EX(int, RT_NOTHING, g_pfnDTraceProviderRegister,(const char *, const dtrace_pattr_t *, uint32_t,
+ /*cred_t*/ void *, const dtrace_pops_t *,
+ void *, dtrace_provider_id_t *));
+static DECLCALLBACKPTR_EX(void, RT_NOTHING, g_pfnDTraceProviderInvalidate,(dtrace_provider_id_t));
+static DECLCALLBACKPTR_EX(int, RT_NOTHING, g_pfnDTraceProviderUnregister,(dtrace_provider_id_t));
+
+#define dtrace_probe g_pfnDTraceProbeFire
+#define dtrace_probe_create g_pfnDTraceProbeCreate
+#define dtrace_probe_lookup g_pfnDTraceProbeLookup
+#define dtrace_register g_pfnDTraceProviderRegister
+#define dtrace_invalidate g_pfnDTraceProviderInvalidate
+#define dtrace_unregister g_pfnDTraceProviderUnregister
+
+/** For dynamical resolving and releasing. */
+static const struct
+{
+ const char *pszName;
+ uintptr_t *ppfn; /**< @note Clang 11 nothrow weirdness forced this from PFNRT * to uintptr_t *. */
+} g_aDTraceFunctions[] =
+{
+ { "dtrace_probe", (uintptr_t *)&dtrace_probe },
+ { "dtrace_probe_create", (uintptr_t *)&dtrace_probe_create },
+ { "dtrace_probe_lookup", (uintptr_t *)&dtrace_probe_lookup },
+ { "dtrace_register", (uintptr_t *)&dtrace_register },
+ { "dtrace_invalidate", (uintptr_t *)&dtrace_invalidate },
+ { "dtrace_unregister", (uintptr_t *)&dtrace_unregister },
+};
+
+/** @} */
+#endif
+
+
+/**
+ * Gets the stack data.
+ *
+ * @returns Pointer to the stack data. Never NULL.
+ */
+static PVBDTSTACKDATA vboxDtGetStackData(void)
+{
+ /*
+ * Locate the caller of probe_dtrace.
+ */
+ int volatile iDummy = 1; /* use this to get the stack address. */
+ PVBDTSTACKDATA pData = (PVBDTSTACKDATA)( ((uintptr_t)&iDummy + SUPDRVDT_STACK_DATA_ALIGN - 1)
+ & ~(uintptr_t)(SUPDRVDT_STACK_DATA_ALIGN - 1));
+ for (;;)
+ {
+ if ( pData->u32Magic1 == SUPDRVDT_STACK_DATA_MAGIC1
+ && pData->u32Magic2 == SUPDRVDT_STACK_DATA_MAGIC2
+ && pData->pSelf == pData)
+ return pData;
+ pData = (PVBDTSTACKDATA)((uintptr_t)pData + SUPDRVDT_STACK_DATA_ALIGN);
+ }
+}
+
+
+/*
+ *
+ * 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);
+ uint32_t idxProbeLoc;
+ for (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 %s 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;
+ RT_NOREF(idProbe);
+ LOG_DTRACE(("%s: %p / %p - %#x / %p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe));
+ AssertPtrReturn(pProv->TracerData.DTrace.idProvider, EINVAL);
+
+ 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);
+ RT_NOREF(idProbe);
+ LOG_DTRACE(("%s: %p / %p - %#x / %p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe));
+ AssertPtrReturnVoid(pProv->TracerData.DTrace.idProvider);
+
+ 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]);
+ ASMAtomicIncU32(&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(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}
+ *
+ *
+ * We just cook our own stuff here, using a stack marker for finding the
+ * required information. That's more reliable than subjecting oneself to the
+ * solaris bugs and 32-bit apple peculiarities.
+ *
+ *
+ * @remarks Solaris Bug
+ *
+ * dtrace_getarg on AMD64 has a different opinion about how to use the cFrames
+ * argument than dtrace_caller() and/or dtrace_getpcstack(), at least when the
+ * probe is fired by dtrace_probe() the way we do.
+ *
+ * Setting aframes to 1 when calling dtrace_probe_create gives me the right
+ * arguments, but the wrong 'caller'. Since I cannot do anything about
+ * 'caller', the only solution is this hack.
+ *
+ * Not sure why the Solaris guys hasn't seen this issue before, but maybe there
+ * isn't anyone using the default argument getter path for ring-0 dtrace_probe()
+ * calls, SDT surely isn't.
+ *
+ * @todo File a solaris bug on dtrace_probe() + dtrace_getarg().
+ *
+ *
+ * @remarks 32-bit XNU (Apple)
+ *
+ * The dtrace_probe arguments are 64-bit unsigned integers instead of uintptr_t,
+ * so we need to make an extra call.
+ *
+ */
+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);
+ RT_NOREF(idProbe, cFrames);
+ 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);
+ 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
+ {
+ int i;
+ for (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 = */ (FNPOPS_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);
+
+ SUPDRV_SAVE_EFL_AC();
+ 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 = (PVTGDESCPROBE)((PVTGPROBELOC)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();
+ SUPDRV_RESTORE_EFL_AC();
+ 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)
+{
+ RT_NOREF(pThis, pSession);
+ LOG_DTRACE(("%s: %p / %p\n", __FUNCTION__, pCtx, pCtx->idProbe));
+ AssertPtrReturnVoid(pProbeLocRO);
+ AssertPtrReturnVoid(pVtgHdr);
+
+ SUPDRV_SAVE_EFL_AC();
+ 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();
+ SUPDRV_RESTORE_EFL_AC();
+ 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)
+{
+ NOREF(pThis); NOREF(pSession); NOREF(uCookie); NOREF(uArg);
+ *puSessionData = 0;
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * 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)
+{
+ NOREF(pThis); NOREF(pSession); NOREF(uSessionData);
+ NOREF(uCmd); NOREF(uArg); NOREF(piRetVal);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * interface_method_impl{SUPDRVTRACERREG,pfnTracerClose}
+ */
+static DECLCALLBACK(void) vboxDtTOps_TracerClose(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, uintptr_t uSessionData)
+{
+ NOREF(pThis); NOREF(pSession); NOREF(uSessionData);
+ return;
+}
+
+
+/**
+ * interface_method_impl{SUPDRVTRACERREG,pfnProviderRegister}
+ */
+static DECLCALLBACK(int) vboxDtTOps_ProviderRegister(PCSUPDRVTRACERREG pThis, PSUPDRVVDTPROVIDERCORE pCore)
+{
+ RT_NOREF(pThis);
+ LOG_DTRACE(("%s: %p %s/%s\n", __FUNCTION__, pThis, pCore->pszModName, pCore->pszName));
+ AssertReturn(pCore->TracerData.DTrace.idProvider == 0, VERR_INTERNAL_ERROR_3);
+
+ 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));
+ SUPDRV_SAVE_EFL_AC();
+ int rc = dtrace_register(pCore->pszName,
+ &DtAttrs,
+ DTRACE_PRIV_KERNEL,
+ NULL /* cred */,
+ &g_vboxDtVtgProvOps,
+ pCore,
+ &pCore->TracerData.DTrace.idProvider);
+ SUPDRV_RESTORE_EFL_AC();
+ 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(FIX_UEK_RC(rc));
+ }
+
+ 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;
+ RT_NOREF(pThis);
+ LOG_DTRACE(("%s: %p / %p\n", __FUNCTION__, pThis, idProvider));
+ AssertPtrReturn(idProvider, VERR_INTERNAL_ERROR_3);
+
+ SUPDRV_SAVE_EFL_AC();
+ dtrace_invalidate(idProvider);
+ int rc = dtrace_unregister(idProvider);
+ SUPDRV_RESTORE_EFL_AC();
+ if (!rc)
+ {
+ pCore->TracerData.DTrace.idProvider = 0;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ AssertMsg(FIX_UEK_RC(rc) == EBUSY, ("%d\n", rc));
+ pCore->TracerData.DTrace.fZombie = true;
+ rc = VERR_TRY_AGAIN;
+ }
+
+ 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;
+ RT_NOREF(pThis);
+ LOG_DTRACE(("%s: %p / %p\n", __FUNCTION__, pThis, idProvider));
+ AssertPtrReturn(idProvider, VERR_INTERNAL_ERROR_3);
+ Assert(pCore->TracerData.DTrace.fZombie);
+
+ SUPDRV_SAVE_EFL_AC();
+ int rc = dtrace_unregister(idProvider);
+ SUPDRV_RESTORE_EFL_AC();
+ if (!rc)
+ {
+ pCore->TracerData.DTrace.idProvider = 0;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ AssertMsg(FIX_UEK_RC(rc) == EBUSY, ("%d\n", rc));
+ rc = VERR_TRY_AGAIN;
+ }
+
+ 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 initialization code.
+ */
+const SUPDRVTRACERREG * VBOXCALL supdrvDTraceInit(void)
+{
+#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
+ /*
+ * Resolve the kernel symbols we need.
+ */
+# ifndef RT_OS_LINUX
+ RTDBGKRNLINFO hKrnlInfo;
+ int rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0);
+ if (RT_FAILURE(rc))
+ {
+ SUPR0Printf("supdrvDTraceInit: RTR0DbgKrnlInfoOpen failed with rc=%d.\n", rc);
+ return NULL;
+ }
+# endif
+
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(g_aDTraceFunctions); i++)
+ {
+# ifndef RT_OS_LINUX
+ rc = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, g_aDTraceFunctions[i].pszName,
+ (void **)g_aDTraceFunctions[i].ppfn);
+ if (RT_FAILURE(rc))
+ {
+ SUPR0Printf("supdrvDTraceInit: Failed to resolved '%s' (rc=%Rrc, i=%u).\n", g_aDTraceFunctions[i].pszName, rc, i);
+ break;
+ }
+# else /* RT_OS_LINUX */
+ uintptr_t ulAddr = (uintptr_t)__symbol_get(g_aDTraceFunctions[i].pszName);
+ if (!ulAddr)
+ {
+ SUPR0Printf("supdrvDTraceInit: Failed to resolved '%s' (i=%u).\n", g_aDTraceFunctions[i].pszName, i);
+ while (i-- > 0)
+ {
+ __symbol_put(g_aDTraceFunctions[i].pszName);
+ *g_aDTraceFunctions[i].ppfn = NULL;
+ }
+ return NULL;
+ }
+ *g_aDTraceFunctions[i].ppfn = (PFNRT)ulAddr;
+# endif /* RT_OS_LINUX */
+ }
+
+# ifndef RT_OS_LINUX
+ RTR0DbgKrnlInfoRelease(hKrnlInfo);
+ if (RT_FAILURE(rc))
+ return NULL;
+# endif
+#endif
+
+ return &g_VBoxDTraceReg;
+}
+
+/**
+ * Module teardown code.
+ */
+void VBOXCALL supdrvDTraceFini(void)
+{
+#ifdef RT_OS_LINUX
+ /* Release the references. */
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(g_aDTraceFunctions); i++)
+ if (*g_aDTraceFunctions[i].ppfn)
+ {
+ __symbol_put(g_aDTraceFunctions[i].pszName);
+ *g_aDTraceFunctions[i].ppfn = NULL;
+ }
+#endif
+}
+
+#ifndef VBOX_WITH_NATIVE_DTRACE
+# error "VBOX_WITH_NATIVE_DTRACE is not defined as it should"
+#endif
+