SUPDrvTracer.cpp revision d13bf73bbb8a73a13fc2d2d30a0d5149afe24fef
/* $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 *
*******************************************************************************/
/**
* Data for a tracepoint provider.
*/
typedef struct SUPDRVTPPROVIDER
{
/** The entry in the 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. */
/** 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;
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#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. */
/**
* 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;
}
/**
* 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;
}
/**
* 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 DTrace provider '%s' / %p\n", pProv->szName, pProv->Core.TracerData.DTrace.idProvider));
pProv->fRegistered = false;
}
/**
* 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.
*/
for (i = 0; ; i++)
{
bool fEmpty;
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",
}
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 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 supdrvTracerRegisterVtgObj(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->fRegistered = true;
if (RT_SUCCESS(rc))
{
if ( pDevExt->pTracerOps
&& !pDevExt->fTracerUnloading)
else
{
pProv->fRegistered = false;
rc = VINF_SUCCESS;
}
if (RT_SUCCESS(rc))
{
LOG_TRACER(("Registered Tracer provider '%s' in '%s' -> %p\n",
}
else
{
}
}
}
else
rc = VERR_NO_MEMORY;
if (RT_FAILURE(rc))
{
RTListForEachReverseSafe(&pDevExt->TracerProviderList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
{
{
}
}
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;
rc = supdrvTracerRegisterVtgObj(pSession->pDevExt, pVtgHdr, _1M, NULL /*pImage*/, pSession, 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.
*/
/*
* Calculate the max VTG object size and hand it over to the common code.
*/
/*
* 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;
/*
* 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(("supdrvTracerRemoveAllProviders: 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(("supdrvTracerRemoveAllProviders: Attemting to unregister '%s' / %p (zombie)...\n",
if (RT_SUCCESS(rc))
{
}
else
{
cZombies++;
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",
}
}
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\
");
#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.
*/
{
/*
* If ring-0 session, make sure it has deregistered VTG objects and the tracer.
*/
{
{
{
}
}
}
/*
* Clean up instance data the trace may have associated with the session.
*/
if (pSession->uTracerData)
{
if ( pSession->uTracerData
&& pDevExt->pTracerOps)
pSession->uTracerData = 0;
}
}
/**
* Early module initialization hook.
*
* @returns VBox status code.
* @param pDevExt The device extension structure.
*/
{
/*
* Register a provider for this module.
*/
if (RT_SUCCESS(rc))
{
/** @todo */
#ifdef VBOX_WITH_DTRACE_R0DRV
rc = supdrvTracerRegisterVtgObj(pDevExt, &g_VTGObjHeader, _1M, NULL /*pImage*/, NULL /*pSession*/, "vboxdrv");
if (RT_SUCCESS(rc))
#endif
return rc;
}
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"));
}