SUPDrv-dtrace.cpp revision 6c93a1dc098d060c4c70ab0a5489944a0d567e30
/* $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
#else
#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 *
*******************************************************************************/
#ifdef RT_OS_DARWIN
/** @name DTrace kernel interface used on Darwin
* @{ */
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
{
for (int 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.
*/
{
#ifdef RT_OS_DARWIN
/*
* Resolve the kernel symbols we need.
*/
if (RT_FAILURE(rc))
{
return NULL;
}
static const struct
{
const char *pszName;
} s_aDTraceFunctions[] =
{
};
for (unsigned i = 0; i < RT_ELEMENTS(s_aDTraceFunctions); i++)
{
(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;
}
}
if (RT_FAILURE(rc))
return NULL;
#endif
return &g_VBoxDTraceReg;
}
#ifndef VBOX_WITH_NATIVE_DTRACE
# error "VBOX_WITH_NATIVE_DTRACE is not defined as it should"
#endif