fasttrap.c revision dafb55404788372f219f7456c26a732d130439d5
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/fasttrap.h>
#include <sys/fasttrap_impl.h>
#include <sys/fasttrap_isa.h>
#include <sys/dtrace_impl.h>
#include <sys/sysmacros.h>
#include <vm/seg_kmem.h>
/*
* User-Land Trap-Based Tracing
* ----------------------------
*
* The fasttrap provider allows DTrace consumers to instrument any user-level
* instruction to gather data; this includes probes with semantic
* signifigance like entry and return as well as simple offsets into the
* function. While the specific techniques used are very ISA specific, the
* methodology is generalizable to any architecture.
*
*
* The General Methodology
* -----------------------
*
* With the primary goal of tracing every user-land instruction and the
* limitation that we can't trust user space so don't want to rely on much
* information there, we begin by replacing the instructions we want to trace
* with trap instructions. Each instruction we overwrite is saved into a hash
* table keyed by process ID and pc address. When we enter the kernel due to
* this trap instruction, we need the effects of the replaced instruction to
* appear to have occurred before we proceed with the user thread's
* execution.
*
* Each user level thread is represented by a ulwp_t structure which is
* always easily accessible through a register. The most basic way to produce
* the effects of the instruction we replaced is to copy that instruction out
* to a bit of scratch space reserved in the user thread's ulwp_t structure
* (a sort of kernel-private thread local storage), set the PC to that
* scratch space and single step. When we reenter the kernel after single
* stepping the instruction we must then adjust the PC to point to what would
* normally be the next instruction. Of course, special care must be taken
* for branches and jumps, but these represent such a small fraction of any
* instruction set that writing the code to emulate these in the kernel is
* not too difficult.
*
* Return probes may require several tracepoints to trace every return site,
* and, conversely, each tracepoint may activate several probes (the entry
* and offset 0 probes, for example). To solve this muliplexing problem,
* tracepoints contain lists of probes to activate and probes contain lists
* of tracepoints to enable. If a probe is activated, it adds its ID to
* existing tracepoints or creates new ones as necessary.
*
* Most probes are activated _before_ the instruction is executed, but return
* probes are activated _after_ the effects of the last instruction of the
* function are visible. Return probes must be fired _after_ we have
* single-stepped the instruction whereas all other probes are fired
* beforehand.
*/
static dev_info_t *fasttrap_devi;
static dtrace_provider_id_t fasttrap_id;
static timeout_id_t fasttrap_timeout;
static kmutex_t fasttrap_cleanup_mtx;
static uint_t fasttrap_cleanup_work;
/*
* Generation count on modifications to the global tracepoint lookup table.
*/
static volatile uint64_t fasttrap_mod_gen;
/*
* When the fasttrap provider is loaded, fasttrap_max is set to either
* FASTTRAP_MAX_DEFAULT or the value for fasttrap-max-probes in the
* fasttrap.conf file. Each time a probe is created, fasttrap_total is
* incremented by the number of tracepoints that may be associated with that
* probe; fasttrap_total is capped at fasttrap_max.
*/
#define FASTTRAP_MAX_DEFAULT 250000
static uint32_t fasttrap_max;
static uint32_t fasttrap_total;
#define FASTTRAP_TPOINTS_DEFAULT_SIZE 0x4000
#define FASTTRAP_PROVIDERS_DEFAULT_SIZE 0x100
#define FASTTRAP_PROCS_DEFAULT_SIZE 0x100
#define FASTTRAP_PID_NAME "pid"
static fasttrap_hash_t fasttrap_provs;
static fasttrap_hash_t fasttrap_procs;
static int fasttrap_count; /* ref count */
static int fasttrap_pid_count; /* pid ref count */
#define FASTTRAP_ENABLE_FAIL 1
#define FASTTRAP_ENABLE_PARTIAL 2
const dtrace_pattr_t *);
static void fasttrap_provider_retire(pid_t, const char *, int);
static void fasttrap_provider_free(fasttrap_provider_t *);
static void fasttrap_proc_release(fasttrap_proc_t *);
static int
{
int h = 1;
if (i == 0)
return (0);
#ifdef _LP64
if (i & 0xffffffff00000000ul) {
h += 32; i >>= 32;
}
#endif
if (i & 0xffff0000) {
h += 16; i >>= 16;
}
if (i & 0xff00) {
h += 8; i >>= 8;
}
if (i & 0xf0) {
h += 4; i >>= 4;
}
if (i & 0xc) {
h += 2; i >>= 2;
}
if (i & 0x2) {
h += 1;
}
return (h);
}
static uint_t
fasttrap_hash_str(const char *p)
{
unsigned int g;
while (*p) {
if ((g = (hval & 0xf0000000)) != 0)
hval ^= g >> 24;
hval &= ~g;
}
return (hval);
}
void
{
mutex_enter(&p->p_lock);
mutex_exit(&p->p_lock);
if (t != NULL)
aston(t);
}
/*
* This function ensures that no threads are actively using the memory
* associated with probes that were formerly live.
*/
static void
{
int i;
if (gen < fasttrap_mod_gen)
return;
for (i = 0; i < NCPU; i++) {
}
}
/*
* This is the timeout's callback for cleaning up the providers and their
* probes.
*/
/*ARGSUSED*/
static void
fasttrap_pid_cleanup_cb(void *data)
{
int i, later;
static volatile int in = 0;
in = 1;
while (fasttrap_cleanup_work) {
later = 0;
/*
* Iterate over all the providers trying to remove the marked
* ones. If a provider is marked but not retired, we just
* have to take a crack at removing it -- it's no big deal if
* we can't.
*/
for (i = 0; i < fasttrap_provs.fth_nent; i++) {
if (!fp->ftp_marked) {
continue;
}
/*
* If this provider is referenced either
* because it is a USDT provider or is being
* modified, we can't unregister or even
* condense.
*/
if (fp->ftp_ccount != 0) {
fp->ftp_marked = 0;
continue;
}
fp->ftp_marked = 0;
/*
* If we successfully unregister this
* provider we can remove it from the hash
* chain and free the memory. If our attempt
* to unregister fails and this is a retired
* provider, increment our flag to try again
* pretty soon. If we've consumed more than
* half of our total permitted number of
* probes call dtrace_condense() to try to
* clean out the unenabled probes.
*/
if (dtrace_unregister(provid) != 0) {
(void) dtrace_condense(provid);
} else {
}
}
}
}
ASSERT(fasttrap_timeout != 0);
/*
* If we were unable to remove a retired provider, try again after
* a second. This situation can occur in certain circumstances where
* providers cannot be unregistered even though they have no probes
* enabled because of an execution of dtrace -l or something similar.
* If the timeout has been disabled (set to 1 because we're trying
* to detach), we set fasttrap_cleanup_work to ensure that we'll
* get a chance to do that work if and when the timeout is reenabled
* (if detach fails).
*/
else if (later > 0)
else
fasttrap_timeout = 0;
in = 0;
}
/*
* Activates the asynchronous cleanup mechanism.
*/
static void
fasttrap_pid_cleanup(void)
{
if (fasttrap_timeout == 0)
}
/*
* This is called from cfork() via dtrace_fasttrap_fork(). The child
* process's address space is a (roughly) a copy of the parent process's so
* we have to remove all the instrumentation we had previously enabled in the
* parent.
*/
static void
{
int i;
ASSERT(p->p_dtrace_count > 0);
/*
* This would be simpler and faster if we maintained per-process
* hash tables of enabled tracepoints. It could, however, potentially
* slow down execution of a tracepoint since we'd need to go
* through two levels of indirection. In the future, we should
* consider either maintaining per-process ancillary lists of
* enabled tracepoints or hanging a pointer to a per-process hash
* table of enabled tracepoints off the proc structure.
*/
/*
* We don't have to worry about the child process disappearing
* because we're in fork().
*/
/*
* Iterate over every tracepoint looking for ones that belong to the
* parent process, and remove each from the child process.
*/
for (i = 0; i < fasttrap_tpoints.fth_nent; i++) {
}
}
}
}
/*
* This is called from proc_exit() or from exec_common() if p_dtrace_probes
* is set on the proc structure to indicate that there is a pid provider
* associated with this process.
*/
static void
{
mutex_exit(&p->p_lock);
/*
* We clean up the pid provider for this process here; user-land
* static probes are handled by the meta-provider remove entry point.
* Note that the consumer count is not artificially elevated on the
* pid provider as it is on USDT providers so there's no need to drop
* it here.
*/
mutex_enter(&p->p_lock);
}
/*ARGSUSED*/
static void
{
/*
* There are no "default" pid probes.
*/
}
/*ARGSUSED*/
static void
{
}
static int
{
/*
* Before we make any modifications, make sure we've imposed a barrier
* on the generation in which this probe was last modified.
*/
/*
* If the tracepoint has already been enabled, just add our id to the
* list of interested probes. This may be our second time through
* this path in which case we'll have constructed the tracepoint we'd
* like to install. If we can't find a match, and have an allocated
* tracepoint ready to go, enable that one now.
*
* A tracepoint whose proc is defunct is also considered defunct.
*/
continue;
/*
* Now that we've found a matching tracepoint, it would be
* a decent idea to confirm that the tracepoint is still
* enabled and the trap instruction hasn't been overwritten.
* Since this is a little hairy, we'll punt for now.
*/
/*
* This can't be the first interested probe. We don't have
* to worry about another thread being in the midst of
* deleting this tracepoint (which would be the only valid
* reason for a tracepoint to have no interested probes)
* since we're holding P_PR_LOCK for this process.
*/
} else {
}
}
return (0);
}
/*
* If we have a good tracepoint ready to go, install it now while
* we have the lock held and no one can screw with us.
*/
int rc = 0;
/*
* Activate the tracepoint in the ISA-specific manner.
* If this fails, we need to report the failure, but
* indicate that this tracepoint must still be disabled
* by calling fasttrap_tracepoint_disable().
*/
if (fasttrap_tracepoint_install(p, new_tp) != 0)
/*
* Increment the count of the number of tracepoints active in
* the victim process.
*/
p->p_dtrace_count++;
return (rc);
}
/*
* Initialize the tracepoint that's been preallocated with the probe.
*/
} else {
}
/*
* If the ISA-dependent initialization goes to plan, go back to the
* beginning and try to install this freshly made tracepoint.
*/
goto again;
return (FASTTRAP_ENABLE_FAIL);
}
static void
{
/*
* Find the tracepoint and make sure that our id is one of the
* ones registered with it.
*/
break;
}
/*
* If we somehow lost this tracepoint, we're in a world of hurt.
*/
} else {
}
}
/*
* If there are other registered enablings of this tracepoint, we're
* all done, but if this was the last probe assocated with this
* this tracepoint, we need to remove and free it.
*/
/*
* If the current probe's tracepoint is in use, swap it
* for an unused tracepoint.
*/
} else {
}
}
/*
* Tag the modified probe with the generation in which it was
* changed.
*/
return;
}
/*
* We can't safely remove the tracepoint from the set of active
* tracepoints until we've actually removed the fasttrap instruction
* from the process's text. We can, however, operate on this
* tracepoint secure in the knowledge that no other thread is going to
* be looking at it since we hold P_PR_LOCK on the process if it's
* live or we hold the provider lock on the process if it's dead and
* gone.
*/
/*
* We only need to remove the actual instruction if we're looking
* at an existing process
*/
if (p != NULL) {
/*
* If we fail to restore the instruction we need to kill
* this process since it's in a completely unrecoverable
* state.
*/
if (fasttrap_tracepoint_remove(p, tp) != 0)
/*
* Decrement the count of the number of tracepoints active
* in the victim process.
*/
p->p_dtrace_count--;
}
/*
* Remove the probe from the hash table of active tracepoints.
*/
}
/*
* Tag the modified probe with the generation in which it was changed.
*/
}
typedef int fasttrap_probe_f(struct regs *);
static void
{
/*
* We don't have to play the rw lock game here because we're
* providing something rather than taking something away --
* we can be sure that no threads have tried to follow this
* function pointer yet.
*/
if (*count == 0) {
*fptr = f;
}
(*count)++;
}
static void
{
(*count)--;
if (*count == 0) {
}
}
}
}
/*ARGSUSED*/
static void
{
/*
* Enable the probe that corresponds to statically placed trace
* points which have not explicitly been placed in the process's text
* by the fasttrap provider.
*/
}
/*ARGSUSED*/
static void
{
proc_t *p;
int i, rc;
/*
* Increment the count of enabled probes on this probe's provider;
* the provider can't go away while the probe still exists. We
* must increment this even if we aren't able to properly enable
* this probe.
*/
/*
* Bail out if we can't find the process for this probe or its
* provider is retired (meaning it was valid in a previously exec'ed
* incarnation of this address space). The provider can't go away
* while we're in this code path.
*/
return;
mutex_exit(&p->p_lock);
/*
* We have to enable the trap entry before any user threads have
* the chance to execute the trap instruction we're about to place
* in their process's text.
*/
/*
* Enable all the tracepoints and add this probe's id to each
* tracepoint's list of active probes.
*/
/*
* If enabling the tracepoint failed completely,
* we don't have to disable it; if the failure
* was only partial we must disable it.
*/
if (rc == FASTTRAP_ENABLE_FAIL)
i--;
else
/*
* Back up and pull out all the tracepoints we've
* created so far for this probe.
*/
while (i >= 0) {
fasttrap_tracepoint_disable(p, probe, i);
i--;
}
mutex_enter(&p->p_lock);
sprunlock(p);
/*
* Since we're not actually enabling this probe,
* drop our reference on the trap table entry.
*/
return;
}
}
mutex_enter(&p->p_lock);
sprunlock(p);
}
/*ARGSUSED*/
static void
{
/*
* Disable the probe the corresponds to statically placed trace
* points.
*/
NULL);
}
/*ARGSUSED*/
static void
{
proc_t *p;
int i, whack = 0;
if (!probe->ftp_enabled) {
provider->ftp_rcount--;
return;
}
/*
* We won't be able to acquire a /proc-esque lock on the process
* iff the process is dead and gone. In this case, we rely on the
* provider lock as a point of mutual exclusion to prevent other
* DTrace consumers from disabling this probe.
*/
mutex_exit(&p->p_lock);
}
/*
* Disable all the associated tracepoints.
*/
fasttrap_tracepoint_disable(p, probe, i);
}
provider->ftp_rcount--;
if (p != NULL) {
/*
* Even though we may not be able to remove it entirely, we
* mark this retired provider to get a chance to remove some
* of the associated probes.
*/
mutex_enter(&p->p_lock);
sprunlock(p);
} else {
/*
* If the process is dead, we're just waiting for the
* last probe to be disabled to be able to free it.
*/
}
if (whack)
probe->ftp_enabled = 0;
}
/*ARGSUSED*/
static void
{
char *str;
int i;
return;
}
/*
* We only need to set this member if the argument is remapped.
*/
for (i = 0; i < desc->dtargd_mapping; i++) {
}
return;
for (i = 0; i < desc->dtargd_ndx; i++) {
}
}
/*ARGSUSED*/
static void
{
}
/*ARGSUSED*/
static void
{
int i;
size = sizeof (fasttrap_probe_t) +
sizeof (fasttrap_tracepoint_t));
}
}
static const dtrace_pattr_t fasttrap_attr = {
};
static dtrace_pops_t fasttrap_pops = {
NULL,
NULL,
NULL,
NULL,
NULL,
};
static const dtrace_pattr_t pid_attr = {
};
static dtrace_pops_t pid_pops = {
NULL,
NULL,
NULL,
NULL,
};
static dtrace_pops_t usdt_pops = {
NULL,
NULL,
NULL,
NULL,
};
static fasttrap_proc_t *
{
fprc->ftpc_count++;
return (fprc);
}
}
/*
* Drop the bucket lock so we don't try to perform a sleeping
* allocation under it.
*/
/*
* Take another lap through the list to make sure a proc hasn't
* been created for this pid while we weren't under the bucket lock.
*/
fprc->ftpc_count++;
return (fprc);
}
}
return (new_fprc);
}
static void
{
if (--proc->ftpc_count != 0) {
return;
}
break;
}
/*
* Something strange has happened if we can't find the proc.
*/
}
/*
* Lookup a fasttrap-managed provider based on its name and associated pid.
* If the pattr argument is non-NULL, this function instantiates the provider
* if it doesn't exist otherwise it returns NULL. The provider is returned
* with its lock held.
*/
static fasttrap_provider_t *
const dtrace_pattr_t *pattr)
{
char provname[DTRACE_PROVNAMELEN];
proc_t *p;
/*
* Take a lap through the list and return the match if we find it.
*/
!fp->ftp_retired) {
return (fp);
}
}
/*
* Drop the bucket lock so we don't try to perform a sleeping
* allocation under it.
*/
/*
* Make sure the process exists, isn't a child created as the result
* of a vfork(2), and isn't a zombie (but may be in fork). Record the
* process's uid to pass to dtrace_register().
*/
return (NULL);
}
mutex_enter(&p->p_lock);
mutex_exit(&p->p_lock);
return (NULL);
}
/*
* Increment p_dtrace_probes so that the process knows to inform us
* when it exits or execs. fasttrap_provider_free() decrements this
* when we're done with this provider.
*/
p->p_dtrace_probes++;
mutex_enter(&p->p_crlock);
mutex_exit(&p->p_crlock);
mutex_exit(&p->p_lock);
/*
* Take another lap through the list to make sure a provider hasn't
* been created for this pid while we weren't under the bucket lock.
*/
!fp->ftp_retired) {
return (fp);
}
}
/*
* Fail and return NULL if either the provider name is too long
* or we fail to register this new provider with the DTrace
* framework. Note that this is the only place we ever construct
* the full provider name -- we keep it in pieces in the provider
* structure.
*/
sizeof (provname) ||
&new_fp->ftp_provid) != 0) {
return (NULL);
}
return (new_fp);
}
static void
{
proc_t *p;
/*
* There need to be no consumers using this provider and no
* associated enabled probes.
*/
/*
* Decrement p_dtrace_probes on the process whose provider we're
* freeing. We don't have to worry about clobbering somone else's
* modifications to it because we have locked the bucket that
* corresponds to this process's hash chain in the provider hash
* table. Don't sweat it if we can't find the process.
*/
return;
}
mutex_enter(&p->p_lock);
p->p_dtrace_probes--;
mutex_exit(&p->p_lock);
}
static void
{
!fp->ftp_retired)
break;
}
return;
}
/*
* Mark the provider to be removed in our post-processing step,
* mark it retired, and mark its proc as defunct (though it may
* already be marked defunct by another provider that shares the
* same proc). Marking it indicates that we should try to remove it;
* setting the retired flag indicates that we're done with this
* provider; setting the proc to be defunct indicates that all
* tracepoints associated with the traced process should be ignored.
* We also drop the consumer count here by the amount specified.
*
* We obviously need to take the bucket lock before the provider lock
* to perform the lookup, but we need to drop the provider lock
* before calling into the DTrace framework since we acquire the
* provider lock in callbacks invoked from the DTrace framework. The
* bucket lock therefore protects the integrity of the provider hash
* table.
*/
/*
* We don't have to worry about invalidating the same provider twice
* since fasttrap_provider_lookup() will ignore provider that have
* been marked as retired.
*/
}
static int
{
char *name;
case DTFTP_ENTRY:
name = "entry";
break;
case DTFTP_RETURN:
name = "return";
break;
case DTFTP_OFFSETS:
break;
default:
return (EINVAL);
}
return (ESRCH);
/*
* Increment this reference count to indicate that a consumer is
* actively adding a new probe associated with this provider.
*/
provider->ftp_ccount++;
goto done;
if (fasttrap_total > fasttrap_max) {
goto no_mem;
}
size = sizeof (fasttrap_probe_t) +
for (i = 0; i < pdata->ftps_noffs; i++) {
KM_SLEEP);
}
} else {
for (i = 0; i < pdata->ftps_noffs; i++) {
char name_str[17];
continue;
if (fasttrap_total > fasttrap_max) {
goto no_mem;
}
KM_SLEEP);
}
}
done:
/*
* We know that the provider is still valid since we incremented the
* reference count. If someone tried to free this provider while we
* were using it (e.g. because the process called exec(2) or exit(2)),
* take note of that and try to free it now.
*/
provider->ftp_ccount--;
if (whack)
return (0);
/*
* If we've exhausted the allowable resources, we'll try to remove
* this provider to free some up. This is to cover the case where
* the user has accidentally created many more probes than was
* intended (e.g. pid123:::).
*/
provider->ftp_ccount--;
return (ENOMEM);
}
/*ARGSUSED*/
static void *
{
/*
* A 32-bit unsigned integer (like a pid for example) can be
* expressed in 10 or fewer decimal digits. Make sure that we'll
* have enough space for the provider name.
*/
return (NULL);
}
/*
* Don't let folks spoof the true pid provider.
*/
return (NULL);
}
/*
* The highest stability class that fasttrap supports is ISA; cap
* the stability of the new provider accordingly.
*/
return (NULL);
}
/*
* We elevate the consumer count here to ensure that this provider
* isn't removed until after the meta provider has been told to
* remove it.
*/
provider->ftp_ccount++;
return (provider);
}
/*ARGSUSED*/
static void
{
int i;
return;
}
if (fasttrap_total > fasttrap_max) {
return;
}
size = sizeof (fasttrap_probe_t) +
#ifdef __sparc
#else
#endif
}
/*
* If the arguments are shuffled around we set the argument remapping
* table. Later, when the probe fires, we only remap the arguments
* if the table is non-NULL.
*/
for (i = 0; i < dhpb->dthpb_xargc; i++) {
if (dhpb->dthpb_args[i] != i) {
break;
}
}
/*
* The probe is fully constructed -- register it with DTrace.
*/
}
/*ARGSUSED*/
static void
{
/*
* Clean up the USDT provider. There may be active consumers of the
* provider busy adding probes, no damage will actually befall the
* provider until that count has dropped to zero. This just puts
* the provider on death row.
*/
}
static dtrace_mops_t fasttrap_mops = {
};
/*ARGSUSED*/
static int
{
return (0);
}
/*ARGSUSED*/
static int
{
if (!dtrace_attached())
return (EAGAIN);
if (cmd == FASTTRAPIOC_MAKEPROBE) {
int ret;
char *c;
sizeof (uprobe->ftps_noffs)))
return (EFAULT);
/*
* Probes must have at least one tracepoint.
*/
if (noffs == 0)
return (EINVAL);
size = sizeof (fasttrap_probe_spec_t) +
return (ENOMEM);
return (EFAULT);
}
/*
* Verify that the function and module strings contain no
* funny characters.
*/
if (*c < 0x20 || 0x7f <= *c) {
goto err;
}
}
if (*c < 0x20 || 0x7f <= *c) {
goto err;
}
}
proc_t *p;
/*
* Report an error if the process doesn't exist
* or is actively being birthed.
*/
return (ESRCH);
}
mutex_enter(&p->p_lock);
mutex_exit(&p->p_lock);
return (ret);
}
mutex_exit(&p->p_lock);
}
err:
return (ret);
} else if (cmd == FASTTRAPIOC_GETINSTR) {
int ret;
return (EFAULT);
proc_t *p;
/*
* Report an error if the process doesn't exist
* or is actively being birthed.
*/
return (ESRCH);
}
mutex_enter(&p->p_lock);
VREAD)) != 0) {
mutex_exit(&p->p_lock);
return (ret);
}
mutex_exit(&p->p_lock);
}
break;
}
return (ENOENT);
}
sizeof (instr.ftiq_instr));
return (EFAULT);
return (0);
}
return (EINVAL);
}
static struct cb_ops fasttrap_cb_ops = {
fasttrap_open, /* open */
nodev, /* close */
nulldev, /* strategy */
nulldev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
fasttrap_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
0, /* streamtab */
};
/*ARGSUSED*/
static int
{
int error;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
*result = (void *)fasttrap_devi;
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
static int
{
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Install our hooks into fork(2), exec(2), and exit(2).
*/
"fasttrap-max-probes", FASTTRAP_MAX_DEFAULT);
fasttrap_total = 0;
/*
* Conjure up the tracepoints hashtable...
*/
"fasttrap-hash-size", FASTTRAP_TPOINTS_DEFAULT_SIZE);
else
sizeof (fasttrap_bucket_t), KM_SLEEP);
/*
* ... and the providers hash table...
*/
else
sizeof (fasttrap_bucket_t), KM_SLEEP);
/*
* ... and the procs hash table.
*/
else
sizeof (fasttrap_bucket_t), KM_SLEEP);
return (DDI_SUCCESS);
}
static int
{
int i, fail = 0;
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/*
* Unregister the meta-provider to make sure no new fasttrap-
* managed providers come along while we're trying to close up
* shop. If we fail to detach, we'll need to re-register as a
* meta-provider. We can fail to unregister as a meta-provider
* if providers we manage still exist.
*/
if (fasttrap_meta_id != DTRACE_METAPROVNONE &&
return (DDI_FAILURE);
/*
* Prevent any new timeouts from running by setting fasttrap_timeout
* to a non-zero value, and wait for the current timeout to complete.
*/
if (tmp != 0) {
}
}
/*
* Iterate over all of our providers. If there's still a process
* that corresponds to that pid, fail to detach.
*/
for (i = 0; i < fasttrap_provs.fth_nent; i++) {
/*
* Acquire and release the lock as a simple way of
* waiting for any other consumer to finish with
* this provider. A thread must first acquire the
* bucket lock so there's no chance of another thread
* blocking on the provider's lock.
*/
fail = 1;
} else {
}
}
}
/*
* If we're failing to detach, we need to unblock timeouts
* and start a new timeout if any work has accumulated while
* we've been unsuccessfully trying to detach.
*/
fasttrap_timeout = 0;
if (work)
return (DDI_FAILURE);
}
#ifdef DEBUG
ASSERT(fasttrap_count == 0);
#endif
fasttrap_tpoints.fth_nent = 0;
fasttrap_provs.fth_nent = 0;
fasttrap_procs.fth_nent = 0;
/*
* We know there are no tracepoints in any process anywhere in
* the system so there is no process which has its p_dtrace_count
* greater than zero, therefore we know that no thread can actively
* be executing code in fasttrap_fork(). Similarly for p_dtrace_probes
* and fasttrap_exec() and fasttrap_exit().
*/
return (DDI_SUCCESS);
}
static struct dev_ops fasttrap_ops = {
DEVO_REV, /* devo_rev */
0, /* refcnt */
fasttrap_info, /* get_dev_info */
nulldev, /* identify */
nulldev, /* probe */
fasttrap_attach, /* attach */
fasttrap_detach, /* detach */
nodev, /* reset */
&fasttrap_cb_ops, /* driver operations */
NULL, /* bus operations */
nodev /* dev power */
};
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* module type (this is a pseudo driver) */
"Fasttrap Tracing", /* name of module */
&fasttrap_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
(void *)&modldrv,
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
{
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}