SUPDrv-dtrace.cpp revision 870c45cce975c26ed5f57b2f9b22acd88614681b
/* $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.
*/
/** @todo Convert this to a generic tracer implementation. */
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SUP_DRV
#include "SUPDrvInternal.h"
#include <iprt/semaphore.h>
#ifdef RT_OS_DARWIN /** @todo figure this! */
#else
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Data for a provider.
*/
typedef struct SUPDRVDTPROVIDER
{
/** The entry in the provider list for this image. */
/** The provider descriptor. */
/** The VTG header. */
/** Pointer to the image this provider resides in. NULL if it's a
* driver. */
/** The session this provider is associated with if registered via
* SUPR0VtgRegisterDrv. NULL if pImage is set. */
/** The module name. */
const char *pszModName;
/** Dtrace provider attributes. */
/** The ID of this provider. */
/** The number of probes we've provided to DTrace. */
/** Set when the module is unloaded or the driver deregisters its probes. */
bool fZombie;
/** The provider name (for logging purposes). */
char szName[1];
/** Pointer to the data for a provider. */
typedef SUPDRVDTPROVIDER *PSUPDRVDTPROVIDER;
/* Seems there is some return code difference here. Keep the return code and
case it to whatever the host desires. */
#ifdef RT_OS_DARWIN
typedef void FNPOPS_ENABLE(void *, dtrace_id_t, void *);
#else
typedef int FNPOPS_ENABLE(void *, dtrace_id_t, void *);
#endif
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#if 0
#else
# define LOG_DTRACE(a_Args) do { } while (0)
#endif
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
#ifdef RT_OS_SOLARIS
#endif
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/**
* DTrace provider method table.
*/
static const dtrace_pops_t g_SupDrvDTraceProvOps =
{
/* .dtps_provide = */ supdrvDTracePOps_Provide,
/* .dtps_provide_module = */ NULL,
/* .dtps_disable = */ supdrvDTracePOps_Disable,
/* .dtps_suspend = */ NULL,
/* .dtps_resume = */ NULL,
/* .dtps_getargdesc = */ supdrvDTracePOps_GetArgDesc,
#ifdef RT_OS_SOLARIS
/* .dtps_getargval = */ supdrvDTracePOps_GetArgVal,
#else
#endif
/* .dtps_usermode = */ NULL,
/* .dtps_destroy = */ supdrvDTracePOps_Destroy
};
static int supdrvVtgValidateString(const char *psz)
{
{
if (!ch)
return VINF_SUCCESS;
if ( !RTLocCIsAlNum(ch)
&& ch != ' '
&& ch != '_'
&& ch != '-'
&& ch != '('
&& ch != ')'
&& ch != ','
&& ch != '*'
&& ch != '&'
)
{
/*RTAssertMsg2("off=%u '%s'\n", off, psz);*/
return VERR_SUPDRV_VTG_BAD_STRING;
}
}
return VERR_SUPDRV_VTG_STRING_TOO_LONG;
}
/**
* Validates the VTG data.
*
* @returns VBox status code.
* @param pVtgHdr The VTG object header of the data to validate.
* @param cbVtgObj The size of the VTG object.
* @param pbImage The image base. For validating the probe
* locations.
* @param cbImage The image size to go with @a pbImage.
*/
static int supdrvVtgValidate(PVTGOBJHDR pVtgHdr, size_t cbVtgObj, const uint8_t *pbImage, size_t cbImage)
{
uintptr_t i;
int rc;
{
cbImage = 0;
}
do { \
return rcBase ## _NOT_MULTIPLE; \
} while (0)
#define MY_WITHIN_IMAGE(p, rc) \
do { \
if (pbImage) \
{ \
return (rc); \
} \
else if (!RT_VALID_PTR(p)) \
return (rc); \
} while (0)
#define MY_VALIDATE_STR(offStrTab) \
do { \
return VERR_SUPDRV_VTG_STRTAB_OFF; \
if (rc != VINF_SUCCESS) \
return rc; \
} while (0)
#define MY_VALIDATE_ATTR(Attr) \
do { \
if ((Attr).u8Code <= (uint8_t)kVTGStability_Invalid || (Attr).u8Code >= (uint8_t)kVTGStability_End) \
return VERR_SUPDRV_VTG_BAD_ATTR; \
if ((Attr).u8Data <= (uint8_t)kVTGStability_Invalid || (Attr).u8Data >= (uint8_t)kVTGStability_End) \
return VERR_SUPDRV_VTG_BAD_ATTR; \
return VERR_SUPDRV_VTG_BAD_ATTR; \
} while (0)
/*
* The header.
*/
return VERR_SUPDRV_VTG_MAGIC;
return VERR_SUPDRV_VTG_BITS;
if (pVtgHdr->u32Reserved0)
return VERR_SUPDRV_VTG_BAD_HDR;
MY_VALIDATE_PTR(pVtgHdr->paProviders, pVtgHdr->cbProviders, 1, 16, sizeof(VTGDESCPROVIDER), VERR_SUPDRV_VTG_BAD_HDR);
MY_VALIDATE_PTR(pVtgHdr->paProbes, pVtgHdr->cbProbes, 1, _32K, sizeof(VTGDESCPROBE), VERR_SUPDRV_VTG_BAD_HDR);
MY_VALIDATE_PTR(pVtgHdr->pafProbeEnabled, pVtgHdr->cbProbeEnabled, 1, _32K, sizeof(bool), VERR_SUPDRV_VTG_BAD_HDR);
MY_VALIDATE_PTR(pVtgHdr->pachStrTab, pVtgHdr->cbStrTab, 4, _1M, sizeof(char), VERR_SUPDRV_VTG_BAD_HDR);
MY_VALIDATE_PTR(pVtgHdr->paArgLists, pVtgHdr->cbArgLists, 1, _32K, sizeof(uint32_t), VERR_SUPDRV_VTG_BAD_HDR);
return VERR_SUPDRV_VTG_BAD_HDR_PTR;
if (cbTmp < sizeof(VTGPROBELOC))
return VERR_SUPDRV_VTG_BAD_HDR_TOO_FEW;
return VERR_SUPDRV_VTG_BAD_HDR_TOO_MUCH;
return VERR_SUPDRV_VTG_BAD_HDR;
/*
* Validate the providers.
*/
while (i-- > 0)
{
return VERR_SUPDRV_VTG_BAD_PROVIDER;
if (pVtgHdr->paProviders[i].iFirstProbe + pVtgHdr->paProviders[i].cProbes > pVtgHdr->cbProbeEnabled)
return VERR_SUPDRV_VTG_BAD_PROVIDER;
return VERR_SUPDRV_VTG_BAD_PROVIDER;
}
/*
* Validate probes.
*/
while (i-- > 0)
{
unsigned iArg;
return VERR_SUPDRV_VTG_BAD_PROBE;
return VERR_SUPDRV_VTG_BAD_PROBE;
return VERR_SUPDRV_VTG_BAD_PROBE;
return VERR_SUPDRV_VTG_BAD_PROBE;
return VERR_SUPDRV_VTG_BAD_PROBE;
return VERR_SUPDRV_VTG_BAD_PROBE;
/* The referenced argument list. */
return VERR_SUPDRV_VTG_BAD_ARGLIST;
if ( pArgList->abReserved[0]
return VERR_SUPDRV_VTG_BAD_ARGLIST;
while (iArg-- > 0)
{
}
}
/*
* Check that pafProbeEnabled is all zero.
*/
i = pVtgHdr->cbProbeEnabled;
while (i-- > 0)
if (pVtgHdr->pafProbeEnabled[0])
/*
* Probe locations.
*/
while (i-- > 0)
{
return VERR_SUPDRV_VTG_BAD_PROBE_LOC;
return VERR_SUPDRV_VTG_BAD_PROBE_LOC;
return VERR_SUPDRV_VTG_BAD_PROBE_LOC;
return VERR_SUPDRV_VTG_BAD_PROBE_LOC;
return VERR_SUPDRV_VTG_BAD_PROBE_LOC;
}
return VINF_SUCCESS;
}
/**
* 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.
*/
{
}
/**
* Frees the provider structure and associated resources.
*
* @param pProv The provider to free.
*/
{
}
/**
* Deregisters a provider.
*
* If the provider is still busy, it will be put in the zombie list.
*
* @param pDevExt The device extension.
* @param pProv The provider.
*
* @remarks The caller owns mtxDTrace.
*/
{
int rc;
if (!rc)
{
return;
}
LOG_DTRACE(("Invalidate DTrace provider '%s' / %p and put it on the zombie list\n", pProv->szName, pProv->idDtProv));
}
/**
* Processes the zombie list.
*
* @param pDevExt The device extension.
*/
{
{
if (!rc)
{
}
}
}
/**
* Registers the VTG tracepoint providers of a driver.
*
* @returns VBox status code.
* @param pszName The driver name.
* @param pVtgHdr The VTG object header.
* @param pVtgObj The size of the VTG object.
* @param pImage The image if applicable.
* @param pSession The session if applicable.
* @param pszModName The module name.
*/
static int supdrvVtgRegister(PSUPDRVDEVEXT pDevExt, PVTGOBJHDR pVtgHdr, size_t cbVtgObj, PSUPDRVLDRIMAGE pImage,
{
int rc;
unsigned i;
/*
* Validate input.
*/
if (pImage)
else
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
{
{
break;
}
{
break;
}
}
if (RT_FAILURE(rc))
return rc;
/*
* Register the providers.
*/
while (i-- > 0)
{
if (pProv)
{
pProv->cProvidedProbes = 0;
NULL /* cred */,
if (!rc)
{
if (RT_SUCCESS(rc))
{
LOG_DTRACE(("Registered DTrace provider '%s' in '%s' -> %p\n", pProv->szName, pszModName, pProv->idDtProv));
}
else
}
else
}
else
rc = VERR_NO_MEMORY;
if (RT_FAILURE(rc))
{
{
{
}
}
return rc;
}
}
return VINF_SUCCESS;
}
/**
* Registers the VTG tracepoint providers of a driver.
*
* @returns VBox status code.
* @param pSession The support driver session handle.
* @param pVtgHdr The VTG header.
* @param pszName The driver name.
*/
SUPR0DECL(int) SUPR0TracerRegisterDrv(PSUPDRVSESSION pSession, PVTGOBJHDR pVtgHdr, const char *pszName)
{
int rc;
/*
* Try unregister zombies while we have a chance.
*/
return rc;
}
/**
* Deregister the VTG tracepoint providers of a driver.
*
* @param pSession The support driver session handle.
* @param pVtgHdr The VTG header.
*/
{
/*
* Search for providers belonging to this driver session.
*/
{
{
}
}
/*
* Try unregister zombies while we have a chance.
*/
}
/**
* Registers the VTG tracepoint providers of a module loaded by
* the support driver.
*
* This should be called from the ModuleInit code.
*
* @returns VBox status code.
* @param hMod The module handle.
* @param pVtgHdr The VTG header.
*/
{
int rc;
/*
* Validate input and context.
*/
/*
* Calculate the max VTG object size and hand it over to the common code.
*/
/*
* Try unregister zombies while we have a chance.
*/
return rc;
}
/**
* Module unloading hook, called after execution in the module have ceased.
*
* @param pDevExt The device extension structure.
* @param pImage The image being unloaded.
*/
{
/*
* Unregister all providers belonging to this image.
*/
{
{
}
}
/*
* Try unregister zombies while we have a chance.
*/
}
/**
* Early module initialization hook.
*
* @returns VBox status code.
* @param pDevExt The device extension structure.
* @param pFireProbeEntry Pointer to the SUPR0TracerFireProbe entry.
*/
{
/*
* Register a provider for this module.
*/
if (RT_SUCCESS(rc))
{
#ifdef RT_OS_SOLARIS
#endif
rc = supdrvVtgRegister(pDevExt, &g_VTGObjHeader, _1M, NULL /*pImage*/, NULL /*pSession*/, "vboxdrv");
if (RT_SUCCESS(rc))
return rc;
}
return rc;
}
/**
* Late module termination hook.
*
* @returns VBox status code.
* @param pDevExt The device extension structure.
*/
{
uint32_t i;
LOG_DTRACE(("supdrvVtgTerm\n"));
/*
* Unregister all probes (there should only be one).
*/
{
}
/*
* Try unregister zombies now, sleep on busy ones.
*/
for (i = 0; ; i++)
{
bool fEmpty;
{
int rc;
LOG_DTRACE(("supdrvVtgTerm: Attemting to unregister '%s' / %p...\n", pProv->szName, pProv->idDtProv));
if (!rc)
{
}
else if (!(i & 0xf))
SUPR0Printf("supdrvVtgTerm: Waiting on busy provider '%s' / %p (rc=%d)\n", pProv->szName, pProv->idDtProv, rc);
else
LOG_DTRACE(("supdrvVtgTerm: Failed to unregister provider '%s' / %p - rc=%d\n", pProv->szName, pProv->idDtProv, rc));
}
if (fEmpty)
break;
/* Delay...*/
RTThreadSleep(1000);
}
LOG_DTRACE(("supdrvVtgTerm: Done\n"));
}
/**
* @callback_method_impl{dtrace_pops_t,dtps_provide}
*/
{
char *pszFnNmBuf;
if (pDtProbeDesc)
return; /* We don't generate probes, so never mind these requests. */
return;
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.
*/
{
{
/* The function name normally needs to be stripped since we're
using C++ compilers for most of the code. ASSUMES nobody are
typedef'ing properly them. */
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) */
if (dtrace_probe_lookup(pProv->idDtProv, pProv->pszModName, pszFnNmBuf, pszPrbName) != DTRACE_IDNONE)
{
if (dtrace_probe_lookup(pProv->idDtProv, pProv->pszModName, pszFnNmBuf, pszPrbName) != DTRACE_IDNONE)
{
unsigned iOrd = 2;
while (iOrd < 128)
{
if (dtrace_probe_lookup(pProv->idDtProv, pProv->pszModName, pszFnNmBuf, pszPrbName) == DTRACE_IDNONE)
break;
iOrd++;
}
if (iOrd >= 128)
{
LogRel(("VBoxDrv: More than 128 duplicate probe location instances in file %s at line %u, function %s [%s], probe %s\n",
continue;
}
}
}
/* Create the probe. */
pProbeLoc->idProbe = dtrace_probe_create(pProv->idDtProv, pProv->pszModName, pszFnNmBuf, pszPrbName,
0 /*aframes*/, pProbeLoc);
pProv->cProvidedProbes++;
}
pProbeLoc++;
}
}
/**
* @callback_method_impl{dtrace_pops_t,dtps_enable}
*/
{
{
{
}
}
return 0;
}
/**
* @callback_method_impl{dtrace_pops_t,dtps_disable}
*/
{
{
{
}
}
}
/**
* @callback_method_impl{dtrace_pops_t,dtps_getargdesc}
*/
{
{
PVTGDESCARGLIST pArgList = (PVTGDESCARGLIST)((uintptr_t)pProv->pHdr->paArgLists + pProbeDesc->offArgList);
{
{
/** @todo mapping */
return;
}
}
}
}
#ifdef RT_OS_SOLARIS
# ifdef __cplusplus
extern "C"
#endif
/**
* @callback_method_impl{dtrace_pops_t,dtps_getargval}
*/
{
/* dtrace_getarg on AMD64 has a different opinion about how to use the
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.
WARNING! This code is subject to dtrace_getarg interface unstability! */
/** @todo File a solaris bug on dtrace_probe() + dtrace_getarg(). */
}
#endif /* RT_OS_SOLARIS */
/**
* @callback_method_impl{dtrace_pops_t,dtps_destroy}
*/
{
{
}
pProv->cProvidedProbes--;
}