SUPDrvTracer.cpp revision a167f8813b459e30d4c34e76f4305bcc2f24adde
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync/* $Id$ */
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync/** @file
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * VBoxDrv - The VirtualBox Support Driver - Tracer Interface.
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync */
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync/*
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * Copyright (C) 2012 Oracle Corporation
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync *
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * available from http://www.virtualbox.org. This file is free software;
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * you can redistribute it and/or modify it under the terms of the GNU
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * General Public License (GPL) as published by the Free Software
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync *
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * The contents of this file may alternatively be used under the terms
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * of the Common Development and Distribution License Version 1.0
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * VirtualBox OSE distribution, in which case the provisions of the
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * CDDL are applicable instead of those of the GPL.
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync *
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * You may elect to license modified versions of this file under the
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * terms and conditions of either the GPL or the CDDL or both.
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync */
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync/*******************************************************************************
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync* Header Files *
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync*******************************************************************************/
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync#define LOG_GROUP LOG_GROUP_SUP_DRV
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync#define SUPDRV_AGNOSTIC
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync#include "SUPDrvInternal.h"
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync#include <VBox/err.h>
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync#include <VBox/log.h>
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync#include <VBox/VBoxTpG.h>
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync#include <iprt/assert.h>
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync#include <iprt/ctype.h>
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync#include <iprt/list.h>
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync#include <iprt/mem.h>
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync#include <iprt/semaphore.h>
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync#include <iprt/thread.h>
6420f75ffc86ab6494eb5e95418f0c95e71e8068vboxsync#include <iprt/param.h>
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync#include <iprt/uuid.h>
6420f75ffc86ab6494eb5e95418f0c95e71e8068vboxsync
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync/*******************************************************************************
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync* Structures and Typedefs *
6420f75ffc86ab6494eb5e95418f0c95e71e8068vboxsync*******************************************************************************/
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync/** Pointer to a user tracer module registration record. */
6420f75ffc86ab6494eb5e95418f0c95e71e8068vboxsynctypedef struct SUPDRVTRACERUMOD *PSUPDRVTRACERUMOD;
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync/**
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * Data for a tracepoint provider.
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync */
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsynctypedef struct SUPDRVTPPROVIDER
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync{
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync /** The entry in the provider list for this image. */
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync RTLISTNODE ListEntry;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** The entry in the per session provider list for this image. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync RTLISTNODE SessionListEntry;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** The core structure. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync SUPDRVVDTPROVIDERCORE Core;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** Pointer to the image this provider resides in. NULL if it's a
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * driver. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync PSUPDRVLDRIMAGE pImage;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** The session this provider is associated with if registered via
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * SUPR0VtgRegisterDrv. NULL if pImage is set. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync PSUPDRVSESSION pSession;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** The user tracepoint module associated with this provider. NULL if
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * pImage is set. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync PSUPDRVTRACERUMOD pUmod;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** Used to indicate that we've called pfnProviderDeregistered already and it
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * failed because the provider was busy. Next time we must try
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * pfnProviderDeregisterZombie.
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync *
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * @remarks This does not necessiarly mean the provider is in the zombie
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * list. See supdrvTracerCommonDeregisterImpl. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync bool fZombie;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** Set if the provider has been successfully registered with the
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * tracer. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync bool fRegistered;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** The provider name (for logging purposes). */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync char szName[1];
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync} SUPDRVTPPROVIDER;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync/** Pointer to the data for a tracepoint provider. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsynctypedef SUPDRVTPPROVIDER *PSUPDRVTPPROVIDER;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync/**
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * User tracer module VTG data copy.
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsynctypedef struct SUPDRVVTGCOPY
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync{
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** Magic (SUDPRVVTGCOPY_MAGIC). */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync uint32_t u32Magic;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** Refernece counter (we expect to share a lot of these). */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync uint32_t cRefs;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** The size of the */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync uint32_t cbStrTab;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** Image type flags. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync uint32_t fFlags;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** Hash list entry (SUPDRVDEVEXT::aTrackerUmodHash). */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync RTLISTNODE ListEntry;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** The VTG object header.
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * The rest of the data follows immediately afterwards. First the object,
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * then the probe locations and finally the probe location string table. All
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * pointers are fixed up to point within this data. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync VTGOBJHDR Hdr;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync} SUPDRVVTGCOPY;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync/** Pointer to a VTG object copy. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsynctypedef SUPDRVVTGCOPY *PSUPDRVVTGCOPY;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync/** Magic value for SUPDRVVTGCOPY. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync#define SUDPRVVTGCOPY_MAGIC UINT32_C(0x00080386)
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync/**
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * User tracer module registration record.
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsynctypedef struct SUPDRVTRACERUMOD
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync{
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** Magic (SUPDRVTRACERUMOD_MAGIC). */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync uint32_t u32Magic;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** List entry. This is anchored in SUPDRVSESSION::UmodList. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync RTLISTNODE ListEntry;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** The address of the ring-3 VTG header. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync RTR3PTR R3PtrVtgHdr;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** Pointer to the ring-0 copy of the VTG data. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync PSUPDRVVTGCOPY pVtgCopy;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** The memory object that locks down the user memory. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync RTR0MEMOBJ hMemObjLock;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** The memory object that maps the locked memory into kernel space. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync RTR0MEMOBJ hMemObjMap;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** Pointer to the probe enabled-count array within the mapping. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync uint32_t *pacProbeEnabled;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** Pointer to the probe location array within the mapping. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync void *pvProbeLocs;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** The address of the ring-3 probe locations. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync RTR3PTR R3PtrProbeLocs;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync /** The lookup table index. */
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync uint8_t iLookupTable;
/** The module bit count. */
uint8_t cBits;
/** The size of a probe location record. */
uint8_t cbProbeLoc;
/** The number of probe locations. */
uint32_t cProbeLocs;
/** Ring-0 probe location info. */
SUPDRVPROBELOC aProbeLocs[1];
} SUPDRVTRACERUMOD;
/** Magic value for SUPDRVVTGCOPY. */
#define SUPDRVTRACERUMOD_MAGIC UINT32_C(0x00080486)
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** Simple SUPR0Printf-style logging. */
#ifdef DEBUG_bird
# define LOG_TRACER(a_Args) SUPR0Printf a_Args
#else
# define LOG_TRACER(a_Args) do { } while (0)
#endif
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** The address of the current probe fire routine for kernel mode. */
PFNRT g_pfnSupdrvProbeFireKernel = supdrvTracerProbeFireStub;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static void supdrvVtgReleaseObjectCopy(PSUPDRVDEVEXT pDevExt, PSUPDRVVTGCOPY pThis);
/**
* 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)
{
size_t off = 0;
while (off < _4K)
{
char const ch = psz[off++];
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. */
#define MY_CHECK_RET(a_Expr, a_rc) \
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. */
#define MY_CHECK_MSG_RET(a_Expr, a_PrintfArgs, a_rc) \
do { if (RT_UNLIKELY(!(a_Expr))) { SUPR0Printf a_PrintfArgs; return (a_rc); } } while (0)
/** Used by the validation code below. */
#define MY_WITHIN_IMAGE(p, rc) \
do { \
if (pbImage) \
{ \
if ((uintptr_t)(p) - (uintptr_t)pbImage > cbImage) \
{ \
SUPR0Printf("supdrvVtgValidate: " #rc " - p=%p pbImage=%p cbImage=%#zxline=%u %s\n", \
p, pbImage, cbImage, #p); \
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
{
uint32_t off;
uint32_t cb;
} const *paAreas;
unsigned cAreas;
unsigned i;
uint32_t cbVtgObj;
uint32_t off;
#define MY_VALIDATE_SIZE(cb, cMin, cMax, cbUnit, rcBase) \
do { \
if ((cb) < (cMin) * (cbUnit)) \
{ \
SUPR0Printf("supdrvVtgValidateHdr: " #rcBase "_TOO_FEW - cb=%#zx cMin=%#zx cbUnit=%#zx line=%u %s\n", \
(size_t)(cb), (size_t)(cMin), (size_t)cbUnit, __LINE__, #cb); \
return rcBase ## _TOO_FEW; \
} \
if ((cb) >= (cMax) * (cbUnit)) \
{ \
SUPR0Printf("supdrvVtgValidateHdr: " #rcBase "_TOO_MUCH - cb=%#zx cMax=%#zx cbUnit=%#zx line=%u %s\n", \
(size_t)(cb), (size_t)(cMax), (size_t)cbUnit, __LINE__, #cb); \
return rcBase ## _TOO_MUCH; \
} \
if ((cb) / (cbUnit) * (cbUnit) != (cb)) \
{ \
SUPR0Printf("supdrvVtgValidateHdr: " #rcBase "_NOT_MULTIPLE - cb=%#zx cbUnit=%#zx line=%u %s\n", \
(size_t)(cb), (size_t)cbUnit, __LINE__, #cb); \
return rcBase ## _NOT_MULTIPLE; \
} \
} while (0)
#define MY_VALIDATE_OFF(off, cb, cMin, cMax, cbUnit, cbAlign, rcBase) \
do { \
if ( (cb) >= cbVtgObj \
|| off > cbVtgObj - (cb) ) \
{ \
SUPR0Printf("supdrvVtgValidateHdr: " #rcBase "_OFF - off=%#x cb=%#x pVtgHdr=%p cbVtgHdr=%#zx line=%u %s\n", \
(off), (cb), pVtgHdr, cbVtgObj, __LINE__, #off); \
return rcBase ## _OFF; \
} \
if (RT_ALIGN(off, cbAlign) != (off)) \
{ \
SUPR0Printf("supdrvVtgValidateHdr: " #rcBase "_OFF - off=%#x align=%#zx line=%u %s\n", \
(off), (size_t)(cbAlign), __LINE__, #off); \
return rcBase ## _OFF; \
} \
MY_VALIDATE_SIZE(cb, cMin, cMax, cbUnit, rcBase); \
} while (0)
/*
* Make sure both pbImage and cbImage are NULL/0 if one if of them is.
*/
if (!pbImage || !cbImage)
{
pbImage = NULL;
cbImage = 0;
cbVtgObj = pVtgHdr->cbObj;
}
else
{
MY_WITHIN_IMAGE(pVtgHdr, VERR_SUPDRV_VTG_BAD_HDR_PTR);
cbVtgObj = pVtgHdr->cbObj;
MY_WITHIN_IMAGE((uint8_t *)pVtgHdr + cbVtgObj - 1, VERR_SUPDRV_VTG_BAD_HDR_PTR);
}
if (cbVtgObj > _1M)
{
SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_TRACER_TOO_LARGE - cbVtgObj=%#x\n", cbVtgObj);
return VERR_SUPDRV_TRACER_TOO_LARGE;
}
/*
* Set the probe location array offset and size members.
*/
if (!pVtgHdr->offProbeLocs)
{
uint64_t u64Tmp = pVtgHdr->uProbeLocsEnd.u64 - pVtgHdr->uProbeLocs.u64;
if (u64Tmp >= UINT32_MAX)
{
SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_TOO_MUCH - u64Tmp=%#llx ProbeLocs=%#llx ProbeLocsEnd=%#llx\n",
u64Tmp, pVtgHdr->uProbeLocs.u64, pVtgHdr->uProbeLocsEnd.u64);
return VERR_SUPDRV_VTG_BAD_HDR_TOO_MUCH;
}
pVtgHdr->cbProbeLocs = (uint32_t)u64Tmp;
u64Tmp = pVtgHdr->uProbeLocs.u64 - uVtgHdrAddr;
#ifdef RT_OS_DARWIN
/* The loader and/or ld64-97.17 seems not to generate fixups for our
__VTGObj section. Detect this by comparing them with the
u64VtgObjSectionStart member and assume max image size of 4MB. */
if ( (int64_t)u64Tmp != (int32_t)u64Tmp
&& pVtgHdr->u64VtgObjSectionStart != uVtgHdrAddr
&& pVtgHdr->u64VtgObjSectionStart < _4M
&& pVtgHdr->uProbeLocsEnd.u64 < _4M
&& !fUmod)
{
uint64_t offDelta = uVtgHdrAddr - pVtgHdr->u64VtgObjSectionStart;
pVtgHdr->uProbeLocs.u64 += offDelta;
pVtgHdr->uProbeLocsEnd.u64 += offDelta;
u64Tmp += offDelta;
}
#endif
if ((int64_t)u64Tmp != (int32_t)u64Tmp)
{
SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_PTR - u64Tmp=%#llx uProbeLocs=%#llx uVtgHdrAddr=%RTptr\n",
u64Tmp, pVtgHdr->uProbeLocs.u64, uVtgHdrAddr);
return VERR_SUPDRV_VTG_BAD_HDR_PTR;
}
pVtgHdr->offProbeLocs = (int32_t)u64Tmp;
}
/*
* The non-area description fields.
*/
if (memcmp(pVtgHdr->szMagic, VTGOBJHDR_MAGIC, sizeof(pVtgHdr->szMagic)))
return VERR_SUPDRV_VTG_MAGIC;
if ( pVtgHdr->cBits != ARCH_BITS
&& ( !fUmod
|| ( pVtgHdr->cBits != 32
&& pVtgHdr->cBits != 64)) )
return VERR_SUPDRV_VTG_BITS;
MY_CHECK_RET(pVtgHdr->au32Reserved1[0] == 0, VERR_SUPDRV_VTG_BAD_HDR_MISC);
MY_CHECK_RET(pVtgHdr->au32Reserved1[1] == 0, VERR_SUPDRV_VTG_BAD_HDR_MISC);
MY_CHECK_RET(!RTUuidIsNull(&pVtgHdr->Uuid), 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)
{
MY_WITHIN_IMAGE(pVtgHdr->uProbeLocs.p, VERR_SUPDRV_VTG_BAD_HDR_PTR);
MY_WITHIN_IMAGE(pVtgHdr->uProbeLocsEnd.p, VERR_SUPDRV_VTG_BAD_HDR_PTR);
MY_VALIDATE_SIZE( pVtgHdr->cbProbeLocs, 1, _128K, sizeof(VTGPROBELOC), VERR_SUPDRV_VTG_BAD_HDR);
}
else
{
if (pVtgHdr->cBits == 32)
MY_VALIDATE_SIZE( pVtgHdr->cbProbeLocs, 1, _8K, sizeof(VTGPROBELOC32), VERR_SUPDRV_VTG_BAD_HDR);
else
MY_VALIDATE_SIZE( pVtgHdr->cbProbeLocs, 1, _8K, sizeof(VTGPROBELOC64), VERR_SUPDRV_VTG_BAD_HDR);
/* 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.
*/
if ( pVtgHdr->uProbeLocsEnd.u64 - pVtgHdr->uProbeLocs.u64 != pVtgHdr->cbProbeLocs
|| (int64_t)(pVtgHdr->uProbeLocs.u64 - uVtgHdrAddr) != pVtgHdr->offProbeLocs)
{
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;
}
if (pVtgHdr->cbProbes / sizeof(VTGDESCPROBE) != pVtgHdr->cbProbeEnabled / sizeof(uint32_t))
{
SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_MISC - cbProbeEnabled=%#zx cbProbes=%#zx\n",
pVtgHdr->cbProbeEnabled, pVtgHdr->cbProbes);
return VERR_SUPDRV_VTG_BAD_HDR_MISC;
}
/*
* Check that there are no overlapping areas. This is a little bit ugly...
*/
paAreas = (struct VTGAREAS const *)&pVtgHdr->offStrTab;
cAreas = pVtgHdr->offProbeLocs >= 0 ? 6 : 5;
off = sizeof(VTGOBJHDR);
for (i = 0; i < cAreas; i++)
{
if (paAreas[i].off < off)
{
SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_MISC - overlapping areas %d and %d\n", i, i-1);
return VERR_SUPDRV_VTG_BAD_HDR_MISC;
}
off = paAreas[i].off + paAreas[i].cb;
}
if ( pVtgHdr->offProbeLocs > 0
&& (uint32_t)-pVtgHdr->offProbeLocs < pVtgHdr->cbProbeLocs)
{
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.
*/
if (pVtgHdr->cbObj != pVtgHdr->offProbeEnabled + pVtgHdr->cbProbeEnabled)
{
SUPR0Printf("supdrvVtgValidateHdr: VERR_SUPDRV_VTG_BAD_HDR_MISC - bad header size %#x, expected %#x\n",
pVtgHdr->cbObj, pVtgHdr->offProbeEnabled + pVtgHdr->cbProbeEnabled);
return VERR_SUPDRV_VTG_BAD_HDR_MISC;
}
return VINF_SUCCESS;
#undef MY_VALIDATE_OFF
#undef MY_VALIDATE_SIZE
}
/**
* 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 offTmp;
uintptr_t i;
uintptr_t cProviders;
int rc;
if (!pbImage || !cbImage)
{
pbImage = NULL;
cbImage = 0;
}
#define MY_VALIDATE_STR(a_offStrTab) \
do { \
if ((a_offStrTab) >= pVtgHdr->cbStrTab) \
return VERR_SUPDRV_VTG_STRTAB_OFF; \
rc = supdrvVtgValidateString((char *)pVtgHdr + pVtgHdr->offStrTab + (a_offStrTab)); \
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; \
if ((Attr).u8DataDep <= (uint8_t)kVTGClass_Invalid || (Attr).u8DataDep >= (uint8_t)kVTGClass_End) \
return VERR_SUPDRV_VTG_BAD_ATTR; \
} while (0)
/*
* The header.
*/
rc = supdrvVtgValidateHdr(pVtgHdr, uVtgHdrAddr, pbImage, cbImage, fUmod);
if (RT_FAILURE(rc))
return rc;
/*
* Validate the providers.
*/
cProviders = i = pVtgHdr->cbProviders / sizeof(VTGDESCPROVIDER);
while (i-- > 0)
{
PCVTGDESCPROVIDER pProvider = (PCVTGDESCPROVIDER)((uintptr_t)pVtgHdr + pVtgHdr->offProviders) + i;
MY_VALIDATE_STR(pProvider->offName);
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),
VERR_SUPDRV_VTG_BAD_PROVIDER);
MY_VALIDATE_ATTR(pProvider->AttrSelf);
MY_VALIDATE_ATTR(pProvider->AttrModules);
MY_VALIDATE_ATTR(pProvider->AttrFunctions);
MY_VALIDATE_ATTR(pProvider->AttrNames);
MY_VALIDATE_ATTR(pProvider->AttrArguments);
MY_CHECK_RET(pProvider->bReserved == 0, VERR_SUPDRV_VTG_BAD_PROVIDER);
}
/*
* Validate probes.
*/
i = pVtgHdr->cbProbes / sizeof(VTGDESCPROBE);
while (i-- > 0)
{
PCVTGDESCPROBE pProbe = (PCVTGDESCPROBE)( (uintptr_t)pVtgHdr + pVtgHdr->offProbes) + i;
PCVTGDESCPROVIDER pProvider = (PCVTGDESCPROVIDER)((uintptr_t)pVtgHdr + pVtgHdr->offProviders) + pProbe->idxProvider;
PCVTGDESCARGLIST pArgList = (PCVTGDESCARGLIST)( (uintptr_t)pVtgHdr + pVtgHdr->offArgLists + pProbe->offArgList );
unsigned iArg;
bool fHaveLargeArgs;
MY_VALIDATE_STR(pProbe->offName);
MY_CHECK_RET(pProbe->offArgList < pVtgHdr->cbArgLists, VERR_SUPDRV_VTG_BAD_PROBE);
MY_CHECK_RET((pProbe->offArgList & 3) == 0, VERR_SUPDRV_VTG_BAD_PROBE);
MY_CHECK_RET(pProbe->idxEnabled == i, VERR_SUPDRV_VTG_BAD_PROBE); /* The lists are parallel. */
MY_CHECK_RET(pProbe->idxProvider < cProviders, VERR_SUPDRV_VTG_BAD_PROBE);
MY_CHECK_RET(i - pProvider->iFirstProbe < pProvider->cProbes, VERR_SUPDRV_VTG_BAD_PROBE);
if (pProbe->offObjHdr != (intptr_t)pVtgHdr - (intptr_t)pProbe)
{
SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_VTG_BAD_PROBE - iProbe=%u offObjHdr=%d expected %zd\n",
i, pProbe->offObjHdr, (intptr_t)pVtgHdr - (intptr_t)pProbe);
return VERR_SUPDRV_VTG_BAD_PROBE;
}
/* The referenced argument list. */
if (pArgList->cArgs > 16)
{
SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_VTG_BAD_ARGLIST - iProbe=%u cArgs=%u\n", i, pArgList->cArgs);
return VERR_SUPDRV_VTG_BAD_ARGLIST;
}
if (pArgList->fHaveLargeArgs >= 2)
{
SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_VTG_BAD_ARGLIST - iProbe=%u fHaveLargeArgs=%d\n", i, pArgList->fHaveLargeArgs);
return VERR_SUPDRV_VTG_BAD_ARGLIST;
}
if ( pArgList->abReserved[0]
|| pArgList->abReserved[1])
{
SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_VTG_BAD_ARGLIST - reserved MBZ iProbe=%u\n", i);
return VERR_SUPDRV_VTG_BAD_ARGLIST;
}
fHaveLargeArgs = false;
iArg = pArgList->cArgs;
while (iArg-- > 0)
{
uint32_t const fType = pArgList->aArgs[iArg].fType;
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;
}
switch (pArgList->aArgs[iArg].fType & VTG_TYPE_SIZE_MASK)
{
case 0:
if (pArgList->aArgs[iArg].fType & VTG_TYPE_FIXED_SIZED)
{
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;
}
if (VTG_TYPE_IS_LARGE(pArgList->aArgs[iArg].fType))
fHaveLargeArgs = true;
MY_VALIDATE_STR(pArgList->aArgs[iArg].offType);
}
if ((uint8_t)fHaveLargeArgs != pArgList->fHaveLargeArgs)
{
SUPR0Printf("supdrvVtgValidate: VERR_SUPDRV_TRACER_BAD_ARG_FLAGS - iProbe=%u fHaveLargeArgs=%d expected %d\n",
i, pArgList->fHaveLargeArgs, fHaveLargeArgs);
return VERR_SUPDRV_VTG_BAD_PROBE;
}
}
/*
* Check that pacProbeEnabled is all zeros.
*/
{
uint32_t const *pcProbeEnabled = (uint32_t const *)((uintptr_t)pVtgHdr + pVtgHdr->offProbeEnabled);
i = pVtgHdr->cbProbeEnabled / sizeof(uint32_t);
while (i-- > 0)
MY_CHECK_RET(pcProbeEnabled[0] == 0, VERR_SUPDRV_VTG_BAD_PROBE_ENABLED);
}
/*
* Probe locations.
*/
{
PVTGPROBELOC paProbeLocs = (PVTGPROBELOC)((intptr_t)pVtgHdr + pVtgHdr->offProbeLocs);
i = pVtgHdr->cbProbeLocs / sizeof(VTGPROBELOC);
while (i-- > 0)
{
MY_CHECK_RET(paProbeLocs[i].uLine < _1G, VERR_SUPDRV_VTG_BAD_PROBE_LOC);
MY_CHECK_RET(paProbeLocs[i].fEnabled == false, VERR_SUPDRV_VTG_BAD_PROBE_LOC);
MY_CHECK_RET(paProbeLocs[i].idProbe == 0, VERR_SUPDRV_VTG_BAD_PROBE_LOC);
MY_WITHIN_IMAGE(paProbeLocs[i].pszFunction, VERR_SUPDRV_VTG_BAD_PROBE_LOC);
offTmp = (uintptr_t)paProbeLocs[i].pProbe - (uintptr_t)pVtgHdr->offProbes - (uintptr_t)pVtgHdr;
#ifdef RT_OS_DARWIN /* See header validation code. */
if ( offTmp >= pVtgHdr->cbProbes
&& pVtgHdr->u64VtgObjSectionStart != uVtgHdrAddr
&& pVtgHdr->u64VtgObjSectionStart < _4M
&& (uintptr_t)paProbeLocs[i].pProbe < _4M
&& !fUmod )
{
uint64_t offDelta = uVtgHdrAddr - pVtgHdr->u64VtgObjSectionStart;
paProbeLocs[i].pProbe = (PVTGDESCPROBE)((uintptr_t)paProbeLocs[i].pProbe + offDelta);
offTmp += offDelta;
}
#endif
MY_CHECK_RET(offTmp < pVtgHdr->cbProbes, VERR_SUPDRV_VTG_BAD_PROBE_LOC);
MY_CHECK_RET(offTmp / sizeof(VTGDESCPROBE) * sizeof(VTGDESCPROBE) == offTmp, VERR_SUPDRV_VTG_BAD_PROBE_LOC);
}
}
return VINF_SUCCESS;
}
#undef MY_VALIDATE_STR
#undef MY_VALIDATE_ATTR
#undef MY_WITHIN_IMAGE
/**
* Gets a string from the string table.
*
* @returns Pointer to the string.
* @param pVtgHdr The VTG object header.
* @param offStrTab The string table offset.
*/
static const char *supdrvVtgGetString(PVTGOBJHDR pVtgHdr, uint32_t offStrTab)
{
Assert(offStrTab < pVtgHdr->cbStrTab);
return (char *)pVtgHdr + pVtgHdr->offStrTab + offStrTab;
}
/**
* Frees the provider structure and associated resources.
*
* @param pProv The provider to free.
*/
static void supdrvTracerFreeProvider(PSUPDRVTPPROVIDER pProv)
{
LOG_TRACER(("Freeing tracepoint provider '%s' / %p\n", pProv->szName, pProv->Core.TracerData.DTrace.idProvider));
pProv->fRegistered = false;
pProv->fZombie = true;
pProv->Core.pDesc = NULL;
pProv->Core.pHdr = NULL;
pProv->Core.paProbeLocsRO = NULL;
pProv->Core.pvProbeLocsEn = NULL;
pProv->Core.pacProbeEnabled = NULL;
pProv->Core.paR0ProbeLocs = NULL;
pProv->Core.paR0Probes = NULL;
RT_ZERO(pProv->Core.TracerData);
RTMemFree(pProv);
}
/**
* 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.
*/
static void supdrvTracerDeregisterVtgObj(PSUPDRVDEVEXT pDevExt, PSUPDRVTPPROVIDER pProv)
{
int rc;
RTListNodeRemove(&pProv->ListEntry);
if (pProv->pSession)
{
RTListNodeRemove(&pProv->SessionListEntry);
RTListInit(&pProv->SessionListEntry);
pProv->pSession->cTpProviders--;
}
if (!pProv->fRegistered || !pDevExt->pTracerOps)
rc = VINF_SUCCESS;
else
rc = pDevExt->pTracerOps->pfnProviderDeregister(pDevExt->pTracerOps, &pProv->Core);
if (RT_SUCCESS(rc))
{
supdrvTracerFreeProvider(pProv);
return;
}
pProv->fZombie = true;
pProv->pImage = NULL;
pProv->pSession = NULL;
pProv->pUmod = NULL;
pProv->Core.pDesc = NULL;
pProv->Core.pHdr = NULL;
pProv->Core.paProbeLocsRO = NULL;
pProv->Core.pvProbeLocsEn = NULL;
pProv->Core.pacProbeEnabled = NULL;
pProv->Core.paR0ProbeLocs = NULL;
RTListAppend(&pDevExt->TracerProviderZombieList, &pProv->ListEntry);
LOG_TRACER(("Invalidated provider '%s' / %p and put it on the zombie list (rc=%Rrc)\n",
pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc));
}
/**
* Processes the zombie list.
*
* @param pDevExt The device extension.
*/
static void supdrvTracerProcessZombies(PSUPDRVDEVEXT pDevExt)
{
PSUPDRVTPPROVIDER pProv, pProvNext;
RTSemFastMutexRequest(pDevExt->mtxTracer);
RTListForEachSafe(&pDevExt->TracerProviderZombieList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
{
int rc = pDevExt->pTracerOps->pfnProviderDeregisterZombie(pDevExt->pTracerOps, &pProv->Core);
if (RT_SUCCESS(rc))
{
RTListNodeRemove(&pProv->ListEntry);
supdrvTracerFreeProvider(pProv);
}
}
RTSemFastMutexRelease(pDevExt->mtxTracer);
}
/**
* Unregisters all providers, including zombies, waiting for busy providers to
* go idle and unregister smoothly.
*
* This may block.
*
* @param pDevExt The device extension.
*/
static void supdrvTracerRemoveAllProviders(PSUPDRVDEVEXT pDevExt)
{
uint32_t i;
PSUPDRVTPPROVIDER pProv;
PSUPDRVTPPROVIDER pProvNext;
/*
* Unregister all probes (there should only be one).
*/
RTSemFastMutexRequest(pDevExt->mtxTracer);
RTListForEachSafe(&pDevExt->TracerProviderList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
{
supdrvTracerDeregisterVtgObj(pDevExt, pProv);
}
RTSemFastMutexRelease(pDevExt->mtxTracer);
/*
* Try unregister zombies now, sleep on busy ones and tracer opens.
*/
for (i = 0; ; i++)
{
bool fEmpty;
RTSemFastMutexRequest(pDevExt->mtxTracer);
/* Zombies */
RTListForEachSafe(&pDevExt->TracerProviderZombieList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
{
int rc;
LOG_TRACER(("supdrvTracerRemoveAllProviders: Attemting to unregister '%s' / %p...\n",
pProv->szName, pProv->Core.TracerData.DTrace.idProvider));
if (pDevExt->pTracerOps)
rc = pDevExt->pTracerOps->pfnProviderDeregisterZombie(pDevExt->pTracerOps, &pProv->Core);
else
rc = VINF_SUCCESS;
if (!rc)
{
RTListNodeRemove(&pProv->ListEntry);
supdrvTracerFreeProvider(pProv);
}
else if (!(i & 0xf))
SUPR0Printf("supdrvTracerRemoveAllProviders: Waiting on busy provider '%s' / %p (rc=%d)\n",
pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc);
else
LOG_TRACER(("supdrvTracerRemoveAllProviders: Failed to unregister provider '%s' / %p - rc=%d\n",
pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc));
}
fEmpty = RTListIsEmpty(&pDevExt->TracerProviderZombieList);
/* Tracer opens. */
if ( pDevExt->cTracerOpens
&& pDevExt->pTracerOps)
{
fEmpty = false;
if (!(i & 0xf))
SUPR0Printf("supdrvTracerRemoveAllProviders: Waiting on %u opens\n", pDevExt->cTracerOpens);
else
LOG_TRACER(("supdrvTracerRemoveAllProviders: Waiting on %u opens\n", pDevExt->cTracerOpens));
}
RTSemFastMutexRelease(pDevExt->mtxTracer);
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,
PSUPDRVSESSION pSession, PSUPDRVTRACERUMOD pUmod, const char *pszModName)
{
int rc;
uintptr_t i;
PSUPDRVTPPROVIDER pProv;
size_t cchModName;
/*
* Validate input.
*/
AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
AssertPtrReturn(pVtgHdr, VERR_INVALID_POINTER);
AssertPtrNullReturn(pImage, VERR_INVALID_POINTER);
AssertPtrNullReturn(pSession, VERR_INVALID_POINTER);
AssertPtrReturn(pszModName, VERR_INVALID_POINTER);
cchModName = strlen(pszModName);
if (pImage)
rc = supdrvVtgValidate(pVtgHdr, (uintptr_t)pVtgHdr,
(const uint8_t *)pImage->pvImage, pImage->cbImageBits,
false /*fUmod*/);
else
rc = supdrvVtgValidate(pVtgHdr, (uintptr_t)pVtgHdr, NULL, 0, pUmod != NULL);
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.)
*/
rc = RTSemFastMutexRequest(pDevExt->mtxTracer);
if (RT_FAILURE(rc))
return rc;
if (pImage || !pSession || pSession->R0Process == NIL_RTPROCESS)
{
RTListForEach(&pDevExt->TracerProviderList, pProv, SUPDRVTPPROVIDER, ListEntry)
{
if (pProv->Core.pHdr == pVtgHdr)
{
rc = VERR_SUPDRV_VTG_ALREADY_REGISTERED;
break;
}
if ( pProv->pSession == pSession
&& pProv->pImage == pImage)
{
rc = VERR_SUPDRV_VTG_ONLY_ONCE_PER_SESSION;
break;
}
}
}
else
{
RTListForEach(&pSession->TpProviders, pProv, SUPDRVTPPROVIDER, SessionListEntry)
{
if (pProv->Core.pHdr == pVtgHdr)
{
rc = VERR_SUPDRV_VTG_ALREADY_REGISTERED;
break;
}
}
}
RTSemFastMutexRelease(pDevExt->mtxTracer);
if (RT_FAILURE(rc))
return rc;
/*
* Register the providers.
*/
i = pVtgHdr->cbProviders / sizeof(VTGDESCPROVIDER);
while (i-- > 0)
{
PVTGDESCPROVIDER pDesc = (PVTGDESCPROVIDER)((uintptr_t)pVtgHdr + pVtgHdr->offProviders) + i;
const char *pszName = supdrvVtgGetString(pVtgHdr, pDesc->offName);
size_t const cchName = strlen(pszName) + (pUmod ? 16 : 0);
pProv = (PSUPDRVTPPROVIDER)RTMemAllocZ(RT_OFFSETOF(SUPDRVTPPROVIDER, szName[cchName + 1 + cchModName + 1]));
if (pProv)
{
pProv->Core.pszName = &pProv->szName[0];
pProv->Core.pszModName = &pProv->szName[cchName + 1];
pProv->Core.pDesc = pDesc;
pProv->Core.pHdr = pVtgHdr;
pProv->Core.paProbeLocsRO = (PCVTGPROBELOC )((uintptr_t)pVtgHdr + pVtgHdr->offProbeLocs);
if (!pUmod)
{
pProv->Core.pvProbeLocsEn = (void *)((uintptr_t)pVtgHdr + pVtgHdr->offProbeLocs);
pProv->Core.pacProbeEnabled = (uint32_t *)((uintptr_t)pVtgHdr + pVtgHdr->offProbeEnabled);
pProv->Core.paR0ProbeLocs = NULL;
pProv->Core.paR0Probes = NULL;
pProv->Core.cbProbeLocsEn = sizeof(VTGPROBELOC);
pProv->Core.cBits = ARCH_BITS;
pProv->Core.fUmod = false;
}
else
{
pProv->Core.pvProbeLocsEn = pUmod->pvProbeLocs;
pProv->Core.pacProbeEnabled = pUmod->pacProbeEnabled;
pProv->Core.paR0ProbeLocs = &pUmod->aProbeLocs[0];
pProv->Core.paR0Probes = (PSUPDRVPROBEINFO)&pUmod->aProbeLocs[pUmod->cProbeLocs];
pProv->Core.cbProbeLocsEn = pUmod->cbProbeLoc;
pProv->Core.cBits = pUmod->cBits;
pProv->Core.fUmod = true;
}
pProv->pImage = pImage;
pProv->pSession = pSession;
pProv->pUmod = pUmod;
pProv->fZombie = false;
pProv->fRegistered = true;
if (!pUmod)
memcpy(pProv->szName, pszName, cchName + 1);
else
RTStrPrintf(pProv->szName, cchName + 1, "%s%u", pszName, (uint32_t)pSession->Process);
memcpy((void *)pProv->Core.pszModName, pszModName, cchModName + 1);
/*
* Do the actual registration and list manipulations while holding
* down the lock.
*/
rc = RTSemFastMutexRequest(pDevExt->mtxTracer);
if (RT_SUCCESS(rc))
{
if ( pDevExt->pTracerOps
&& !pDevExt->fTracerUnloading)
rc = pDevExt->pTracerOps->pfnProviderRegister(pDevExt->pTracerOps, &pProv->Core);
else
{
pProv->fRegistered = false;
rc = VINF_SUCCESS;
}
if (RT_SUCCESS(rc))
{
RTListAppend(&pDevExt->TracerProviderList, &pProv->ListEntry);
if (pSession)
{
RTListAppend(&pSession->TpProviders, &pProv->SessionListEntry);
pSession->cTpProviders++;
}
else
RTListInit(&pProv->SessionListEntry);
RTSemFastMutexRelease(pDevExt->mtxTracer);
LOG_TRACER(("Registered tracepoint provider '%s' in '%s' -> %p\n",
pProv->szName, pszModName, pProv->Core.TracerData.DTrace.idProvider));
}
else
{
RTSemFastMutexRelease(pDevExt->mtxTracer);
LOG_TRACER(("Failed to register tracepoint provider '%s' in '%s' -> %Rrc\n",
pProv->szName, pszModName, rc));
}
}
}
else
rc = VERR_NO_MEMORY;
/*
* In case of failure, we have to undo any providers we already
* managed to register.
*/
if (RT_FAILURE(rc))
{
PSUPDRVTPPROVIDER pProvNext;
if (pProv)
supdrvTracerFreeProvider(pProv);
RTSemFastMutexRequest(pDevExt->mtxTracer);
if (pImage)
{
RTListForEachReverseSafe(&pDevExt->TracerProviderList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
{
if (pProv->Core.pHdr == pVtgHdr)
supdrvTracerDeregisterVtgObj(pDevExt, pProv);
}
}
else
{
RTListForEachSafe(&pSession->TpProviders, pProv, pProvNext, SUPDRVTPPROVIDER, SessionListEntry)
{
if (pProv->Core.pHdr == pVtgHdr)
supdrvTracerDeregisterVtgObj(pDevExt, pProv);
}
}
RTSemFastMutexRelease(pDevExt->mtxTracer);
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;
AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
AssertPtrReturn(pszName, VERR_INVALID_POINTER);
AssertPtrReturn(pVtgHdr, VERR_INVALID_POINTER);
AssertReturn(pSession->R0Process == NIL_RTR0PROCESS, VERR_INVALID_PARAMETER);
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.
*/
supdrvTracerProcessZombies(pSession->pDevExt);
return rc;
}
/**
* Deregister the VTG tracepoint providers of a driver.
*
* @param pSession The support driver session handle.
* @param pVtgHdr The VTG header.
*/
SUPR0DECL(void) SUPR0TracerDeregisterDrv(PSUPDRVSESSION pSession)
{
PSUPDRVTPPROVIDER pProv, pProvNext;
PSUPDRVDEVEXT pDevExt;
AssertReturnVoid(SUP_IS_SESSION_VALID(pSession));
AssertReturnVoid(pSession->R0Process == NIL_RTR0PROCESS);
LOG_TRACER(("SUPR0TracerDeregisterDrv: pSession=%p\n", pSession));
pDevExt = pSession->pDevExt;
/*
* Search for providers belonging to this driver session.
*/
RTSemFastMutexRequest(pDevExt->mtxTracer);
RTListForEachSafe(&pSession->TpProviders, pProv, pProvNext, SUPDRVTPPROVIDER, SessionListEntry)
{
supdrvTracerDeregisterVtgObj(pDevExt, pProv);
}
RTSemFastMutexRelease(pDevExt->mtxTracer);
/*
* Try unregister zombies while we have a chance.
*/
supdrvTracerProcessZombies(pDevExt);
}
/**
* 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.
*/
SUPR0DECL(int) SUPR0TracerRegisterModule(void *hMod, PVTGOBJHDR pVtgHdr)
{
PSUPDRVLDRIMAGE pImage = (PSUPDRVLDRIMAGE)hMod;
PSUPDRVDEVEXT pDevExt;
int rc;
LOG_TRACER(("SUPR0TracerRegisterModule: %p\n", pVtgHdr));
/*
* Validate input and context.
*/
AssertPtrReturn(pImage, VERR_INVALID_HANDLE);
AssertPtrReturn(pVtgHdr, VERR_INVALID_POINTER);
AssertPtrReturn(pImage, VERR_INVALID_POINTER);
pDevExt = pImage->pDevExt;
AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
AssertReturn(pDevExt->pLdrInitImage == pImage, VERR_WRONG_ORDER);
AssertReturn(pDevExt->hLdrInitThread == RTThreadNativeSelf(), VERR_WRONG_ORDER);
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);
LOG_TRACER(("SUPR0TracerRegisterModule: rc=%d\n", rc));
/*
* Try unregister zombies while we have a chance.
*/
supdrvTracerProcessZombies(pDevExt);
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)
{
PSUPDRVLDRIMAGE pImage = (PSUPDRVLDRIMAGE)hMod;
PSUPDRVDEVEXT pDevExt;
PSUPDRVTPPROVIDER pProv;
int rc;
int rc2;
/*
* Validate input and context.
*/
AssertPtrReturn(ppHlp, VERR_INVALID_POINTER);
*ppHlp = NULL;
AssertPtrReturn(pReg, VERR_INVALID_HANDLE);
if (pImage)
{
AssertPtrReturn(pImage, VERR_INVALID_POINTER);
AssertReturn(pSession == NULL, VERR_INVALID_PARAMETER);
pDevExt = pImage->pDevExt;
AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
AssertReturn(pDevExt->pLdrInitImage == pImage, VERR_WRONG_ORDER);
AssertReturn(pDevExt->hLdrInitThread == RTThreadNativeSelf(), VERR_WRONG_ORDER);
}
else
{
AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
AssertReturn(pSession->R0Process == NIL_RTR0PROCESS, VERR_INVALID_PARAMETER);
pDevExt = pSession->pDevExt;
AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
}
AssertReturn(pReg->u32Magic == SUPDRVTRACERREG_MAGIC, VERR_INVALID_MAGIC);
AssertReturn(pReg->u32Version == SUPDRVTRACERREG_VERSION, VERR_VERSION_MISMATCH);
AssertReturn(pReg->uEndMagic == SUPDRVTRACERREG_MAGIC, VERR_VERSION_MISMATCH);
AssertPtrReturn(pReg->pfnProbeFireKernel, VERR_INVALID_POINTER);
AssertPtrReturn(pReg->pfnProbeFireUser, VERR_INVALID_POINTER);
AssertPtrReturn(pReg->pfnTracerOpen, VERR_INVALID_POINTER);
AssertPtrReturn(pReg->pfnTracerIoCtl, VERR_INVALID_POINTER);
AssertPtrReturn(pReg->pfnTracerClose, VERR_INVALID_POINTER);
AssertPtrReturn(pReg->pfnProviderRegister, VERR_INVALID_POINTER);
AssertPtrReturn(pReg->pfnProviderDeregister, VERR_INVALID_POINTER);
AssertPtrReturn(pReg->pfnProviderDeregisterZombie, VERR_INVALID_POINTER);
/*
* Do the job.
*/
rc = RTSemFastMutexRequest(pDevExt->mtxTracer);
if (RT_SUCCESS(rc))
{
if (!pDevExt->pTracerOps)
{
LOG_TRACER(("SUPR0TracerRegisterImpl: pReg=%p\n", pReg));
pDevExt->pTracerOps = pReg;
pDevExt->pTracerSession = pSession;
pDevExt->pTracerImage = pImage;
g_pfnSupdrvProbeFireKernel = (PFNRT)pDevExt->pTracerOps->pfnProbeFireKernel;
*ppHlp = &pDevExt->TracerHlp;
rc = VINF_SUCCESS;
/*
* Iterate the already loaded modules and register their providers.
*/
RTListForEach(&pDevExt->TracerProviderList, pProv, SUPDRVTPPROVIDER, ListEntry)
{
Assert(!pProv->fRegistered);
pProv->fRegistered = true;
rc2 = pDevExt->pTracerOps->pfnProviderRegister(pDevExt->pTracerOps, &pProv->Core);
if (RT_FAILURE(rc2))
{
pProv->fRegistered = false;
SUPR0Printf("SUPR0TracerRegisterImpl: Failed to register provider %s::%s - rc=%d\n",
pProv->Core.pszModName, pProv->szName, rc2);
}
}
}
else
rc = VERR_SUPDRV_TRACER_ALREADY_REGISTERED;
RTSemFastMutexRelease(pDevExt->mtxTracer);
}
return rc;
}
/**
* Common tracer implementation deregistration code.
*
* The caller sets fTracerUnloading prior to calling this function.
*
* @param pDevExt The device extension structure.
*/
static void supdrvTracerCommonDeregisterImpl(PSUPDRVDEVEXT pDevExt)
{
uint32_t i;
PSUPDRVTPPROVIDER pProv;
PSUPDRVTPPROVIDER pProvNext;
RTSemFastMutexRequest(pDevExt->mtxTracer);
/*
* Reinstall the stub probe-fire function.
*/
g_pfnSupdrvProbeFireKernel = supdrvTracerProbeFireStub;
/*
* Disassociate the tracer implementation from all providers.
* We will have to wait on busy providers.
*/
for (i = 0; ; i++)
{
uint32_t cZombies = 0;
/* Live providers. */
RTListForEachSafe(&pDevExt->TracerProviderList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
{
int rc;
LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Attemting to unregister '%s' / %p...\n",
pProv->szName, pProv->Core.TracerData.DTrace.idProvider));
if (!pProv->fRegistered)
continue;
if (!pProv->fZombie)
{
rc = pDevExt->pTracerOps->pfnProviderDeregister(pDevExt->pTracerOps, &pProv->Core);
if (RT_FAILURE(rc))
pProv->fZombie = true;
}
else
rc = pDevExt->pTracerOps->pfnProviderDeregisterZombie(pDevExt->pTracerOps, &pProv->Core);
if (RT_SUCCESS(rc))
pProv->fZombie = pProv->fRegistered = false;
else
{
cZombies++;
if (!(i & 0xf))
SUPR0Printf("supdrvTracerCommonDeregisterImpl: Waiting on busy provider '%s' / %p (rc=%d)\n",
pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc);
else
LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Failed to unregister provider '%s' / %p - rc=%d\n",
pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc));
}
}
/* Zombies providers. */
RTListForEachSafe(&pDevExt->TracerProviderZombieList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
{
int rc;
LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Attemting to unregister '%s' / %p (zombie)...\n",
pProv->szName, pProv->Core.TracerData.DTrace.idProvider));
rc = pDevExt->pTracerOps->pfnProviderDeregisterZombie(pDevExt->pTracerOps, &pProv->Core);
if (RT_SUCCESS(rc))
{
RTListNodeRemove(&pProv->ListEntry);
supdrvTracerFreeProvider(pProv);
}
else
{
cZombies++;
if (!(i & 0xf))
SUPR0Printf("supdrvTracerCommonDeregisterImpl: Waiting on busy provider '%s' / %p (rc=%d)\n",
pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc);
else
LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Failed to unregister provider '%s' / %p - rc=%d\n",
pProv->szName, pProv->Core.TracerData.DTrace.idProvider, rc));
}
}
/* Tracer opens. */
if (pDevExt->cTracerOpens)
{
cZombies++;
if (!(i & 0xf))
SUPR0Printf("supdrvTracerCommonDeregisterImpl: Waiting on %u opens\n", pDevExt->cTracerOpens);
else
LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Waiting on %u opens\n", pDevExt->cTracerOpens));
}
/* Tracer calls. */
if (pDevExt->cTracerCallers)
{
cZombies++;
if (!(i & 0xf))
SUPR0Printf("supdrvTracerCommonDeregisterImpl: Waiting on %u callers\n", pDevExt->cTracerCallers);
else
LOG_TRACER(("supdrvTracerCommonDeregisterImpl: Waiting on %u callers\n", pDevExt->cTracerCallers));
}
/* Done? */
if (cZombies == 0)
break;
/* Delay...*/
RTSemFastMutexRelease(pDevExt->mtxTracer);
RTThreadSleep(1000);
RTSemFastMutexRequest(pDevExt->mtxTracer);
}
/*
* Deregister the tracer implementation.
*/
pDevExt->pTracerImage = NULL;
pDevExt->pTracerSession = NULL;
pDevExt->pTracerOps = NULL;
pDevExt->fTracerUnloading = false;
RTSemFastMutexRelease(pDevExt->mtxTracer);
}
/**
* 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.
*/
SUPR0DECL(int) SUPR0TracerDeregisterImpl(void *hMod, PSUPDRVSESSION pSession)
{
PSUPDRVLDRIMAGE pImage = (PSUPDRVLDRIMAGE)hMod;
PSUPDRVDEVEXT pDevExt;
int rc;
/*
* Validate input and context.
*/
if (pImage)
{
AssertPtrReturn(pImage, VERR_INVALID_POINTER);
AssertReturn(pSession == NULL, VERR_INVALID_PARAMETER);
pDevExt = pImage->pDevExt;
}
else
{
AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
AssertReturn(pSession->R0Process == NIL_RTR0PROCESS, VERR_INVALID_PARAMETER);
pDevExt = pSession->pDevExt;
}
AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
/*
* Do the job.
*/
rc = RTSemFastMutexRequest(pDevExt->mtxTracer);
if (RT_SUCCESS(rc))
{
if ( pImage
? pDevExt->pTracerImage == pImage
: pDevExt->pTracerSession == pSession)
{
LOG_TRACER(("SUPR0TracerDeregisterImpl: Unloading ...\n"));
pDevExt->fTracerUnloading = true;
RTSemFastMutexRelease(pDevExt->mtxTracer);
supdrvTracerCommonDeregisterImpl(pDevExt);
LOG_TRACER(("SUPR0TracerDeregisterImpl: ... done.\n"));
}
else
{
rc = VERR_SUPDRV_TRACER_NOT_REGISTERED;
RTSemFastMutexRelease(pDevExt->mtxTracer);
}
}
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.
*/
#if defined(__GNUC__) && (defined(RT_OS_FREEBSD) || defined(RT_OS_LINUX))
__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.
*/
void VBOXCALL supdrvTracerModuleUnloading(PSUPDRVDEVEXT pDevExt, PSUPDRVLDRIMAGE pImage)
{
PSUPDRVTPPROVIDER pProv, pProvNext;
AssertPtrReturnVoid(pImage); /* paranoia */
RTSemFastMutexRequest(pDevExt->mtxTracer);
/*
* If it is the tracer image, we have to unload all the providers.
*/
if (pDevExt->pTracerImage == pImage)
{
LOG_TRACER(("supdrvTracerModuleUnloading: Unloading tracer ...\n"));
pDevExt->fTracerUnloading = true;
RTSemFastMutexRelease(pDevExt->mtxTracer);
supdrvTracerCommonDeregisterImpl(pDevExt);
LOG_TRACER(("supdrvTracerModuleUnloading: ... done.\n"));
}
else
{
/*
* Unregister all providers belonging to this image.
*/
RTListForEachSafe(&pDevExt->TracerProviderList, pProv, pProvNext, SUPDRVTPPROVIDER, ListEntry)
{
if (pProv->pImage == pImage)
supdrvTracerDeregisterVtgObj(pDevExt, pProv);
}
RTSemFastMutexRelease(pDevExt->mtxTracer);
/*
* Try unregister zombies while we have a chance.
*/
supdrvTracerProcessZombies(pDevExt);
}
}
/**
* Called when a session is being cleaned up.
*
* @param pDevExt The device extension structure.
* @param pSession The session that is being torn down.
*/
void VBOXCALL supdrvTracerCleanupSession(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
{
/*
* Deregister all providers.
*/
SUPDRVTPPROVIDER *pProvNext;
SUPDRVTPPROVIDER *pProv;
RTSemFastMutexRequest(pDevExt->mtxTracer);
RTListForEachSafe(&pSession->TpProviders, pProv, pProvNext, SUPDRVTPPROVIDER, SessionListEntry)
{
supdrvTracerDeregisterVtgObj(pDevExt, pProv);
}
RTSemFastMutexRelease(pDevExt->mtxTracer);
/*
* Clean up instance data the trace may have associated with the session.
*/
if (pSession->uTracerData)
supdrvIOCtl_TracerClose(pDevExt, pSession);
/*
* Deregister any tracer implementation.
*/
if (pSession->R0Process == NIL_RTR0PROCESS)
(void)SUPR0TracerDeregisterImpl(NULL, pSession);
if (pSession->R0Process != NIL_RTR0PROCESS)
{
/*
* 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.
*/
PSUPDRVTRACERUMOD pUmodNext;
PSUPDRVTRACERUMOD pUmod;
RTListForEachSafe(&pSession->TpUmods, pUmod, pUmodNext, SUPDRVTRACERUMOD, ListEntry)
{
RTR0MemObjFree(pUmod->hMemObjMap, false /*fFreeMappings*/);
RTR0MemObjFree(pUmod->hMemObjLock, false /*fFreeMappings*/);
supdrvVtgReleaseObjectCopy(pDevExt, pUmod->pVtgCopy);
RTMemFree(pUmod);
}
}
}
static void supdrvVtgReleaseObjectCopy(PSUPDRVDEVEXT pDevExt, PSUPDRVVTGCOPY pThis)
{
uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
if (!cRefs)
{
RTSemFastMutexRequest(pDevExt->mtxTracer);
pThis->u32Magic = ~SUDPRVVTGCOPY_MAGIC;
RTListNodeRemove(&pThis->ListEntry);
RTSemFastMutexRelease(pDevExt->mtxTracer);
RTMemFree(pThis);
}
}
/**
* 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)
{
PSUPDRVVTGCOPY pCur;
fFlags &= SUP_TRACER_UMOD_FLAGS_TYPE_MASK;
RTListForEach(pHashList, pCur, SUPDRVVTGCOPY, ListEntry)
{
#define HDR_EQUALS(member) pCur->Hdr.member == pHdr->member
if ( HDR_EQUALS(Uuid.au32[0])
&& HDR_EQUALS(Uuid.au32[1])
&& HDR_EQUALS(Uuid.au32[2])
&& HDR_EQUALS(Uuid.au32[3])
&& HDR_EQUALS(cbObj)
&& HDR_EQUALS(cBits)
&& pCur->cbStrTab == cbStrTab
&& pCur->fFlags == fFlags
)
{
if (RT_LIKELY( HDR_EQUALS(offStrTab)
&& HDR_EQUALS(cbStrTab)
&& HDR_EQUALS(offArgLists)
&& HDR_EQUALS(cbArgLists)
&& HDR_EQUALS(offProbes)
&& HDR_EQUALS(cbProbes)
&& HDR_EQUALS(offProviders)
&& HDR_EQUALS(cbProviders)
&& HDR_EQUALS(offProbeEnabled)
&& HDR_EQUALS(cbProbeEnabled)
&& HDR_EQUALS(offProbeLocs)
&& HDR_EQUALS(cbProbeLocs)
)
)
{
Assert(pCur->cRefs > 0);
Assert(pCur->cRefs < _1M);
pCur->cRefs++;
return pCur;
}
}
#undef HDR_EQUALS
}
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)];
PSUPDRVVTGCOPY pRet;
int rc = RTSemFastMutexRequest(pDevExt->mtxTracer);
AssertRCReturn(rc, NULL);
pRet = supdrvVtgFindObjectCopyLocked(pHashList, pHdr, cbStrTab, fFlags);
RTSemFastMutexRelease(pDevExt->mtxTracer);
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,
RTR3PTR R3PtrStrTab, uint32_t cbStrTab, uint32_t fFlags, PSUPDRVTRACERUMOD pUmod)
{
/*
* 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));
size_t const cbProbeLocs = cProbeLocs * sizeof(VTGPROBELOC);
size_t const offProbeLocs = RT_ALIGN(pVtgHdr->cbObj, 8);
size_t const cb = offProbeLocs + cbProbeLocs + cbStrTab + 1;
PSUPDRVVTGCOPY pThis = (PSUPDRVVTGCOPY)RTMemAlloc(RT_OFFSETOF(SUPDRVVTGCOPY, Hdr) + cb);
if (!pThis)
return VERR_NO_MEMORY;
pThis->u32Magic = SUDPRVVTGCOPY_MAGIC;
pThis->cRefs = 1;
pThis->cbStrTab = cbStrTab;
pThis->fFlags = fFlags & SUP_TRACER_UMOD_FLAGS_TYPE_MASK;
RTListInit(&pThis->ListEntry);
rc = RTR0MemUserCopyFrom(&pThis->Hdr, R3PtrVtgHdr, pVtgHdr->cbObj);
if (RT_SUCCESS(rc))
{
char *pchStrTab = (char *)&pThis->Hdr + offProbeLocs + cbProbeLocs;
rc = RTR0MemUserCopyFrom(pchStrTab, R3PtrStrTab, cbStrTab);
if (RT_SUCCESS(rc))
{
PVTGPROBELOC paDst = (PVTGPROBELOC)((char *)&pThis->Hdr + offProbeLocs);
uint32_t i;
/*
* Some paranoia: Overwrite the header with the copy we've already
* validated and zero terminate the string table.
*/
pThis->Hdr = *pVtgHdr;
pchStrTab[cbStrTab] = '\0';
/*
* Set the probe location array related header members since we're
* making our own copy in a different location.
*/
pThis->Hdr.uProbeLocs.u64 = (uintptr_t)paDst;
pThis->Hdr.uProbeLocsEnd.u64 = (uintptr_t)paDst + cbProbeLocs;
pThis->Hdr.offProbeLocs = offProbeLocs;
pThis->Hdr.cbProbeLocs = cbProbeLocs;
pThis->Hdr.cBits = ARCH_BITS;
/*
* Copy, convert and fix up the probe location table.
*/
if (pVtgHdr->cBits == 32)
{
uintptr_t const offDelta = (uintptr_t)&pThis->Hdr - uVtgHdrAddr;
PCVTGPROBELOC32 paSrc = (PCVTGPROBELOC32)pUmod->pvProbeLocs;
for (i = 0; i < cProbeLocs; i++)
{
paDst[i].uLine = paSrc[i].uLine;
paDst[i].fEnabled = paSrc[i].fEnabled;
paDst[i].idProbe = paSrc[i].idProbe;
if (paSrc[i].pszFunction > cbStrTab)
{
rc = VERR_SUPDRV_TRACER_UMOD_STRTAB_OFF_BAD;
break;
}
paDst[i].pszFunction = pchStrTab + paSrc[i].pszFunction;
paDst[i].pProbe = (PVTGDESCPROBE)(uintptr_t)(paSrc[i].pProbe + offDelta);
}
}
else
{
uint64_t const offDelta = (uintptr_t)&pThis->Hdr - uVtgHdrAddr;
PCVTGPROBELOC64 paSrc = (PCVTGPROBELOC64)pUmod->pvProbeLocs;
for (i = 0; i < cProbeLocs; i++)
{
paDst[i].uLine = paSrc[i].uLine;
paDst[i].fEnabled = paSrc[i].fEnabled;
paDst[i].idProbe = paSrc[i].idProbe;
if (paSrc[i].pszFunction > cbStrTab)
{
rc = VERR_SUPDRV_TRACER_UMOD_STRTAB_OFF_BAD;
break;
}
paDst[i].pszFunction = pchStrTab + (uintptr_t)paSrc[i].pszFunction;
paDst[i].pProbe = (PVTGDESCPROBE)(uintptr_t)(paSrc[i].pProbe + offDelta);
}
}
/*
* 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.
*/
PRTLISTANCHOR pHashList = &pDevExt->aTrackerUmodHash[ pVtgHdr->Uuid.au8[3]
% RT_ELEMENTS(pDevExt->aTrackerUmodHash)];
rc = RTSemFastMutexRequest(pDevExt->mtxTracer);
if (RT_SUCCESS(rc))
{
pUmod->pVtgCopy = supdrvVtgFindObjectCopyLocked(pHashList, pVtgHdr, cbStrTab, fFlags);
if (!pUmod->pVtgCopy)
{
pUmod->pVtgCopy = pThis;
RTListAppend(pHashList, &pThis->ListEntry);
RTSemFastMutexRelease(pDevExt->mtxTracer);
return rc;
}
/*
* Someone raced us, free our copy and return the existing
* one instead.
*/
RTSemFastMutexRelease(pDevExt->mtxTracer);
}
}
}
}
RTMemFree(pThis);
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;
AssertReturnVoid(pUmod->iLookupTable < RT_ELEMENTS(pSession->apTpLookupTable));
AssertReturnVoid(pSession->apTpLookupTable[pUmod->iLookupTable] == pUmod);
/*
* Clear the probe IDs and disable the probes.
*/
i = pUmod->cProbeLocs;
if (pUmod->cBits == 32)
{
PVTGPROBELOC32 paProbeLocs = (PVTGPROBELOC32)pUmod->pvProbeLocs;
while (i-- > 0)
paProbeLocs[i].idProbe = 0;
}
else
{
PVTGPROBELOC64 paProbeLocs = (PVTGPROBELOC64)pUmod->pvProbeLocs;
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.
*/
RTSemFastMutexRequest(pDevExt->mtxTracer);
if (pSession->apTpLookupTable[pUmod->iLookupTable] == pUmod)
{
if (pSession->cTpProbesFiring > 0)
{
i = 0;
while (pSession->cTpProbesFiring > 0)
{
RTSemFastMutexRelease(pDevExt->mtxTracer);
i++;
if (!(i & 0xff))
SUPR0Printf("supdrvTracerUmodClearProbeIds: waiting for lookup table to go idle (i=%u)\n", i);
RTThreadSleep(10);
RTSemFastMutexRequest(pDevExt->mtxTracer);
}
}
ASMAtomicWriteNullPtr(&pSession->apTpLookupTable[pUmod->iLookupTable]);
}
RTSemFastMutexRelease(pDevExt->mtxTracer);
}
/**
* 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 iBase;
uint32_t i;
/*
* Allocate a lookup table entry.
*/
RTSemFastMutexRequest(pDevExt->mtxTracer);
for (i = 0; i < RT_ELEMENTS(pSession->apTpLookupTable); i++)
{
if (!pSession->apTpLookupTable[i])
{
pSession->apTpLookupTable[i] = pUmod;
pUmod->iLookupTable = i;
break;
}
}
RTSemFastMutexRelease(pDevExt->mtxTracer);
if (i >= RT_ELEMENTS(pSession->apTpLookupTable))
return VERR_SUPDRV_TRACER_TOO_MANY_PROVIDERS;
/*
* Set probe IDs of the usermode probe location to indicate our lookup
* table entry as well as the probe location array entry.
*/
iBase = (uint32_t)pUmod->iLookupTable << 24;
i = pUmod->cProbeLocs;
if (pUmod->cBits == 32)
{
PVTGPROBELOC32 paProbeLocs = (PVTGPROBELOC32)pUmod->pvProbeLocs;
while (i-- > 0)
paProbeLocs[i].idProbe = iBase | i;
}
else
{
PVTGPROBELOC64 paProbeLocs = (PVTGPROBELOC64)pUmod->pvProbeLocs;
while (i-- > 0)
paProbeLocs[i].idProbe = iBase | i;
}
return VINF_SUCCESS;
}
int VBOXCALL supdrvIOCtl_TracerUmodRegister(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession,
RTR3PTR R3PtrVtgHdr, RTUINTPTR uVtgHdrAddr,
RTR3PTR R3PtrStrTab, uint32_t cbStrTab,
const char *pszModName, uint32_t fFlags)
{
VTGOBJHDR Hdr;
PSUPDRVTRACERUMOD pUmod;
RTR3PTR R3PtrLock;
size_t cbLock;
uint32_t cProbeLocs;
int rc;
/*
* Validate input.
*/
if (pSession->R0Process == NIL_RTR0PROCESS)
return VERR_INVALID_CONTEXT;
if ( fFlags != SUP_TRACER_UMOD_FLAGS_EXE
&& fFlags != SUP_TRACER_UMOD_FLAGS_SHARED)
return VERR_INVALID_PARAMETER;
if (pSession->cTpProviders >= RT_ELEMENTS(pSession->apTpLookupTable))
return VERR_SUPDRV_TRACER_TOO_MANY_PROVIDERS;
if ( cbStrTab < 2
|| cbStrTab > _1M)
return VERR_SUPDRV_TRACER_UMOD_STRTAB_TOO_BIG;
/*
* Read the VTG header into a temporary buffer and perform some simple
* validations to make sure we aren't wasting our time here.
*/
rc = RTR0MemUserCopyFrom(&Hdr, R3PtrVtgHdr, sizeof(Hdr));
if (RT_FAILURE(rc))
return rc;
rc = supdrvVtgValidateHdr(&Hdr, uVtgHdrAddr, NULL, 0, true /*fUmod*/);
if (RT_FAILURE(rc))
return rc;
if (Hdr.cbProviders / sizeof(VTGDESCPROVIDER) > 2)
return VERR_SUPDRV_TRACER_TOO_MANY_PROVIDERS;
/*
* Check how much needs to be locked down and how many probe locations
* there are.
*/
if ( Hdr.offProbeLocs <= 0
|| Hdr.offProbeEnabled > (uint32_t)Hdr.offProbeLocs
|| (uint32_t)Hdr.offProbeLocs - Hdr.offProbeEnabled - Hdr.cbProbeEnabled > 128)
return VERR_SUPDRV_TRACER_UMOD_NOT_ADJACENT;
R3PtrLock = R3PtrVtgHdr + Hdr.offProbeEnabled;
cbLock = Hdr.offProbeLocs + Hdr.cbProbeLocs - Hdr.offProbeEnabled + (R3PtrLock & PAGE_OFFSET_MASK);
R3PtrLock &= ~(RTR3PTR)PAGE_OFFSET_MASK;
if (cbLock > _64K)
return VERR_SUPDRV_TRACER_UMOD_TOO_MANY_PROBES;
cProbeLocs = Hdr.cbProbeLocs / (Hdr.cBits == 32 ? sizeof(VTGPROBELOC32) : sizeof(VTGPROBELOC64));
/*
* Allocate the tracker data we keep in the session.
*/
pUmod = (PSUPDRVTRACERUMOD)RTMemAllocZ( RT_OFFSETOF(SUPDRVTRACERUMOD, aProbeLocs[cProbeLocs])
+ (Hdr.cbProbeEnabled / sizeof(uint32_t) * sizeof(SUPDRVPROBEINFO)) );
if (!pUmod)
return VERR_NO_MEMORY;
pUmod->u32Magic = SUPDRVTRACERUMOD_MAGIC;
RTListInit(&pUmod->ListEntry);
pUmod->R3PtrVtgHdr = R3PtrVtgHdr;
pUmod->pVtgCopy = NULL;
pUmod->hMemObjLock = NIL_RTR0MEMOBJ;
pUmod->hMemObjMap = NIL_RTR0MEMOBJ;
pUmod->R3PtrProbeLocs = (RTR3INTPTR)R3PtrVtgHdr + Hdr.offProbeLocs;
pUmod->iLookupTable = UINT8_MAX;
pUmod->cBits = Hdr.cBits;
pUmod->cbProbeLoc = Hdr.cBits == 32 ? sizeof(VTGPROBELOC32) : sizeof(VTGPROBELOC64);
pUmod->cProbeLocs = cProbeLocs;
/*
* 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))
{
pUmod->pacProbeEnabled = (uint32_t *)( (uintptr_t)RTR0MemObjAddress(pUmod->hMemObjMap)
+ ((uintptr_t)(R3PtrVtgHdr + Hdr.offProbeEnabled) & PAGE_OFFSET_MASK));
pUmod->pvProbeLocs = (uint8_t *)pUmod->pacProbeEnabled + Hdr.offProbeLocs - Hdr.offProbeEnabled;
/*
* Does some other process use the same module already? If so,
* share the VTG data with it. Otherwise, make a ring-0 copy it.
*/
pUmod->pVtgCopy = supdrvVtgFindObjectCopy(pDevExt, &Hdr, cbStrTab, fFlags);
if (!pUmod->pVtgCopy)
rc = supdrvVtgCreateObjectCopy(pDevExt, &Hdr, R3PtrVtgHdr, uVtgHdrAddr, R3PtrStrTab, cbStrTab, fFlags, pUmod);
if (RT_SUCCESS(rc))
{
AssertPtr(pUmod->pVtgCopy);
/*
* Grabe a place in apTpLookupTable and set the probe IDs
* accordingly.
*/
rc = supdrvTracerUmodSetProbeIds(pDevExt, pSession, pUmod);
if (RT_SUCCESS(rc))
{
/*
* Register the providers.
*/
rc = supdrvTracerRegisterVtgObj(pDevExt, &pUmod->pVtgCopy->Hdr,
NULL /*pImage*/, pSession, pUmod, pszModName);
if (RT_SUCCESS(rc))
{
RTSemFastMutexRequest(pDevExt->mtxTracer);
RTListAppend(&pSession->TpUmods, &pUmod->ListEntry);
RTSemFastMutexRelease(pDevExt->mtxTracer);
return VINF_SUCCESS;
}
/* bail out. */
supdrvTracerUmodClearProbeIds(pDevExt, pSession, pUmod);
}
supdrvVtgReleaseObjectCopy(pDevExt, pUmod->pVtgCopy);
}
RTR0MemObjFree(pUmod->hMemObjMap, false /*fFreeMappings*/);
}
RTR0MemObjFree(pUmod->hMemObjLock, false /*fFreeMappings*/);
}
pUmod->u32Magic = ~SUPDRVTRACERUMOD_MAGIC;
RTMemFree(pUmod);
return rc;
}
int VBOXCALL supdrvIOCtl_TracerUmodDeregister(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, RTR3PTR R3PtrVtgHdr)
{
PSUPDRVTRACERUMOD pUmod = NULL;
uint32_t i;
int rc;
/*
* Validate the request.
*/
RTSemFastMutexRequest(pDevExt->mtxTracer);
for (i = 0; i < RT_ELEMENTS(pSession->apTpLookupTable); i++)
{
pUmod = pSession->apTpLookupTable[i];
if ( pUmod
&& pUmod->u32Magic == SUPDRVTRACERUMOD_MAGIC
&& pUmod->R3PtrVtgHdr == R3PtrVtgHdr)
break;
}
RTSemFastMutexRelease(pDevExt->mtxTracer);
if (pUmod)
{
SUPDRVTPPROVIDER *pProvNext;
SUPDRVTPPROVIDER *pProv;
/*
* 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.
*/
supdrvTracerUmodClearProbeIds(pDevExt, pSession, pUmod);
/*
* Deregister providers related to the VTG object.
*/
RTSemFastMutexRequest(pDevExt->mtxTracer);
RTListForEachSafe(&pSession->TpProviders, pProv, pProvNext, SUPDRVTPPROVIDER, SessionListEntry)
{
if (pProv->pUmod == pUmod)
supdrvTracerDeregisterVtgObj(pDevExt, pProv);
}
RTSemFastMutexRelease(pDevExt->mtxTracer);
/*
* Destroy the Umod object.
*/
pUmod->u32Magic = ~SUPDRVTRACERUMOD_MAGIC;
supdrvVtgReleaseObjectCopy(pDevExt, pUmod->pVtgCopy);
RTR0MemObjFree(pUmod->hMemObjMap, false /*fFreeMappings*/);
RTR0MemObjFree(pUmod->hMemObjLock, false /*fFreeMappings*/);
RTMemFree(pUmod);
}
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.
*/
PSUPDRVTRACERUMOD pUmod;
uint32_t const iLookupTable = pCtx->idProbe >> 24;
uint32_t const iProbeLoc = pCtx->idProbe & UINT32_C(0x00ffffff);
if (RT_UNLIKELY( !pDevExt->pTracerOps
|| pDevExt->fTracerUnloading))
return;
if (RT_UNLIKELY(iLookupTable >= RT_ELEMENTS(pSession->apTpLookupTable)))
return;
if (RT_UNLIKELY( pCtx->cBits != 32
&& pCtx->cBits != 64))
return;
ASMAtomicIncU32(&pSession->cTpProviders);
pUmod = pSession->apTpLookupTable[iLookupTable];
if (RT_LIKELY(pUmod))
{
if (RT_LIKELY( pUmod->u32Magic == SUPDRVTRACERUMOD_MAGIC
&& iProbeLoc < pUmod->cProbeLocs
&& pCtx->cBits == pUmod->cBits))
{
#if 0 /* This won't work for RC modules. */
RTR3PTR R3PtrProbeLoc = pUmod->R3PtrProbeLocs + iProbeLoc * pUmod->cbProbeLoc;
if (RT_LIKELY( (pCtx->cBits == 32 ? (RTR3PTR)pCtx->u.X86.uVtgProbeLoc : pCtx->u.Amd64.uVtgProbeLoc)
== R3PtrProbeLoc))
#endif
{
if (RT_LIKELY(pUmod->aProbeLocs[iProbeLoc].fEnabled))
{
PSUPDRVVTGCOPY pVtgCopy;
ASMAtomicIncU32(&pDevExt->cTracerCallers);
pVtgCopy = pUmod->pVtgCopy;
if (RT_LIKELY( pDevExt->pTracerOps
&& !pDevExt->fTracerUnloading
&& pVtgCopy))
{
PCVTGPROBELOC pProbeLocRO;
pProbeLocRO = (PCVTGPROBELOC)((uintptr_t)&pVtgCopy->Hdr + pVtgCopy->Hdr.offProbeLocs) + iProbeLoc;
pCtx->idProbe = pUmod->aProbeLocs[iProbeLoc].idProbe;
pDevExt->pTracerOps->pfnProbeFireUser(pDevExt->pTracerOps, pSession, pCtx, &pVtgCopy->Hdr, pProbeLocRO);
}
ASMAtomicDecU32(&pDevExt->cTracerCallers);
}
}
}
}
ASMAtomicDecU32(&pSession->cTpProviders);
}
SUPR0DECL(void) SUPR0TracerUmodProbeFire(PSUPDRVSESSION pSession, PSUPDRVTRACERUSRCTX pCtx)
{
AssertReturnVoid(SUP_IS_SESSION_VALID(pSession));
AssertPtrReturnVoid(pCtx);
supdrvTracerUmodProbeFire(pSession->pDevExt, pSession, pCtx);
}
void VBOXCALL supdrvIOCtl_TracerUmodProbeFire(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPDRVTRACERUSRCTX pCtx)
{
supdrvTracerUmodProbeFire(pDevExt, pSession, 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)
{
RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
int rc;
RTSemFastMutexRequest(pDevExt->mtxTracer);
if (!pSession->uTracerData)
{
if (pDevExt->pTracerOps)
{
if (pDevExt->pTracerSession != pSession)
{
if (!pDevExt->fTracerUnloading)
{
if (pSession->hTracerCaller == NIL_RTNATIVETHREAD)
{
pDevExt->cTracerOpens++;
pSession->uTracerData = ~(uintptr_t)0;
pSession->hTracerCaller = hNativeSelf;
RTSemFastMutexRelease(pDevExt->mtxTracer);
rc = pDevExt->pTracerOps->pfnTracerOpen(pDevExt->pTracerOps, pSession, uCookie, uArg, &pSession->uTracerData);
RTSemFastMutexRequest(pDevExt->mtxTracer);
if (RT_FAILURE(rc))
{
pDevExt->cTracerOpens--;
pSession->uTracerData = 0;
}
pSession->hTracerCaller = NIL_RTNATIVETHREAD;
}
else
rc = VERR_SUPDRV_TRACER_SESSION_BUSY;
}
else
rc = VERR_SUPDRV_TRACER_UNLOADING;
}
else
rc = VERR_SUPDRV_TRACER_CANNOT_OPEN_SELF;
}
else
rc = VERR_SUPDRV_TRACER_NOT_PRESENT;
}
else
rc = VERR_SUPDRV_TRACER_ALREADY_OPENED;
RTSemFastMutexRelease(pDevExt->mtxTracer);
return rc;
}
/**
* Closes the tracer.
*
* @returns VBox status code.
* @param pDevExt The device extension structure.
* @param pSession The current session.
*/
int VBOXCALL supdrvIOCtl_TracerClose(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession)
{
RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
int rc;
RTSemFastMutexRequest(pDevExt->mtxTracer);
if (pSession->uTracerData)
{
Assert(pDevExt->cTracerOpens > 0);
if (pDevExt->pTracerOps)
{
if (pSession->hTracerCaller == NIL_RTNATIVETHREAD)
{
uintptr_t uTracerData = pSession->uTracerData;
pSession->uTracerData = 0;
pSession->hTracerCaller = hNativeSelf;
RTSemFastMutexRelease(pDevExt->mtxTracer);
pDevExt->pTracerOps->pfnTracerClose(pDevExt->pTracerOps, pSession, uTracerData);
rc = VINF_SUCCESS;
RTSemFastMutexRequest(pDevExt->mtxTracer);
pSession->hTracerCaller = NIL_RTNATIVETHREAD;
Assert(pDevExt->cTracerOpens > 0);
pDevExt->cTracerOpens--;
}
else
rc = VERR_SUPDRV_TRACER_SESSION_BUSY;
}
else
{
rc = VERR_SUPDRV_TRACER_NOT_PRESENT;
pSession->uTracerData = 0;
Assert(pDevExt->cTracerOpens > 0);
pDevExt->cTracerOpens--;
}
}
else
rc = VERR_SUPDRV_TRACER_NOT_OPENED;
RTSemFastMutexRelease(pDevExt->mtxTracer);
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)
{
RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
int rc;
*piRetVal = 0;
RTSemFastMutexRequest(pDevExt->mtxTracer);
if (pSession->uTracerData)
{
Assert(pDevExt->cTracerOpens > 0);
if (pDevExt->pTracerOps)
{
if (!pDevExt->fTracerUnloading)
{
if (pSession->hTracerCaller == NIL_RTNATIVETHREAD)
{
uintptr_t uTracerData = pSession->uTracerData;
pDevExt->cTracerOpens++;
pSession->hTracerCaller = hNativeSelf;
RTSemFastMutexRelease(pDevExt->mtxTracer);
rc = pDevExt->pTracerOps->pfnTracerIoCtl(pDevExt->pTracerOps, pSession, uTracerData, uCmd, uArg, piRetVal);
RTSemFastMutexRequest(pDevExt->mtxTracer);
pSession->hTracerCaller = NIL_RTNATIVETHREAD;
Assert(pDevExt->cTracerOpens > 0);
pDevExt->cTracerOpens--;
}
else
rc = VERR_SUPDRV_TRACER_SESSION_BUSY;
}
else
rc = VERR_SUPDRV_TRACER_UNLOADING;
}
else
rc = VERR_SUPDRV_TRACER_NOT_PRESENT;
}
else
rc = VERR_SUPDRV_TRACER_NOT_OPENED;
RTSemFastMutexRelease(pDevExt->mtxTracer);
return rc;
}
/**
* Early module initialization hook.
*
* @returns VBox status code.
* @param pDevExt The device extension structure.
*/
int VBOXCALL supdrvTracerInit(PSUPDRVDEVEXT pDevExt)
{
/*
* Initialize the tracer.
*/
int rc = RTSemFastMutexCreate(&pDevExt->mtxTracer);
if (RT_SUCCESS(rc))
{
uint32_t i;
pDevExt->TracerHlp.uVersion = SUPDRVTRACERHLP_VERSION;
/** @todo */
pDevExt->TracerHlp.uEndVersion = SUPDRVTRACERHLP_VERSION;
RTListInit(&pDevExt->TracerProviderList);
RTListInit(&pDevExt->TracerProviderZombieList);
for (i = 0; i < RT_ELEMENTS(pDevExt->aTrackerUmodHash); i++)
RTListInit(&pDevExt->aTrackerUmodHash[i]);
#ifdef VBOX_WITH_NATIVE_DTRACE
pDevExt->pTracerOps = supdrvDTraceInit();
if (pDevExt->pTracerOps)
g_pfnSupdrvProbeFireKernel = (PFNRT)pDevExt->pTracerOps->pfnProbeFireKernel;
#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;
SUPR0Printf("supdrvTracerInit: supdrvTracerRegisterVtgObj failed with rc=%d\n", rc);
RTSemFastMutexDestroy(pDevExt->mtxTracer);
#else
return VINF_SUCCESS;
#endif
}
pDevExt->mtxTracer = NIL_RTSEMFASTMUTEX;
return rc;
}
/**
* Late module termination hook.
*
* @returns VBox status code.
* @param pDevExt The device extension structure.
*/
void VBOXCALL supdrvTracerTerm(PSUPDRVDEVEXT pDevExt)
{
LOG_TRACER(("supdrvTracerTerm\n"));
supdrvTracerRemoveAllProviders(pDevExt);
RTSemFastMutexDestroy(pDevExt->mtxTracer);
pDevExt->mtxTracer = NIL_RTSEMFASTMUTEX;
LOG_TRACER(("supdrvTracerTerm: Done\n"));
}