SUPDrv-dtrace.cpp revision f714516da18876b1836a804fc0cac5e8ff589e83
/* $Id$ */
/** @file
* VBoxDrv - The VirtualBox Support Driver - DTrace Provider.
*/
/*
* Copyright (C) 2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SUP_DRV
#include "SUPDrvInternal.h"
#ifdef RT_OS_DARWIN
#endif
#ifdef RT_OS_DARWIN
# include VBOX_PATH_MACOSX_DTRACE_H
#elif defined(RT_OS_LINUX)
/* Avoid type and define conflicts. */
# 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
2. To generate the missing entries for the dtrace module in Module.symvers
of UEK:
| 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:
| 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>
# endif
# include <linux/kallsyms.h>
/** Status code fixer (UEK uses linux convension unlike the others). */
#else
#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
#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
typedef int FNPOPS_ENABLE(void *, dtrace_id_t, void *);
#endif
/** Caller indicator. */
typedef enum VBOXDTCALLER
{
} 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). */
/** Eyecatcher no. 2 (SUPDRVDT_STACK_DATA_MAGIC2). */
/** The format of the caller specific data. */
/** Caller specific data. */
union
{
/** kVBoxDtCaller_ProbeFireKernel. */
struct
{
/** Pointer to the stack arguments of a probe function call. */
/** kVBoxDtCaller_ProbeFireUser. */
struct
{
/** The user context. */
/** The argument displacement caused by 64-bit arguments passed directly to
* dtrace_probe. */
int offArg;
} u;
/** Pointer to this structure.
* This is the final bit of integrity checking. */
struct VBDTSTACKDATA *pSelf;
/** Pointer to the on-stack thread specific data. */
typedef VBDTSTACKDATA *PVBDTSTACKDATA;
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The first magic value. */
/** The second magic value. */
/** 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) \
/** Passifies the stack data and frees up resource held within it. */
#define VBDT_CLEAR_STACK_DATA() \
do \
{ \
pStackData->u32Magic1 = 0; \
pStackData->u32Magic2 = 0; \
} 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 void (* g_pfnDTraceProbeFire)(dtrace_id_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
static dtrace_id_t (* g_pfnDTraceProbeCreate)(dtrace_provider_id_t, const char *, const char *, const char *, int, void *);
static dtrace_id_t (* g_pfnDTraceProbeLookup)(dtrace_provider_id_t, const char *, const char *, const char *);
static int (* g_pfnDTraceProviderRegister)(const char *, const dtrace_pattr_t *, uint32_t, /*cred_t*/ void *,
const dtrace_pops_t *, void *, dtrace_provider_id_t *);
static void (* g_pfnDTraceProviderInvalidate)(dtrace_provider_id_t);
static int (* g_pfnDTraceProviderUnregister)(dtrace_provider_id_t);
#define dtrace_probe g_pfnDTraceProbeFire
/** @} */
#endif
/**
* Gets the stack data.
*
* @returns Pointer to the stack data. Never NULL.
*/
static PVBDTSTACKDATA vboxDtGetStackData(void)
{
/*
* Locate the caller of probe_dtrace.
*/
for (;;)
{
return pData;
}
}
/*
*
* 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).
*/
{
}
/**
* Gets a string from the string table.
*
* @returns Pointer to the string.
* @param pVtgHdr The VTG object header.
* @param offStrTab The string table offset.
*/
{
}
/*
*
* DTrace Provider Interface.
* DTrace Provider Interface.
* DTrace Provider Interface.
*
*/
/**
* @callback_method_impl{dtrace_pops_t,dtps_provide}
*/
{
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. */
return;
/* Need a buffer for extracting the function names and mangling them in
case of collision. */
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);
{
/* Skip probe location belonging to other providers or once that
we've already reported. */
continue;
else
if (*pidProbe != 0)
continue;
/* The function name may need to be stripped since we're using C++
enough to use function pointer returns without typedef'ing
properly them (e.g. signal). */
if (psz)
{
/* skip blanks preceeding the parameter parenthesis. */
psz--;
/* Find the start of the function name. */
{
break;
pszFunc--;
}
}
else
/* 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) */
{
{
unsigned iOrd = 2;
while (iOrd < 128)
{
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",
continue;
}
}
}
/* Create the probe. */
}
}
/**
* @callback_method_impl{dtrace_pops_t,dtps_enable}
*/
{
LOG_DTRACE(("%s: %p / %p - %#x / %p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe));
{
PVTGPROBELOC32 pProbeLocEn = (PVTGPROBELOC32)( (uintptr_t)pProv->pvProbeLocsEn + idxProbeLoc * pProv->cbProbeLocsEn);
{
if (!pProbeLocEn->fEnabled)
{
}
}
else
{
/* Update kernel mode structure */
{
}
/* Update user mode structure. */
}
}
return 0;
}
/**
* @callback_method_impl{dtrace_pops_t,dtps_disable}
*/
{
LOG_DTRACE(("%s: %p / %p - %#x / %p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe));
{
PVTGPROBELOC32 pProbeLocEn = (PVTGPROBELOC32)( (uintptr_t)pProv->pvProbeLocsEn + idxProbeLoc * pProv->cbProbeLocsEn);
{
if (pProbeLocEn->fEnabled)
{
pProbeLocEn->fEnabled = 0;
}
}
else
{
/* Update kernel mode structure */
{
}
/* Update user mode structure. */
pProbeLocEn->fEnabled = 0;
}
}
}
/**
* @callback_method_impl{dtrace_pops_t,dtps_getargdesc}
*/
{
LOG_DTRACE(("%s: %p / %p - %#x / %p uArg=%d\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe, uArg));
{
+ pProbeDesc->offArgList);
{
{
/** @todo mapping? */
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
* 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.
*
*/
{
LOG_DTRACE(("%s: %p / %p - %#x / %p iArg=%d cFrames=%u\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe, iArg, cFrames));
return UINT64_MAX;
+ pProbeDesc->offArgList);
/*
* Get the stack data. This is a wee bit complicated on 32-bit systems
* since we want to support 64-bit integer arguments.
*/
if (iArg >= 20)
u64Ret = UINT64_MAX;
{
#if ARCH_BITS == 64
#else
if ( !pArgList->fHaveLargeArgs
else
{
/* Similar to what we did for mac in when calling dtrace_probe(). */
for (int i = 5; i < iArg; i++)
offArg++;
}
#endif
}
{
{
if ( !pArgList->fHaveLargeArgs
{
else
u64Ret = UINT64_MAX;
}
else
{
int i;
for (i = 5; i < iArg; i++)
offArg++;
{
}
else
u64Ret = UINT64_MAX;
}
}
else
{
else
u64Ret = UINT64_MAX;
}
}
else
return u64Ret;
}
/**
* @callback_method_impl{dtrace_pops_t,dtps_destroy}
*/
{
LOG_DTRACE(("%s: %p / %p - %#x / %p\n", __FUNCTION__, pProv, pProv->TracerData.DTrace.idProvider, idProbe, pvProbe));
{
{
}
else
{
}
*pidProbe = 0;
}
}
/**
* DTrace provider method table.
*/
static const dtrace_pops_t g_vboxDtVtgProvOps =
{
/* .dtps_provide = */ vboxDtPOps_Provide,
/* .dtps_provide_module = */ NULL,
/* .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,
{
/*
* Convert arguments from uintptr_t to uint64_t.
*/
PVTGDESCARGLIST pArgList = (PVTGDESCARGLIST)((uintptr_t)pVtgHdr + pVtgHdr->offArgLists + pProbe->offArgList);
if (!pArgList->fHaveLargeArgs)
else
{
{
iSrcArg++;
iDstArg++;
}
dtrace_probe(pVtgProbeLoc->idProbe, au64DstArgs[0], au64DstArgs[1], au64DstArgs[2], au64DstArgs[3], au64DstArgs[4]);
}
#else
#endif
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnProbeFireUser}
*/
static DECLCALLBACK(void) vboxDtTOps_ProbeFireUser(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, PCSUPDRVTRACERUSRCTX pCtx,
{
{
/*
* Combine two 32-bit arguments into one 64-bit argument where needed.
*/
PVTGDESCARGLIST pArgList = (PVTGDESCARGLIST)((uintptr_t)pVtgHdr + pVtgHdr->offArgLists + pProbeDesc->offArgList);
if (!pArgList->fHaveLargeArgs)
else
{
{
iSrcArg++;
iDstArg++;
}
dtrace_probe(pCtx->idProbe, au64DstArgs[0], au64DstArgs[1], au64DstArgs[2], au64DstArgs[3], au64DstArgs[4]);
}
#else
#endif
}
{
}
else
AssertFailed();
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnTracerOpen}
*/
static DECLCALLBACK(int) vboxDtTOps_TracerOpen(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, uint32_t uCookie,
{
*puSessionData = 0;
return VERR_NOT_SUPPORTED;
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnTracerClose}
*/
static DECLCALLBACK(int) vboxDtTOps_TracerIoCtl(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, uintptr_t uSessionData,
{
return VERR_NOT_SUPPORTED;
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnTracerClose}
*/
static DECLCALLBACK(void) vboxDtTOps_TracerClose(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, uintptr_t uSessionData)
{
return;
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnProviderRegister}
*/
static DECLCALLBACK(int) vboxDtTOps_ProviderRegister(PCSUPDRVTRACERREG pThis, PSUPDRVVDTPROVIDERCORE pCore)
{
/* Note! DTrace may call us back before dtrace_register returns, so we
have to point it to pCore->TracerData.DTrace.idProvider. */
&DtAttrs,
NULL /* cred */,
if (!rc)
{
rc = VINF_SUCCESS;
}
else
{
}
return rc;
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnProviderDeregister}
*/
static DECLCALLBACK(int) vboxDtTOps_ProviderDeregister(PCSUPDRVTRACERREG pThis, PSUPDRVVDTPROVIDERCORE pCore)
{
if (!rc)
{
rc = VINF_SUCCESS;
}
else
{
rc = VERR_TRY_AGAIN;
}
return rc;
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnProviderDeregisterZombie}
*/
static DECLCALLBACK(int) vboxDtTOps_ProviderDeregisterZombie(PCSUPDRVTRACERREG pThis, PSUPDRVVDTPROVIDERCORE pCore)
{
if (!rc)
{
rc = VINF_SUCCESS;
}
else
{
rc = VERR_TRY_AGAIN;
}
return rc;
}
/**
* The tracer registration record of the VBox DTrace implementation
*/
static SUPDRVTRACERREG g_VBoxDTraceReg =
{
};
/**
* Module initialization code.
*
* @param hMod Opque module handle.
*/
{
#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX)
/*
* Resolve the kernel symbols we need.
*/
# ifndef RT_OS_LINUX
if (RT_FAILURE(rc))
{
return NULL;
}
# endif
static const struct
{
const char *pszName;
} s_aDTraceFunctions[] =
{
};
unsigned i;
for (i = 0; i < RT_ELEMENTS(s_aDTraceFunctions); i++)
{
# ifndef RT_OS_LINUX
(void **)s_aDTraceFunctions[i].ppfn);
if (RT_FAILURE(rc))
{
SUPR0Printf("supdrvDTraceInit: Failed to resolved '%s' (rc=%Rrc, i=%u).\n", s_aDTraceFunctions[i].pszName, rc, i);
break;
}
# else
if (!ulAddr)
{
SUPR0Printf("supdrvDTraceInit: Failed to resolved '%s' (i=%u).\n", s_aDTraceFunctions[i].pszName, i);
break;
}
# endif
}
# ifndef RT_OS_LINUX
if (RT_FAILURE(rc))
return NULL;
# endif
#endif
return &g_VBoxDTraceReg;
}
#ifndef VBOX_WITH_NATIVE_DTRACE
# error "VBOX_WITH_NATIVE_DTRACE is not defined as it should"
#endif