/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#include <sys/fasttrap.h>
#include <sys/fasttrap_impl.h>
#include <sys/fasttrap_isa.h>
#include <sys/dtrace_impl.h>
#include <sys/sysmacros.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.
*
*
* Lock Ordering
* -------------
*
* The lock ordering below -- both internally and with respect to the DTrace
* framework -- is a little tricky and bears some explanation. Each provider
* has a lock (ftp_mtx) that protects its members including reference counts
* for enabled probes (ftp_rcount), consumers actively creating probes
* (ftp_ccount) and USDT consumers (ftp_mcount); all three prevent a provider
* from being freed. A provider is looked up by taking the bucket lock for the
* provider hash table, and is returned with its lock held. The provider lock
* may be taken in functions invoked by the DTrace framework, but may not be
* held while calling functions in the DTrace framework.
*
* To ensure consistency over multiple calls to the DTrace framework, the
* creation lock (ftp_cmtx) should be held. Naturally, the creation lock may
* not be taken when holding the provider lock as that would create a cyclic
* lock ordering. In situations where one would naturally take the provider
* lock and then the creation lock, we instead up a reference count to prevent
* the provider from disappearing, drop the provider lock, and acquire the
* creation lock.
*
* Briefly:
* bucket lock before provider lock
* DTrace before provider lock
* creation lock before DTrace
* never hold the provider lock and creation lock simultaneously
*/
/*
* Generation count on modifications to the global tracepoint lookup table.
*/
/*
* 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.
*/
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
{
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 has consumers actively
* creating probes (ftp_ccount) or is a USDT
* provider (ftp_mcount), we can't unregister
* or even condense.
*/
if (fp->ftp_ccount != 0 ||
fp->ftp_mcount != 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.
*/
(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).
*/
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 (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++) {
/*
* The count of active providers can only be
* decremented (i.e. to zero) during exec,
* exit, and removal of a meta provider so it
* should be impossible to drop the count
* mid-fork.
*/
}
}
}
}
/*
* 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.
*/
mutex_enter(&p->p_lock);
}
/*ARGSUSED*/
static void
{
/*
* There are no "default" pid probes.
*/
}
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 process is defunct is also considered defunct.
*/
/*
* Note that it's safe to access the active count on the
* associated proc structure because we know that at least one
* provider (this one) will still be around throughout this
* operation.
*/
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.
*/
case DTFTP_ENTRY:
case DTFTP_OFFSETS:
case DTFTP_IS_ENABLED:
break;
case DTFTP_RETURN:
case DTFTP_POST_OFFSETS:
break;
default:
ASSERT(0);
}
}
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.
*/
case DTFTP_ENTRY:
case DTFTP_OFFSETS:
case DTFTP_IS_ENABLED:
break;
case DTFTP_RETURN:
case DTFTP_POST_OFFSETS:
break;
default:
ASSERT(0);
}
/*
* 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.
*/
case DTFTP_ENTRY:
case DTFTP_OFFSETS:
case DTFTP_IS_ENABLED:
break;
case DTFTP_RETURN:
case DTFTP_POST_OFFSETS:
break;
default:
ASSERT(0);
}
}
/*
* 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.
*/
/* LINTED - alignment */
} else {
/* LINTED - alignment */
}
}
/*
* 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.
*/
}
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 (fasttrap_pid_count == 0) {
}
}
static void
{
ASSERT(fasttrap_pid_count > 0);
if (fasttrap_pid_count == 0) {
}
}
}
}
/*ARGSUSED*/
static int
{
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.
*/
/*
* If this probe's provider is retired (meaning it was valid in a
* previously exec'ed incarnation of this address space), bail out. The
* provider can't go away while we're in this code path.
*/
return (0);
/*
* If we can't find the process, it may be that we're in the context of
* a fork in which the traced process is being born and we're copying
* USDT probes. Otherwise, the process is gone so bail.
*/
return (0);
if (p == NULL) {
/*
* So it's not that the target process is being born,
* it's that it isn't there at all (and we simply
* happen to be forking). Anyway, we know that the
* target is definitely gone, so bail out.
*/
return (0);
}
/*
* Confirm that curproc is indeed forking the process in which
* we're trying to enable probes.
*/
mutex_enter(&p->p_lock);
sprlock_proc(p);
}
mutex_exit(&p->p_lock);
/*
* We have to enable the trap entry point 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 (0);
}
}
mutex_enter(&p->p_lock);
sprunlock(p);
return (0);
}
/*ARGSUSED*/
static void
{
proc_t *p;
int i, whack = 0;
/*
* 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 (for fully enabled probes).
*/
if (probe->ftp_enabled) {
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)
if (!probe->ftp_enabled)
return;
probe->ftp_enabled = 0;
}
/*ARGSUSED*/
static void
{
char *str;
int i, ndx;
return;
}
for (i = 0; i < ndx; i++) {
}
return;
for (i = 0; i < desc->dtargd_ndx; i++) {
}
}
/*ARGSUSED*/
static void
{
int i;
sizeof (fasttrap_tracepoint_t));
}
}
};
NULL,
NULL,
NULL,
NULL,
};
NULL,
NULL,
NULL,
NULL,
};
static fasttrap_proc_t *
{
fprc->ftpc_rcount++;
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_rcount++;
return (fprc);
}
}
return (new_fprc);
}
static void
{
if (--proc->ftpc_rcount != 0) {
return;
}
/*
* There should definitely be no live providers associated with this
* process at this point.
*/
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)
{
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).
*/
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++;
/*
* Grab the credentials for this process so we have
* something to pass to dtrace_register().
*/
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 associated enabled probes, no consumers
* creating probes, and no meta providers referencing this provider.
*/
/*
* If this provider hasn't been retired, we need to explicitly drop the
* count of active providers on the associated process structure.
*/
if (!provider->ftp_retired) {
}
/*
* 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;
}
return;
}
/*
* Mark the provider to be removed in our post-processing step, mark it
* retired, and drop the active count on its proc. Marking it indicates
* that we should try to remove it; setting the retired flag indicates
* that we're done with this provider; dropping the active the proc
* releases our hold, and when this reaches zero (as it will during
* exit or exec) the proc and associated providers become defunct.
*
* 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
{
}
static int
{
}
static int
{
char *name;
/*
* There needs to be at least one desired trace point.
*/
if (pdata->ftps_noffs == 0)
return (EINVAL);
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. This
* prevents the provider from being deleted -- we'll need to check
* for pending deletions when we drop this reference count.
*/
provider->ftp_ccount++;
/*
* Grab the creation lock to ensure consistency between calls to
* dtrace_probe_lookup() and dtrace_probe_create() in the face of
* other threads creating probes. We must drop the provider lock
* before taking this lock to avoid a three-way deadlock with the
* DTrace framework.
*/
for (i = 0; i < pdata->ftps_noffs; i++) {
continue;
if (fasttrap_total > fasttrap_max) {
goto no_mem;
}
KM_SLEEP);
}
if (fasttrap_total > fasttrap_max) {
goto no_mem;
}
/*
* Make sure all tracepoint program counter values are unique.
* We later assume that each probe has exactly one tracepoint
* for a given pc.
*/
sizeof (uint64_t), fasttrap_uint64_cmp);
continue;
goto no_mem;
}
for (i = 0; i < pdata->ftps_noffs; i++) {
KM_SLEEP);
}
}
/*
* We know that the provider is still valid since we incremented the
* creation reference count. If someone tried to clean up 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 clean it up 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);
}
/*
* Up the meta provider count so this provider isn't removed until
* the meta provider has been told to remove it.
*/
provider->ftp_mcount++;
return (provider);
}
/*
* We know a few things about our context here: we know that the probe being
* created doesn't already exist (DTrace won't load DOF at the same address
* twice, even if explicitly told to do so) and we know that we are
* single-threaded with respect to the meta provider machinery. Knowing that
* this is a new probe and that there is no way for us to race with another
* operation on this provider allows us an important optimization: we need not
* lookup a probe before adding it. Saving this lookup is important because
* this code is in the fork path for processes with USDT probes, and lookups
* here are potentially very expensive because of long hash conflicts on
* module, function and name (DTrace doesn't hash on provider name).
*/
/*ARGSUSED*/
static void
{
int i, j;
/*
* Since the meta provider count is non-zero we don't have to worry
* about this provider disappearing.
*/
/*
* The offsets must be unique.
*/
return;
}
return;
}
if (fasttrap_total > fasttrap_max) {
return;
}
/*
* First create a tracepoint for each actual point of interest.
*/
for (i = 0; i < dhpb->dthpb_noffs; i++) {
#ifdef __sparc
#else
#endif
}
/*
* Then create a tracepoint for each is-enabled point.
*/
for (j = 0; i < ntps; i++, j++) {
}
/*
* 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.
*/
}
};
/*ARGSUSED*/
static int
{
return (0);
}
/*ARGSUSED*/
static int
{
if (!dtrace_attached())
return (EAGAIN);
if (cmd == FASTTRAPIOC_MAKEPROBE) {
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.
*/
goto err;
}
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);
}
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 (fail) {
/*
* 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_pid_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);
}
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 */
ddi_quiesce_not_needed, /* quiesce */
};
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* module type (this is a pseudo driver) */
"Fasttrap Tracing", /* name of module */
&fasttrap_ops, /* driver ops */
};
(void *)&modldrv,
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
{
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}