SUPDrvTracer.cpp revision 31919eb765832d55d5ed9f713c2d61946f9860ed
/* $Id$ */
/** @file
* VBoxDrv - The VirtualBox Support Driver - Tracer Interface.
*/
/*
* 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
#define SUPDRV_AGNOSTIC
#include "SUPDrvInternal.h"
#include <iprt/semaphore.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** Pointer to a user tracer module registration record. */
typedef struct SUPDRVTRACERUMOD *PSUPDRVTRACERUMOD;
/**
* Data for a tracepoint provider.
*/
typedef struct SUPDRVTPPROVIDER
{
/** The entry in the provider list for this image. */
/** The entry in the per session provider list for this image. */
/** The core structure. */
/** 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 user tracepoint module associated with this provider. NULL if
* pImage is set. */
/** Used to indicate that we've called pfnProviderDeregistered already and it
* failed because the provider was busy. Next time we must try
* pfnProviderDeregisterZombie.
*
* @remarks This does not necessiarly mean the provider is in the zombie
* list. See supdrvTracerCommonDeregisterImpl. */
bool fZombie;
/** Set if the provider has been successfully registered with the
* tracer. */
bool fRegistered;
/** The provider name (for logging purposes). */
char szName[1];
/** Pointer to the data for a tracepoint provider. */
typedef SUPDRVTPPROVIDER *PSUPDRVTPPROVIDER;
/**
* User tracer module VTG data copy.
*/
typedef struct SUPDRVVTGCOPY
{
/** Magic (SUDPRVVTGCOPY_MAGIC). */
/** Refernece counter (we expect to share a lot of these). */
/** The size of the */
/** Image type flags. */
/** Hash list entry (SUPDRVDEVEXT::aTrackerUmodHash). */
/** The VTG object header.
* The rest of the data follows immediately afterwards. First the object,
* then the probe locations and finally the probe location string table. All
* pointers are fixed up to point within this data. */
/** Pointer to a VTG object copy. */
typedef SUPDRVVTGCOPY *PSUPDRVVTGCOPY;
/** Magic value for SUPDRVVTGCOPY. */
/**
* User tracer module registration record.
*/
typedef struct SUPDRVTRACERUMOD
{
/** Magic (SUPDRVTRACERUMOD_MAGIC). */
/** List entry. This is anchored in SUPDRVSESSION::UmodList. */
/** The address of the ring-3 VTG header. */
/** Pointer to the ring-0 copy of the VTG data. */
/** The memory object that locks down the user memory. */
/** The memory object that maps the locked memory into kernel space. */
/** Pointer to the probe enabled-count array within the mapping. */
/** Pointer to the probe location array within the mapping. */
void *pvProbeLocs;
/** The address of the ring-3 probe locations. */
/** The lookup table index. */
/** The module bit count. */
/** The size of a probe location record. */
/** The number of probe locations. */
/** Ring-0 probe location info. */
/** Magic value for SUPDRVVTGCOPY. */
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** Simple SUPR0Printf-style logging. */
#ifdef DEBUG_bird
#else
# define LOG_TRACER(a_Args) do { } while (0)
#endif
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** The address of the current probe fire routine for kernel mode. */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Validates a VTG string against length and characterset limitations.
*
* @returns VINF_SUCCESS, VERR_SUPDRV_VTG_BAD_STRING or
* VERR_SUPDRV_VTG_STRING_TOO_LONG.
* @param psz The string.
*/
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;
}
/** Used by the validation code below. */
MY_CHECK_MSG_RET(a_Expr, ("%s: Validation failed on line " RT_XSTR(__LINE__) ": " #a_Expr "\n", __FUNCTION__), a_rc)
/** Used by the validation code below. */
/** Used by the validation code below. */
#define MY_WITHIN_IMAGE(p, rc) \
do { \
if (pbImage) \
{ \
{ \
return (rc); \
} \
} \
else if (!RT_VALID_PTR(p)) \
return (rc); \
} while (0)
/**
* Validates the VTG object header.
*
* @returns VBox status code.
* @param pVtgHdr The header.
* @param uVtgHdrAddr The address where the header is actually
* loaded.
* @param cbVtgObj The alleged size of the header.
* @param pbImage The image base, if available.
* @param cbImage The image size, if available.
* @param fUmod Whether this is a user module.
*/
static int supdrvVtgValidateHdr(PVTGOBJHDR pVtgHdr, RTUINTPTR uVtgHdrAddr, const uint8_t *pbImage, size_t cbImage, bool fUmod)
{
struct VTGAREAS
{
} const *paAreas;
unsigned cAreas;
unsigned i;
do { \
{ \
SUPR0Printf("supdrvVtgValidateHdr: " #rcBase "_TOO_FEW - cb=%#zx cMin=%#zx cbUnit=%#zx line=%u %s\n", \
} \
{ \
SUPR0Printf("supdrvVtgValidateHdr: " #rcBase "_TOO_MUCH - cb=%#zx cMax=%#zx cbUnit=%#zx line=%u %s\n", \
} \
{ \
return rcBase ## _NOT_MULTIPLE; \
} \
} while (0)
do { \
{ \
SUPR0Printf("supdrvVtgValidateHdr: " #rcBase "_OFF - off=%#x cb=%#x pVtgHdr=%p cbVtgHdr=%#zx line=%u %s\n", \
} \
{ \
} \
} while (0)
/*
* Make sure both pbImage and cbImage are NULL/0 if one if of them is.
*/
{
cbImage = 0;
}
else
{
}
{
return VERR_SUPDRV_TRACER_TOO_LARGE;
}
/*
* Set the probe location array offset and size members.
*/
if (!pVtgHdr->offProbeLocs)
{
if (u64Tmp >= UINT32_MAX)
return VERR_SUPDRV_VTG_BAD_HDR_TOO_MUCH;
return VERR_SUPDRV_VTG_BAD_HDR_PTR;
}
/*
* The non-area description fields.
*/
return VERR_SUPDRV_VTG_MAGIC;
&& ( !fUmod
return VERR_SUPDRV_VTG_BITS;
if ( pVtgHdr->au32Reserved1[0]
return VERR_SUPDRV_VTG_BAD_HDR_MISC;
return VERR_SUPDRV_VTG_BAD_HDR_MISC;
/*
* Check the individual area descriptors.
*/
MY_VALIDATE_OFF(pVtgHdr->offStrTab, pVtgHdr->cbStrTab, 4, _1M, sizeof(char), sizeof(uint8_t), VERR_SUPDRV_VTG_BAD_HDR);
MY_VALIDATE_OFF(pVtgHdr->offArgLists, pVtgHdr->cbArgLists, 1, _32K, sizeof(uint32_t), sizeof(uint32_t), VERR_SUPDRV_VTG_BAD_HDR);
MY_VALIDATE_OFF(pVtgHdr->offProbes, pVtgHdr->cbProbes, 1, _32K, sizeof(VTGDESCPROBE), sizeof(uint32_t), VERR_SUPDRV_VTG_BAD_HDR);
MY_VALIDATE_OFF(pVtgHdr->offProviders, pVtgHdr->cbProviders, 1, 16, sizeof(VTGDESCPROVIDER), sizeof(uint32_t), VERR_SUPDRV_VTG_BAD_HDR);
MY_VALIDATE_OFF(pVtgHdr->offProbeEnabled, pVtgHdr->cbProbeEnabled, 1, _32K, sizeof(uint32_t), sizeof(uint32_t), VERR_SUPDRV_VTG_BAD_HDR);
if (!fUmod)
{
}
else
{
else
/* Will check later that offProbeLocs are following closely on the
enable count array, so no need to validate the offset here. */
}
/*
* Some additional consistency checks.
*/
{
SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_MISC - uProbeLocs=%#llx uProbeLocsEnd=%#llx offProbeLocs=%#llx cbProbeLocs=%#x uVtgHdrAddr=%RTptr\n",
pVtgHdr->uProbeLocs.u64, pVtgHdr->uProbeLocsEnd.u64, pVtgHdr->offProbeLocs, pVtgHdr->cbProbeLocs, uVtgHdrAddr);
return VERR_SUPDRV_VTG_BAD_HDR_MISC;
}
{
SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_MISC - cbProbeEnabled=%#zx cbProbes=%#zx\n",
return VERR_SUPDRV_VTG_BAD_HDR_MISC;
}
/*
* Check that there are no overlapping areas. This is a little bit ugly...
*/
for (i = 0; i < cAreas; i++)
{
{
SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_MISC - overlapping areas %d and %d\n", i, i-1);
return VERR_SUPDRV_VTG_BAD_HDR_MISC;
}
}
if ( pVtgHdr->offProbeLocs > 0
{
SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_MISC - probe locations overlaps the header\n");
return VERR_SUPDRV_VTG_BAD_HDR_MISC;
}
/*
* Check that the object size is correct.
*/
{
SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_MISC - bad header size %#x, expected %#x\n",
return VERR_SUPDRV_VTG_BAD_HDR_MISC;
}
return VINF_SUCCESS;
}
/**
* Validates the VTG data.
*
* @returns VBox status code.
* @param pVtgHdr The VTG object header of the data to validate.
* @param uVtgHdrAddr The address where the header is actually
* loaded.
* @param pbImage The image base. For validating the probe
* locations.
* @param cbImage The image size to go with @a pbImage.
* @param fUmod Whether this is a user module.
*/
static int supdrvVtgValidate(PVTGOBJHDR pVtgHdr, RTUINTPTR uVtgHdrAddr, const uint8_t *pbImage, size_t cbImage, bool fUmod)
{
uintptr_t i;
int rc;
{
cbImage = 0;
}
#define MY_VALIDATE_STR(a_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.
*/
if (RT_FAILURE(rc))
return rc;
/*
* Validate the providers.
*/
while (i-- > 0)
{
MY_CHECK_RET(pProvider->iFirstProbe < pVtgHdr->cbProbeEnabled / sizeof(uint32_t), VERR_SUPDRV_VTG_BAD_PROVIDER);
MY_CHECK_RET((uint32_t)pProvider->iFirstProbe + pProvider->cProbes <= pVtgHdr->cbProbeEnabled / sizeof(uint32_t),
}
/*
* Validate probes.
*/
while (i-- > 0)
{
PCVTGDESCPROVIDER pProvider = (PCVTGDESCPROVIDER)((uintptr_t)pVtgHdr + pVtgHdr->offProviders) + pProbe->idxProvider;
PCVTGDESCARGLIST pArgList = (PCVTGDESCARGLIST)( (uintptr_t)pVtgHdr + pVtgHdr->offArgLists + pProbe->offArgList );
unsigned iArg;
bool fHaveLargeArgs;
{
SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_TRACER_BAD_ARG_FLAGS - iProbe=%u offObjHdr=%d expected %zd\n",
return VERR_SUPDRV_VTG_BAD_PROBE;
}
/* The referenced argument list. */
{
SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_TRACER_BAD_ARG_FLAGS - iProbe=%u cArgs=%u\n", i, pArgList->cArgs);
return VERR_SUPDRV_VTG_BAD_ARGLIST;
}
{
SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_TRACER_BAD_ARG_FLAGS - iProbe=%u fHaveLargeArgs=%d\n", i, pArgList->fHaveLargeArgs);
return VERR_SUPDRV_VTG_BAD_ARGLIST;
}
if ( pArgList->abReserved[0]
{
SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_TRACER_BAD_ARG_FLAGS - reserved MBZ iProbe=%u\n", i);
return VERR_SUPDRV_VTG_BAD_ARGLIST;
}
fHaveLargeArgs = false;
while (iArg-- > 0)
{
if (fType & ~VTG_TYPE_VALID_MASK)
{
SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_TRACER_BAD_ARG_FLAGS - fType=%#x iArg=%u iProbe=%u (#0)\n", fType, iArg, i);
return VERR_SUPDRV_TRACER_BAD_ARG_FLAGS;
}
{
case 0:
{
SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_TRACER_BAD_ARG_FLAGS - fType=%#x iArg=%u iProbe=%u (#1)\n", fType, iArg, i);
return VERR_SUPDRV_TRACER_BAD_ARG_FLAGS;
}
break;
case 1: case 2: case 4: case 8:
break;
default:
SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_TRACER_BAD_ARG_FLAGS - fType=%#x iArg=%u iProbe=%u (#2)\n", fType, iArg, i);
return VERR_SUPDRV_TRACER_BAD_ARG_FLAGS;
}
fHaveLargeArgs = true;
}
{
SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_TRACER_BAD_ARG_FLAGS - iProbe=%u fHaveLargeArgs=%d expected %d\n",
return VERR_SUPDRV_VTG_BAD_PROBE;
}
}
/*
* Check that pacProbeEnabled is all zeros.
*/
{
while (i-- > 0)
}
/*
* Probe locations.
*/
{
while (i-- > 0)
{
MY_CHECK_RET(offTmp / sizeof(VTGDESCPROBE) * sizeof(VTGDESCPROBE) == offTmp, VERR_SUPDRV_VTG_BAD_PROBE_LOC);
}
}
return VINF_SUCCESS;
}
/**
* 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.
*/
{
LOG_TRACER(("Freeing tracepoint provider '%s' / %p\n", pProv->szName, pProv->Core.TracerData.DTrace.idProvider));
pProv->fRegistered = false;
}
/**
* Unlinks and 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 mtxTracer.
*/
{
int rc;
{
}
rc = VINF_SUCCESS;
else
if (RT_SUCCESS(rc))
{
return;
}
LOG_TRACER(("Invalidated provider '%s' / %p and put it on the zombie list (rc=%Rrc)\n",
}
/**
* Processes the zombie list.
*
* @param pDevExt The device extension.
*/
{
RTListForEachSafe(&pDevExt->TracerProviderZombieList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
{
if (RT_SUCCESS(rc))
{
}
}
}
/**
* Unregisters all providers, including zombies, waiting for busy providers to
* go idle and unregister smoothly.
*
* This may block.
*
* @param pDevExt The device extension.
*/
{
uint32_t i;
/*
* Unregister all probes (there should only be one).
*/
{
}
/*
* Try unregister zombies now, sleep on busy ones and tracer opens.
*/
for (i = 0; ; i++)
{
bool fEmpty;
/* Zombies */
RTListForEachSafe(&pDevExt->TracerProviderZombieList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
{
int rc;
LOG_TRACER(("supdrvTracerRemoveAllProviders: Attemting to unregister '%s' / %p...\n",
if (pDevExt->pTracerOps)
else
rc = VINF_SUCCESS;
if (!rc)
{
}
else if (!(i & 0xf))
SUPR0Printf("supdrvTracerRemoveAllProviders: Waiting on busy provider '%s' / %p (rc=%d)\n",
else
LOG_TRACER(("supdrvTracerRemoveAllProviders: Failed to unregister provider '%s' / %p - rc=%d\n",
}
/* Tracer opens. */
if ( pDevExt->cTracerOpens
&& pDevExt->pTracerOps)
{
fEmpty = false;
if (!(i & 0xf))
else
}
if (fEmpty)
break;
/* Delay...*/
RTThreadSleep(1000);
}
}
/**
* Registers the VTG tracepoint providers of a driver.
*
* @returns VBox status code.
* @param pszName The driver name.
* @param pVtgHdr The VTG object header.
* @param pImage The image if applicable.
* @param pSession The session if applicable.
* @param pUmod The associated user tracepoint module if
* applicable.
* @param pszModName The module name.
*/
static int supdrvTracerRegisterVtgObj(PSUPDRVDEVEXT pDevExt, PVTGOBJHDR pVtgHdr, PSUPDRVLDRIMAGE pImage,
{
int rc;
uintptr_t i;
/*
* Validate input.
*/
if (pImage)
false /*fUmod*/);
else
if (RT_FAILURE(rc))
return rc;
/*
* Check that there aren't any obvious duplicates.
* (Yes, this isn't race free, but it's good enough for now.)
*/
if (RT_FAILURE(rc))
return rc;
{
{
{
break;
}
{
break;
}
}
}
else
{
{
{
break;
}
}
}
if (RT_FAILURE(rc))
return rc;
/*
* Register the providers.
*/
while (i-- > 0)
{
pProv = (PSUPDRVTPPROVIDER)RTMemAllocZ(RT_OFFSETOF(SUPDRVTPPROVIDER, szName[cchName + 1 + cchModName + 1]));
if (pProv)
{
if (!pUmod)
{
}
else
{
}
pProv->fRegistered = true;
if (!pUmod)
else
/*
* Do the actual registration and list manipulations while holding
* down the lock.
*/
if (RT_SUCCESS(rc))
{
if ( pDevExt->pTracerOps
&& !pDevExt->fTracerUnloading)
else
{
pProv->fRegistered = false;
rc = VINF_SUCCESS;
}
if (RT_SUCCESS(rc))
{
if (pSession)
{
pSession->cTpProviders++;
}
else
LOG_TRACER(("Registered tracepoint provider '%s' in '%s' -> %p\n",
}
else
{
LOG_TRACER(("Failed to register tracepoint provider '%s' in '%s' -> %Rrc\n",
}
}
}
else
rc = VERR_NO_MEMORY;
/*
* In case of failure, we have to undo any providers we already
* managed to register.
*/
if (RT_FAILURE(rc))
{
if (pProv)
if (pImage)
{
RTListForEachReverseSafe(&pDevExt->TracerProviderList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
{
}
}
else
{
{
}
}
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;
LOG_TRACER(("SUPR0TracerRegisterDrv: pSession=%p pVtgHdr=%p pszName=%s\n", pSession, pVtgHdr, pszName));
rc = supdrvTracerRegisterVtgObj(pSession->pDevExt, pVtgHdr, NULL /*pImage*/, pSession, NULL /*pUmod*/, pszName);
/*
* 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.
*/
AssertReturn((uintptr_t)pVtgHdr - (uintptr_t)pImage->pvImage < pImage->cbImageBits, VERR_INVALID_PARAMETER);
/*
* Do the job.
*/
rc = supdrvTracerRegisterVtgObj(pDevExt, pVtgHdr, pImage, NULL /*pSession*/, NULL /*pUmod*/, pImage->szName);
/*
* Try unregister zombies while we have a chance.
*/
return rc;
}
/**
* Registers the tracer implementation.
*
* This should be called from the ModuleInit code or from a ring-0 session.
*
* @returns VBox status code.
* @param hMod The module handle.
* @param pSession Ring-0 session handle.
* @param pReg Pointer to the tracer registration structure.
* @param ppHlp Where to return the tracer helper method table.
*/
SUPR0DECL(int) SUPR0TracerRegisterImpl(void *hMod, PSUPDRVSESSION pSession, PCSUPDRVTRACERREG pReg, PCSUPDRVTRACERHLP *ppHlp)
{
int rc;
/*
* Validate input and context.
*/
if (pImage)
{
}
else
{
}
/*
* Do the job.
*/
if (RT_SUCCESS(rc))
{
if (!pDevExt->pTracerOps)
{
rc = VINF_SUCCESS;
/*
* Iterate the already loaded modules and register their providers.
*/
{
pProv->fRegistered = true;
if (RT_FAILURE(rc2))
{
pProv->fRegistered = false;
SUPR0Printf("SUPR0TracerRegisterImpl: Failed to register provider %s::%s - rc=%d\n",
}
}
}
else
}
return rc;
}
/**
* Common tracer implementation deregistration code.
*
* The caller sets fTracerUnloading prior to calling this function.
*
* @param pDevExt The device extension structure.
*/
{
uint32_t i;
/*
* Reinstall the stub probe-fire function.
*/
/*
* Disassociate the tracer implementation from all providers.
* We will have to wait on busy providers.
*/
for (i = 0; ; i++)
{
/* Live providers. */
{
int rc;
LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Attemting to unregister '%s' / %p...\n",
if (!pProv->fRegistered)
continue;
{
if (RT_FAILURE(rc))
}
else
if (RT_SUCCESS(rc))
else
{
cZombies++;
if (!(i & 0xf))
SUPR0Printf("supdrvTracerCommonDeregisterImpl: Waiting on busy provider '%s' / %p (rc=%d)\n",
else
LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Failed to unregister provider '%s' / %p - rc=%d\n",
}
}
/* Zombies providers. */
RTListForEachSafe(&pDevExt->TracerProviderZombieList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
{
int rc;
LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Attemting to unregister '%s' / %p (zombie)...\n",
if (RT_SUCCESS(rc))
{
}
else
{
cZombies++;
if (!(i & 0xf))
SUPR0Printf("supdrvTracerCommonDeregisterImpl: Waiting on busy provider '%s' / %p (rc=%d)\n",
else
LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Failed to unregister provider '%s' / %p - rc=%d\n",
}
}
/* Tracer opens. */
if (pDevExt->cTracerOpens)
{
cZombies++;
if (!(i & 0xf))
else
}
/* Tracer calls. */
if (pDevExt->cTracerCallers)
{
cZombies++;
if (!(i & 0xf))
else
}
/* Done? */
if (cZombies == 0)
break;
/* Delay...*/
RTThreadSleep(1000);
}
/*
* Deregister the tracer implementation.
*/
pDevExt->fTracerUnloading = false;
}
/**
* Deregister a tracer implementation.
*
* This should be called from the ModuleTerm code or from a ring-0 session.
*
* @returns VBox status code.
* @param hMod The module handle.
* @param pSession Ring-0 session handle.
*/
{
int rc;
/*
* Validate input and context.
*/
if (pImage)
{
}
else
{
}
/*
* Do the job.
*/
if (RT_SUCCESS(rc))
{
if ( pImage
{
LOG_TRACER(("SUPR0TracerDeregisterImpl: Unloading ...\n"));
pDevExt->fTracerUnloading = true;
LOG_TRACER(("SUPR0TracerDeregisterImpl: ... done.\n"));
}
else
{
}
}
return rc;
}
/*
* The probe function is a bit more fun since we need tail jump optimizating.
*
* Since we cannot ship yasm sources for linux and freebsd, owing to the cursed
* rebuilding of the kernel module from scratch at install time, we have to
* deploy some ugly gcc inline assembly here.
*/
__asm__("\
.section .text \n\
\n\
.p2align 2,,3 \n\
.global SUPR0TracerFireProbe \n\
SUPR0TracerFireProbe: \n\
");
# if defined(RT_ARCH_AMD64)
__asm__(" \
movq g_pfnSupdrvProbeFireKernel(%rip), %rax \n\
jmp *%rax \n\
");
# elif defined(RT_ARCH_X86)
__asm__("\
movl g_pfnSupdrvProbeFireKernel, %eax \n\
jmp *%eax \n\
");
# else
# error "Which arch is this?"
# endif
__asm__("\
\n\
.type supdrvTracerProbeFireStub,@function \n\
.global supdrvTracerProbeFireStub \n\
supdrvTracerProbeFireStub: \n\
ret \n\
\n\
.previous \n\
");
# if 0 /* Slickedit on windows highlighting fix */
)
# endif
#endif
/**
* Module unloading hook, called after execution in the module have ceased.
*
* @param pDevExt The device extension structure.
* @param pImage The image being unloaded.
*/
{
/*
* If it is the tracer image, we have to unload all the providers.
*/
{
LOG_TRACER(("supdrvTracerModuleUnloading: Unloading tracer ...\n"));
pDevExt->fTracerUnloading = true;
LOG_TRACER(("supdrvTracerModuleUnloading: ... done.\n"));
}
else
{
/*
* Unregister all providers belonging to this image.
*/
{
}
/*
* Try unregister zombies while we have a chance.
*/
}
}
/**
* Called when a session is being cleaned up.
*
* @param pDevExt The device extension structure.
* @param pSession The session that is being torn down.
*/
{
/*
* Deregister all providers.
*/
{
}
/*
* Clean up instance data the trace may have associated with the session.
*/
if (pSession->uTracerData)
/*
* Deregister any tracer implementation.
*/
{
/*
* Free any lingering user modules. We don't bother holding the lock
* here as there shouldn't be anyone messing with the session at this
* point.
*/
{
}
}
}
{
if (!cRefs)
{
}
}
/**
* Finds a matching VTG object copy, caller owns the lock already.
*
* @returns Copy with reference. NULL if not found.
* @param pHashList The hash list to search.
* @param pHdr The VTG header (valid).
* @param cbStrTab The string table size.
* @param fFlags The user module flags.
*/
static PSUPDRVVTGCOPY supdrvVtgFindObjectCopyLocked(PRTLISTANCHOR pHashList, PCVTGOBJHDR pHdr, uint32_t cbStrTab, uint32_t fFlags)
{
{
&& HDR_EQUALS(cbObj)
&& HDR_EQUALS(cBits)
)
{
&& HDR_EQUALS(cbStrTab)
&& HDR_EQUALS(cbArgLists)
&& HDR_EQUALS(offProbes)
&& HDR_EQUALS(cbProbes)
)
)
{
return pCur;
}
}
}
return NULL;
}
/**
* Finds a matching VTG object copy.
*
* @returns Copy with reference. NULL if not found.
* @param pDevExt The device extension.
* @param pHdr The VTG header (valid).
* @param cbStrTab The string table size.
* @param fFlags The user module flags.
*/
static PSUPDRVVTGCOPY supdrvVtgFindObjectCopy(PSUPDRVDEVEXT pDevExt, PCVTGOBJHDR pHdr, uint32_t cbStrTab, uint32_t fFlags)
{
PRTLISTANCHOR pHashList = &pDevExt->aTrackerUmodHash[pHdr->Uuid.au8[3] % RT_ELEMENTS(pDevExt->aTrackerUmodHash)];
return pRet;
}
/**
* Makes a shared copy of the VTG object.
*
* @returns VBox status code.
* @param pDevExt The device extension.
* @param pVtgHdr The VTG header (valid).
* @param R3PtrVtgHdr The ring-3 VTG header address.
* @param uVtgHdrAddr The address of the VTG header in the context
* where it is actually used.
* @param R3PtrStrTab The ring-3 address of the probe location string
* table. The probe location array have offsets
* into this instead of funciton name pointers.
* @param cbStrTab The size of the probe location string table.
* @param fFlags The user module flags.
* @param pUmod The structure we've allocated to track the
* module. This have a valid kernel mapping of the
* probe location array. Upon successful return,
* the pVtgCopy member will hold the address of our
* copy (with a referenced of course).
*/
static int supdrvVtgCreateObjectCopy(PSUPDRVDEVEXT pDevExt, PCVTGOBJHDR pVtgHdr, RTR3PTR R3PtrVtgHdr, RTUINTPTR uVtgHdrAddr,
{
/*
* Calculate the space required, allocate and copy in the data.
*/
int rc;
size_t const cProbeLocs = pVtgHdr->cbProbeLocs / (pVtgHdr->cBits == 32 ? sizeof(VTGPROBELOC32) : sizeof(VTGPROBELOC64));
if (!pThis)
return VERR_NO_MEMORY;
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
uint32_t i;
/*
* Some paranoia: Overwrite the header with the copy we've already
* validated and zero terminate the string table.
*/
/*
* Set the probe location array related header members since we're
* making our own copy in a different location.
*/
/*
* Copy, convert and fix up the the probe location table.
*/
{
for (i = 0; i < cProbeLocs; i++)
{
{
break;
}
}
}
else
{
for (i = 0; i < cProbeLocs; i++)
{
{
break;
}
}
}
/*
* Validate it
*
* Note! fUmod is false as this is a kernel copy with all native
* structures.
*/
if (RT_SUCCESS(rc))
rc = supdrvVtgValidate(&pThis->Hdr, (uintptr_t)&pThis->Hdr, (uint8_t *)&pThis->Hdr, cb, false /*fUmod*/);
if (RT_SUCCESS(rc))
{
/*
* Add it to the hash list, making sure nobody raced us.
*/
if (RT_SUCCESS(rc))
{
{
return rc;
}
/*
* Someone raced us, free our copy and return the existing
* one instead.
*/
}
}
}
}
return rc;
}
/**
* Undoes what supdrvTracerUmodSetProbeIds did.
*
* @param pDevExt The device extension.
* @param pSession The current session.
* @param pUmod The user tracepoint module.
*/
static void supdrvTracerUmodClearProbeIds(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVTRACERUMOD pUmod)
{
uint32_t i;
/*
* Clear the probe IDs and disable the probes.
*/
i = pUmod->cProbeLocs;
{
while (i-- > 0)
paProbeLocs[i].idProbe = 0;
}
else
{
while (i-- > 0)
paProbeLocs[i].idProbe = 0;
}
/*
* Free the lookup table entry. We'll have to wait for the table to go
* idle to make sure there are no current users of pUmod.
*/
{
if (pSession->cTpProbesFiring > 0)
{
i = 0;
while (pSession->cTpProbesFiring > 0)
{
i++;
if (!(i & 0xff))
SUPR0Printf("supdrvTracerUmodClearProbeIds: waiting for lookup table to go idle (i=%u)\n", i);
RTThreadSleep(10);
}
}
}
}
/**
* Allocates a lookup table entry for the Umod and sets the
* VTGPROBELOC::idProbe fields in user mode.
*
* @returns VINF_SUCCESS or VERR_SUPDRV_TRACER_TOO_MANY_PROVIDERS.
* @param pDevExt The device extension.
* @param pSession The current session.
* @param pUmod The user tracepoint module.
*/
static int supdrvTracerUmodSetProbeIds(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVTRACERUMOD pUmod)
{
uint32_t i;
/*
* Allocate a lookup table entry.
*/
{
if (!pSession->apTpLookupTable[i])
{
pUmod->iLookupTable = i;
break;
}
}
/*
* Set probe IDs of the usermode probe location to indicate our lookup
* table entry as well as the probe location array entry.
*/
i = pUmod->cProbeLocs;
{
while (i-- > 0)
}
else
{
while (i-- > 0)
}
return VINF_SUCCESS;
}
{
int rc;
/*
* Validate input.
*/
return VERR_INVALID_CONTEXT;
if ( fFlags != SUP_TRACER_UMOD_FLAGS_EXE
return VERR_INVALID_PARAMETER;
if ( cbStrTab < 2
/*
* Read the VTG header into a temporary buffer and perform some simple
* validations to make sure we aren't wasting our time here.
*/
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
/*
* Check how much needs to be locked down and how many probe locations
* there are.
*/
if ( Hdr.offProbeLocs <= 0
/*
* Allocate the tracker data we keep in the session.
*/
if (!pUmod)
return VERR_NO_MEMORY;
/*
* Lock down and map the user-mode structures.
*/
rc = RTR0MemObjLockUser(&pUmod->hMemObjLock, R3PtrLock, cbLock, RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS);
if (RT_SUCCESS(rc))
{
rc = RTR0MemObjMapKernel(&pUmod->hMemObjMap, pUmod->hMemObjLock, (void *)-1, 0, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
if (RT_SUCCESS(rc))
{
/*
* Does some other process use the same module already? If so,
* share the VTG data with it. Otherwise, make a ring-0 copy it.
*/
rc = supdrvVtgCreateObjectCopy(pDevExt, &Hdr, R3PtrVtgHdr, uVtgHdrAddr, R3PtrStrTab, cbStrTab, fFlags, pUmod);
if (RT_SUCCESS(rc))
{
/*
* Grabe a place in apTpLookupTable and set the probe IDs
* accordingly.
*/
if (RT_SUCCESS(rc))
{
/*
* Register the providers.
*/
if (RT_SUCCESS(rc))
{
return VINF_SUCCESS;
}
/* bail out. */
}
}
}
}
return rc;
}
int VBOXCALL supdrvIOCtl_TracerUmodDeregister(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, RTR3PTR R3PtrVtgHdr)
{
uint32_t i;
int rc;
/*
* Validate the request.
*/
{
if ( pUmod
break;
}
if (pUmod)
{
/*
* Remove ourselves from the lookup table and clean up the ring-3 bits
* we've dirtied. We do this first to make sure no probes are firing
* when we're destroying the providers in the next step.
*/
/*
* Deregister providers related to the VTG object.
*/
{
}
/*
* Destroy the Umod object.
*/
}
else
rc = VERR_NOT_FOUND;
return rc;
}
/**
* Implementation of supdrvIOCtl_TracerUmodProbeFire and
* SUPR0TracerUmodProbeFire.
*
* @param pDevExt The device extension.
* @param pSession The calling session.
* @param pCtx The context record.
*/
static void supdrvTracerUmodProbeFire(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVTRACERUSRCTX pCtx)
{
/*
* We cannot trust user mode to hand us the right bits nor not calling us
* when disabled. So, we have to check for our selves.
*/
|| pDevExt->fTracerUnloading))
return;
return;
return;
{
{
== R3PtrProbeLoc))
{
{
&& !pDevExt->fTracerUnloading
&& pVtgCopy))
{
pDevExt->pTracerOps->pfnProbeFireUser(pDevExt->pTracerOps, pSession, pCtx, &pVtgCopy->Hdr, pProbeLocRO);
}
}
}
}
}
}
{
}
void VBOXCALL supdrvIOCtl_TracerUmodProbeFire(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVTRACERUSRCTX pCtx)
{
}
/**
* Open the tracer.
*
* @returns VBox status code
* @param pDevExt The device extension structure.
* @param pSession The current session.
* @param uCookie The tracer cookie.
* @param uArg The tracer open argument.
*/
int VBOXCALL supdrvIOCtl_TracerOpen(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, uint32_t uCookie, uintptr_t uArg)
{
int rc;
if (!pSession->uTracerData)
{
if (pDevExt->pTracerOps)
{
{
if (!pDevExt->fTracerUnloading)
{
{
pDevExt->cTracerOpens++;
rc = pDevExt->pTracerOps->pfnTracerOpen(pDevExt->pTracerOps, pSession, uCookie, uArg, &pSession->uTracerData);
if (RT_FAILURE(rc))
{
pDevExt->cTracerOpens--;
pSession->uTracerData = 0;
}
}
else
}
else
}
else
}
else
}
else
return rc;
}
/**
* Closes the tracer.
*
* @returns VBox status code.
* @param pDevExt The device extension structure.
* @param pSession The current session.
*/
{
int rc;
if (pSession->uTracerData)
{
if (pDevExt->pTracerOps)
{
{
pSession->uTracerData = 0;
rc = VINF_SUCCESS;
pDevExt->cTracerOpens--;
}
else
}
else
{
pSession->uTracerData = 0;
pDevExt->cTracerOpens--;
}
}
else
return rc;
}
/**
* Performs a tracer I/O control request.
*
* @returns VBox status code.
* @param pDevExt The device extension structure.
* @param pSession The current session.
* @param uCmd The tracer command.
* @param uArg The tracer argument.
* @param piRetVal Where to store the tracer specific return value.
*/
int VBOXCALL supdrvIOCtl_TracerIOCtl(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, uintptr_t uCmd, uintptr_t uArg, int32_t *piRetVal)
{
int rc;
*piRetVal = 0;
if (pSession->uTracerData)
{
if (pDevExt->pTracerOps)
{
if (!pDevExt->fTracerUnloading)
{
{
pDevExt->cTracerOpens++;
rc = pDevExt->pTracerOps->pfnTracerIoCtl(pDevExt->pTracerOps, pSession, uTracerData, uCmd, uArg, piRetVal);
pDevExt->cTracerOpens--;
}
else
}
else
}
else
}
else
return rc;
}
/**
* Early module initialization hook.
*
* @returns VBox status code.
* @param pDevExt The device extension structure.
*/
{
/*
* Initialize the tracer.
*/
if (RT_SUCCESS(rc))
{
uint32_t i;
/** @todo */
#ifdef VBOX_WITH_NATIVE_DTRACE
if (pDevExt->pTracerOps)
#endif
/*
* Register the provider for this module, if compiled in.
*/
#ifdef VBOX_WITH_DTRACE_R0DRV
rc = supdrvTracerRegisterVtgObj(pDevExt, &g_VTGObjHeader, NULL /*pImage*/, NULL /*pSession*/, NULL /*pUmod*/, "vboxdrv");
if (RT_SUCCESS(rc))
return rc;
#else
return VINF_SUCCESS;
#endif
}
return rc;
}
/**
* Late module termination hook.
*
* @returns VBox status code.
* @param pDevExt The device extension structure.
*/
{
LOG_TRACER(("supdrvTracerTerm\n"));
LOG_TRACER(("supdrvTracerTerm: Done\n"));
}