VBoxDTraceR0.cpp revision e3de7948c4ce365719e80faad164a0fad245bbbb
/* $Id$ */
/** @file
* VBoxDTraceR0.
*/
/*
* Copyright (c) 2012 bird
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/asm-amd64-x86.h>
#include <iprt/semaphore.h>
#include <iprt/spinlock.h>
#include <sys/dtrace_impl.h>
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
# define HAVE_RTMEMALLOCEX_FEATURES
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** Caller indicator. */
typedef enum VBOXDTCALLER
{
} VBOXDTCALLER;
/**
* Stack data used for thread structure and such.
*
* This is planted in every external entry point and used to emulate solaris
* curthread, CRED, curproc and similar. It is also used to get at the
* uncached probe arguments.
*/
typedef struct VBoxDtStackData
{
/** Eyecatcher no. 1 (VBDT_STACK_DATA_MAGIC2). */
/** Eyecatcher no. 2 (VBDT_STACK_DATA_MAGIC2). */
/** The format of the caller specific data. */
/** Caller specific data. */
union
{
/** kVBoxDtCaller_ProbeFireKernel. */
struct
{
/** The caller. */
/** Pointer to the stack arguments of a probe function call. */
/** kVBoxDtCaller_ProbeFireUser. */
struct
{
/** The user context. */
} u;
/** Credentials allocated by VBoxDtGetCurrentCreds. */
struct VBoxDtCred *pCred;
/** Thread structure currently being held by this thread. */
struct VBoxDtThread *pThread;
/** Pointer to this structure.
* This is the final bit of integrity checking. */
struct VBoxDtStackData *pSelf;
/** Pointer to the on-stack thread specific data. */
typedef VBDTSTACKDATA *PVBDTSTACKDATA;
/** The first magic value. */
/** The second magic value. */
/** The alignment of the stack data.
* The data doesn't require more than sizeof(uintptr_t) alignment, but the
* greater alignment the quicker lookup. */
#define VBDT_STACK_DATA_ALIGN 32
/** Plants the stack data. */
#define VBDT_SETUP_STACK_DATA(a_enmCaller) \
/** Passifies the stack data and frees up resource held within it. */
#define VBDT_CLEAR_STACK_DATA() \
do \
{ \
pStackData->u32Magic1 = 0; \
pStackData->u32Magic2 = 0; \
if (pStackData->pCred) \
if (pStackData->pThread) \
} while (0)
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Per CPU information */
/** Dummy mutex. */
struct VBoxDtMutex g_DummyMtx;
/** Pointer to the tracer helpers provided by VBoxDrv. */
static PCSUPDRVTRACERHLP g_pVBoxDTraceHlp;
#if 0
void (*dtrace_cpu_init)(processorid_t);
void (*dtrace_modload)(struct modctl *);
void (*dtrace_modunload)(struct modctl *);
void (*dtrace_helpers_cleanup)(void);
void (*dtrace_cpustart_init)(void);
void (*dtrace_cpustart_fini)(void);
void (*dtrace_cpc_fire)(uint64_t);
void (*dtrace_debugger_init)(void);
void (*dtrace_debugger_fini)(void);
#endif
/**
* Gets the stack data.
*
* @returns Pointer to the stack data. Never NULL.
*/
static PVBDTSTACKDATA vboxDtGetStackData(void)
{
for (;;)
{
return pData;
}
}
{
/** @todo ? */
}
/**
* Dummy callback used by dtrace_sync.
*/
{
}
/**
* Synchronzie across all CPUs (expensive).
*/
void dtrace_sync(void)
{
}
/**
* Fetch a 8-bit "word" from userland.
*
* @return The byte value.
* @param pvUserAddr The userland address.
*/
{
if (RT_FAILURE(rc))
{
u8 = 0;
}
return u8;
}
/**
* Fetch a 16-bit word from userland.
*
* @return The word value.
* @param pvUserAddr The userland address.
*/
{
if (RT_FAILURE(rc))
{
u16 = 0;
}
return u16;
}
/**
* Fetch a 32-bit word from userland.
*
* @return The dword value.
* @param pvUserAddr The userland address.
*/
{
if (RT_FAILURE(rc))
{
u32 = 0;
}
return u32;
}
/**
* Fetch a 64-bit word from userland.
*
* @return The qword value.
* @param pvUserAddr The userland address.
*/
{
if (RT_FAILURE(rc))
{
u64 = 0;
}
return u64;
}
/** copyin implementation */
{
}
/** copyout implementation */
{
}
/**
* Copy data from userland into the kernel.
*
* @param uUserAddr The userland address.
* @param uKrnlAddr The kernel buffer address.
* @param cb The number of bytes to copy.
* @param pfFlags Pointer to the relevant cpuc_dtrace_flags.
*/
void dtrace_copyin( uintptr_t uUserAddr, uintptr_t uKrnlAddr, size_t cb, volatile uint16_t *pfFlags)
{
if (RT_FAILURE(rc))
{
}
}
/**
* Copy data from the kernel into userlad.
*
* @param uKrnlAddr The kernel buffer address.
* @param uUserAddr The userland address.
* @param cb The number of bytes to copy.
* @param pfFlags Pointer to the relevant cpuc_dtrace_flags.
*/
void dtrace_copyout( uintptr_t uKrnlAddr, uintptr_t uUserAddr, size_t cb, volatile uint16_t *pfFlags)
{
if (RT_FAILURE(rc))
{
}
}
/**
* Copy a string from userland into the kernel.
*
* @param uUserAddr The userland address.
* @param uKrnlAddr The kernel buffer address.
* @param cbMax The maximum number of bytes to copy. May stop
* earlier if zero byte is encountered.
* @param pfFlags Pointer to the relevant cpuc_dtrace_flags.
*/
void dtrace_copyinstr( uintptr_t uUserAddr, uintptr_t uKrnlAddr, size_t cbMax, volatile uint16_t *pfFlags)
{
if (!cbMax)
return;
if (RT_FAILURE(rc))
{
/* Byte by byte - lazy bird! */
{
if (RT_FAILURE(rc))
{
return;
}
return;
off++;
}
}
}
/**
* Copy a string from the kernel and into user land.
*
* @param uKrnlAddr The kernel string address.
* @param uUserAddr The userland address.
* @param cbMax The maximum number of bytes to copy. Will stop
* earlier if zero byte is encountered.
* @param pfFlags Pointer to the relevant cpuc_dtrace_flags.
*/
void dtrace_copyoutstr(uintptr_t uKrnlAddr, uintptr_t uUserAddr, size_t cbMax, volatile uint16_t *pfFlags)
{
}
/**
* Get the caller @a cCallFrames call frames up the stack.
*
* @returns The caller's return address or ~(uintptr_t)0.
* @param cCallFrames The number of frames.
*/
{
return ~(uintptr_t)0;
}
/**
* Get argument number @a iArg @a cCallFrames call frames up the stack.
*
* @returns The caller's return address or ~(uintptr_t)0.
* @param iArg The argument to get.
* @param cCallFrames The number of frames.
*/
{
return UINT64_MAX;
}
/**
* Produce a traceback of the kernel stack.
*
* @param paPcStack Where to return the program counters.
* @param cMaxFrames The maximum number of PCs to return.
* @param cSkipFrames The number of artificial callstack frames to
* skip at the top.
* @param pIntr Not sure what this is...
*/
{
int iFrame = 0;
while (iFrame < cMaxFrames)
{
iFrame++;
}
}
/**
* Get the number of call frames on the stack.
*
* @returns The stack depth.
* @param cSkipFrames The number of artificial callstack frames to
* skip at the top.
*/
int dtrace_getstackdepth(int cSkipFrames)
{
return 1;
}
/**
* Produce a traceback of the userland stack.
*
* @param paPcStack Where to return the program counters.
* @param paFpStack Where to return the frame pointers.
* @param cMaxFrames The maximum number of frames to return.
*/
{
int iFrame = 0;
while (iFrame < cMaxFrames)
{
iFrame++;
}
}
/**
* Produce a traceback of the userland stack.
*
* @param paPcStack Where to return the program counters.
* @param cMaxFrames The maximum number of frames to return.
*/
{
int iFrame = 0;
while (iFrame < cMaxFrames)
{
iFrame++;
}
}
/**
* Computes the depth of the userland stack.
*/
int dtrace_getustackdepth(void)
{
return 0;
}
/**
*
* @returns Current level.
*/
int dtrace_getipl(void)
{
#ifdef RT_ARCH_AMD64
/* CR8 is normally the same as IRQL / IPL on AMD64. */
return ASMGetCR8();
#else
/* Just fake it on x86. */
return !ASMIntAreEnabled();
#endif
}
/**
* Get current monotonic timestamp.
*
* @returns Timestamp, nano seconds.
*/
hrtime_t dtrace_gethrtime(void)
{
return RTTimeNanoTS();
}
/**
* Get current walltime.
*
* @returns Timestamp, nano seconds.
*/
hrtime_t dtrace_gethrestime(void)
{
/** @todo try get better resolution here somehow ... */
}
/**
* DTrace panic routine.
*
* @param pszFormat Panic message.
* @param va Arguments to the panic message.
*/
{
for (;;)
{
*pchCrash = '\0';
}
}
/**
* DTrace panic routine.
*
* @param pszFormat Panic message.
* @param ... Arguments to the panic message.
*/
void VBoxDtPanic(const char *pszFormat, ...)
{
}
/**
* DTrace kernel message routine.
*
* @param pszFormat Kernel message.
* @param ... Arguments to the panic message.
*/
{
}
/** uprintf implementation */
void VBoxDtUPrintf(const char *pszFormat, ...)
{
}
/** vuprintf implementation */
{
}
/* CRED implementation. */
cred_t *VBoxDtGetCurrentCreds(void)
{
{
struct VBoxDtCred *pCred;
#ifdef HAVE_RTMEMALLOCEX_FEATURES
#else
#endif
/** @todo get the right creds on unix systems. */
}
}
/* crhold implementation */
{
}
/* crfree implementation */
{
if (!cRefs)
}
/** Spinlock protecting the thread structures. */
/** List of threads by usage age. */
static RTLISTANCHOR g_ThreadAgeList;
/** Hash table for looking up thread structures. */
/** Fake kthread_t structures.
* The size of this array is making horrible ASSUMPTIONS about the number of
* thread in the system that will be subjected to DTracing. */
static int vboxDtInitThreadDb(void)
{
if (RT_FAILURE(rc))
return rc;
{
}
return VINF_SUCCESS;
}
static void vboxDtTermThreadDb(void)
{
}
/* curthread implementation, providing a fake kthread_t. */
struct VBoxDtThread *VBoxDtGetCurrentThread(void)
{
/*
* Once we've retrieved a thread, we hold on to it until the thread exits
* the VBoxDTrace module.
*/
{
}
/*
* Lookup the thread in the hash table.
*/
while (pThread)
{
{
{
/* Re-initialize the reused thread. */
pThread->t_dtrace_vtime = 0;
pThread->t_dtrace_start = 0;
pThread->t_dtrace_stop = 0;
pThread->t_dtrace_scrpc = 0;
pThread->t_dtrace_astpc = 0;
pThread->t_predcache = 0;
}
/* Hold the thread in the on-stack data, making sure it does not
get reused till the thread leaves VBoxDTrace. */
return pThread;
}
}
/*
* Unknown thread. Allocate a new entry, recycling unused or old ones.
*/
{
else
{
{
{
break;
}
}
}
}
/*
* Initialize the data.
*/
pThread->t_dtrace_vtime = 0;
pThread->t_dtrace_start = 0;
pThread->t_dtrace_stop = 0;
pThread->t_dtrace_scrpc = 0;
pThread->t_dtrace_astpc = 0;
pThread->t_predcache = 0;
/*
* Add it to the hash as well as the on-stack data.
*/
return pThread;
}
/**
* Called by the stack data destructor.
*
* @param pThread The thread to release.
*
*/
{
}
/*
*
* Virtual Memory / Resource Allocator.
* Virtual Memory / Resource Allocator.
* Virtual Memory / Resource Allocator.
*
*/
/** The number of bits per chunk.
* @remarks The 32 bytes are for heap headers and such like. */
/**
* Resource allocator chunk.
*/
typedef struct VBoxDtVMemChunk
{
/** The ordinal (unbased) of the first item. */
/** The current number of free items in this chunk. */
/** The allocation bitmap. */
/** Pointer to a resource allocator chunk. */
typedef VBOXDTVMEMCHUNK *PVBOXDTVMEMCHUNK;
/**
* Resource allocator instance.
*/
typedef struct VBoxDtVMem
{
/** Spinlock protecting the data. */
/** Magic value. */
/** The current number of free items in the chunks. */
/** The current number of chunks that we have allocated. */
/** The configured resource base. */
/** The configured max number of items. */
/** The size of the apChunks array. */
/** Array of chunk pointers.
* (The size is determined at creation.) */
} VBOXDTVMEM;
/** Pointer to a resource allocator instance. */
typedef VBOXDTVMEM *PVBOXDTVMEM;
/** Magic value for the VBOXDTVMEM structure. */
/* vmem_create implementation */
{
/*
* Assert preconditions of this implementation.
*/
/*
* Allocate the instance.
*/
if (cb % VBOXDTVMEMCHUNK_BITS)
cChunks++;
if (!pThis)
return NULL;
if (RT_FAILURE(rc))
{
return NULL;
}
pThis->cCurChunks = 0;
return pThis;
}
/* vmem_destroy implementation */
{
if (!pThis)
return;
/*
* Invalidate the instance.
*/
/*
* Free the chunks, then the instance.
*/
while (iChunk-- > 0)
{
}
}
/* vmem_alloc implementation */
{
/*
* Validate input.
*/
/*
* Allocation loop.
*/
for (;;)
{
{
{
{
NULL);
}
}
}
/* Out of resources? */
break;
/*
* Allocate another chunk.
*/
uint32_t const iFirstBit = cChunks > 0 ? pThis->apChunks[cChunks - 1]->iFirst + VBOXDTVMEMCHUNK_BITS : 0;
if (!pChunk)
return NULL;
if (cFreeBits != VBOXDTVMEMCHUNK_BITS)
{
/* lazy bird. */
while (iBit < VBOXDTVMEMCHUNK_BITS)
{
iBit++;
}
}
/*
* Insert the new chunk. If someone raced us here, we'll drop it to
* avoid wasting resources.
*/
{
}
else
{
}
}
return NULL;
}
/* vmem_free implementation */
{
/*
* Validate input.
*/
/*
* Free it.
*/
{
}
}
/*
*
* Memory Allocators.
* Memory Allocators.
* Memory Allocators.
*
*/
/* kmem_alloc implementation */
{
void *pvMem;
#ifdef HAVE_RTMEMALLOCEX_FEATURES
#else
uint32_t fMemAllocFlags = 0;
#endif
return pvMem;
}
/* kmem_zalloc implementation */
{
void *pvMem;
#ifdef HAVE_RTMEMALLOCEX_FEATURES
uint32_t fMemAllocFlags = (fFlags & KM_NOSLEEP ? RTMEMALLOCEX_FLAGS_ANY_CTX : 0) | RTMEMALLOCEX_FLAGS_ZEROED;
#else
#endif
return pvMem;
}
/* kmem_free implementation */
{
}
/**
* Memory cache mockup structure.
* No slab allocator here!
*/
struct VBoxDtMemCache
{
};
/* Limited kmem_cache_create implementation. */
{
/*
* Check the input.
*/
/*
* Create a parameter container. Don't bother with anything fancy here yet,
* just get something working.
*/
if (!pThis)
return NULL;
return pThis;
}
/* Limited kmem_cache_destroy implementation. */
{
}
/* kmem_cache_alloc implementation. */
{
void *pvMem;
#ifdef HAVE_RTMEMALLOCEX_FEATURES
uint32_t fMemAllocFlags = (fFlags & KM_NOSLEEP ? RTMEMALLOCEX_FLAGS_ANY_CTX : 0) | RTMEMALLOCEX_FLAGS_ZEROED;
#else
#endif
return pvMem;
}
/* kmem_cache_free implementation. */
{
}
/*
*
* Mutex Semaphore Wrappers.
*
*/
/** Initializes a mutex. */
{
if (RT_SUCCESS(rc))
return 0;
return -1;
}
/** Deletes a mutex. */
{
return;
}
/* mutex_enter implementation */
{
if (pMtx == &g_DummyMtx)
return;
}
/* mutex_exit implementation */
{
if (pMtx == &g_DummyMtx)
return;
}
/* MUTEX_HELD implementation */
{
AssertPtrReturn(pMtx, false);
if (pMtx == &g_DummyMtx)
return true;
}
/*
*
* Helpers for handling VTG structures.
* Helpers for handling VTG structures.
* Helpers for handling VTG structures.
*
*/
/**
* Converts an attribute from VTG description speak to DTrace.
*
* @param pDtAttr The DTrace attribute (dst).
* @param pVtgAttr The VTG attribute descriptor (src).
*/
{
}
/**
* Gets a string from the string table.
*
* @returns Pointer to the string.
* @param pVtgHdr The VTG object header.
* @param offStrTab The string table offset.
*/
{
}
/*
*
* DTrace Provider Interface.
* DTrace Provider Interface.
* DTrace Provider Interface.
*
*/
/**
* @callback_method_impl{dtrace_pops_t,dtps_provide}
*/
{
char *pszFnNmBuf;
if (pDtProbeDesc)
return; /* We don't generate probes, so never mind these requests. */
return;
return;
/* Need a buffer for extracting the function names and mangling them in
case of collision. */
if (!pszFnNmBuf)
return;
/*
* Itereate the probe location list and register all probes related to
* this provider.
*/
{
{
/* The function name normally needs to be stripped since we're
using C++ compilers for most of the code. ASSUMES nobody are
typedef'ing properly them. */
if (psz)
{
/* skip blanks preceeding the parameter parenthesis. */
psz--;
/* Find the start of the function name. */
{
break;
pszFunc--;
}
}
else
/* Look up the probe, if we have one in the same function, mangle
the function name a little to avoid having to deal with having
multiple location entries with the same probe ID. (lazy bird) */
{
{
unsigned iOrd = 2;
while (iOrd < 128)
{
break;
iOrd++;
}
if (iOrd >= 128)
{
LogRel(("VBoxDrv: More than 128 duplicate probe location instances %s at line %u in function %s [%s], probe %s\n",
continue;
}
}
}
/* Create the probe. */
}
pProbeLoc++;
}
}
/**
* @callback_method_impl{dtrace_pops_t,dtps_enable}
*/
{
{
{
}
}
return 0;
}
/**
* @callback_method_impl{dtrace_pops_t,dtps_disable}
*/
{
{
{
}
}
}
/**
* @callback_method_impl{dtrace_pops_t,dtps_getargdesc}
*/
{
{
PVTGDESCARGLIST pArgList = (PVTGDESCARGLIST)((uintptr_t)pProv->pHdr->paArgLists + pProbeDesc->offArgList);
{
{
/** @todo mapping */
return;
}
}
}
}
/**
* @callback_method_impl{dtrace_pops_t,dtps_getargval}
*/
{
{
{
}
{
}
else
AssertFailed();
}
return UINT64_MAX;
}
/**
* @callback_method_impl{dtrace_pops_t,dtps_destroy}
*/
{
{
}
}
/**
* DTrace provider method table.
*/
static const dtrace_pops_t g_vboxDtVtgProvOps =
{
/* .dtps_provide = */ vboxDtPOps_Provide,
/* .dtps_provide_module = */ NULL,
/* .dtps_enable = */ vboxDtPOps_Enable,
/* .dtps_disable = */ vboxDtPOps_Disable,
/* .dtps_suspend = */ NULL,
/* .dtps_resume = */ NULL,
/* .dtps_getargdesc = */ vboxDtPOps_GetArgDesc,
/* .dtps_getargval = */ vboxDtPOps_GetArgVal,
/* .dtps_usermode = */ NULL,
/* .dtps_destroy = */ vboxDtPOps_Destroy
};
/*
*
* Support Driver Tracer Interface.
* Support Driver Tracer Interface.
* Support Driver Tracer Interface.
*
*/
/**
* interface_method_impl{SUPDRVTRACERREG,pfnProbeFireUser}
*/
static DECLCALLBACK(void) vbdt_ProbeFireKernel(struct VTGPROBELOC *pVtgProbeLoc, uintptr_t uArg0, uintptr_t uArg1, uintptr_t uArg2,
{
return ;
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnProbeFireUser}
*/
static DECLCALLBACK(void) vbdt_ProbeFireUser(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, PCSUPDRVTRACERUSRCTX pCtx)
{
else
AssertFailed();
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnTracerOpen}
*/
static DECLCALLBACK(int) vbdt_TracerOpen(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, uint32_t uCookie, uintptr_t uArg,
{
return VERR_INVALID_MAGIC;
if (uArg)
return VERR_INVALID_PARAMETER;
return RTErrConvertFromErrno(rc);
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnTracerClose}
*/
static DECLCALLBACK(int) vbdt_TracerIoCtl(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, uintptr_t uSessionData,
{
return RTErrConvertFromErrno(rc);
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnTracerClose}
*/
static DECLCALLBACK(void) vbdt_TracerClose(PCSUPDRVTRACERREG pThis, PSUPDRVSESSION pSession, uintptr_t uSessionData)
{
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnProviderRegister}
*/
static DECLCALLBACK(int) vbdt_ProviderRegister(PCSUPDRVTRACERREG pThis, PSUPDRVVDTPROVIDERCORE pCore)
{
AssertReturn(pCore->TracerData.DTrace.idProvider == UINT32_MAX || pCore->TracerData.DTrace.idProvider == 0,
&DtAttrs,
NULL /* cred */,
&idProvider);
if (!rc)
{
rc = VINF_SUCCESS;
}
else
return rc;
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnProviderDeregister}
*/
static DECLCALLBACK(int) vbdt_ProviderDeregister(PCSUPDRVTRACERREG pThis, PSUPDRVVDTPROVIDERCORE pCore)
{
if (!rc)
{
rc = VINF_SUCCESS;
}
else
{
rc = VERR_TRY_AGAIN;
}
return rc;
}
/**
* interface_method_impl{SUPDRVTRACERREG,pfnProviderDeregisterZombie}
*/
static DECLCALLBACK(int) vbdt_ProviderDeregisterZombie(PCSUPDRVTRACERREG pThis, PSUPDRVVDTPROVIDERCORE pCore)
{
if (!rc)
{
rc = VINF_SUCCESS;
}
else
{
rc = VERR_TRY_AGAIN;
}
return rc;
}
/**
* The tracer registration record of the VBox DTrace implementation
*/
static SUPDRVTRACERREG g_VBoxDTraceReg =
{
};
/**
* Module termination code.
*
* @param hMod Opque module handle.
*/
{
}
/**
* Module initialization code.
*
* @param hMod Opque module handle.
*/
{
int rc = dtrace_attach();
if (rc == DDI_SUCCESS)
{
if (RT_SUCCESS(rc))
{
return rc;
}
}
else
{
}
return rc;
}