Path: blob/main/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c
96395 views
/*1* CDDL HEADER START2*3* The contents of this file are subject to the terms of the4* Common Development and Distribution License (the "License").5* You may not use this file except in compliance with the License.6*7* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE8* or http://www.opensolaris.org/os/licensing.9* See the License for the specific language governing permissions10* and limitations under the License.11*12* When distributing Covered Code, include this CDDL HEADER in each13* file and include the License file at usr/src/OPENSOLARIS.LICENSE.14* If applicable, add the following below this CDDL HEADER, with the15* fields enclosed by brackets "[]" replaced with your own identifying16* information: Portions Copyright [yyyy] [name of copyright owner]17*18* CDDL HEADER END19*20* Portions Copyright 2010 The FreeBSD Foundation21*/2223/*24* Copyright 2008 Sun Microsystems, Inc. All rights reserved.25* Use is subject to license terms.26*/2728/*29* Copyright (c) 2015, Joyent, Inc. All rights reserved.30*/3132#include <sys/atomic.h>33#include <sys/errno.h>34#include <sys/stat.h>35#include <sys/endian.h>36#include <sys/modctl.h>37#include <sys/conf.h>38#include <sys/systm.h>39#ifdef illumos40#include <sys/ddi.h>41#endif42#include <sys/sunddi.h>43#include <sys/cpuvar.h>44#include <sys/kmem.h>45#ifdef illumos46#include <sys/strsubr.h>47#endif48#include <sys/fasttrap.h>49#include <sys/fasttrap_impl.h>50#include <sys/fasttrap_isa.h>51#include <sys/dtrace.h>52#include <sys/dtrace_impl.h>53#include <sys/sysmacros.h>54#include <sys/proc.h>55#undef AT_UID56#undef AT_GID57#include <sys/policy.h>58#ifdef illumos59#include <util/qsort.h>60#endif61#include <sys/mutex.h>62#include <sys/kernel.h>63#ifndef illumos64#include <sys/dtrace_bsd.h>65#include <sys/eventhandler.h>66#include <sys/rmlock.h>67#include <sys/sysent.h>68#include <sys/sysctl.h>69#include <sys/u8_textprep.h>70#include <sys/user.h>7172#include <vm/vm.h>73#include <vm/pmap.h>74#include <vm/vm_map.h>75#include <vm/vm_param.h>7677#include <cddl/dev/dtrace/dtrace_cddl.h>78#endif7980/*81* User-Land Trap-Based Tracing82* ----------------------------83*84* The fasttrap provider allows DTrace consumers to instrument any user-level85* instruction to gather data; this includes probes with semantic86* signifigance like entry and return as well as simple offsets into the87* function. While the specific techniques used are very ISA specific, the88* methodology is generalizable to any architecture.89*90*91* The General Methodology92* -----------------------93*94* With the primary goal of tracing every user-land instruction and the95* limitation that we can't trust user space so don't want to rely on much96* information there, we begin by replacing the instructions we want to trace97* with trap instructions. Each instruction we overwrite is saved into a hash98* table keyed by process ID and pc address. When we enter the kernel due to99* this trap instruction, we need the effects of the replaced instruction to100* appear to have occurred before we proceed with the user thread's101* execution.102*103* Each user level thread is represented by a ulwp_t structure which is104* always easily accessible through a register. The most basic way to produce105* the effects of the instruction we replaced is to copy that instruction out106* to a bit of scratch space reserved in the user thread's ulwp_t structure107* (a sort of kernel-private thread local storage), set the PC to that108* scratch space and single step. When we reenter the kernel after single109* stepping the instruction we must then adjust the PC to point to what would110* normally be the next instruction. Of course, special care must be taken111* for branches and jumps, but these represent such a small fraction of any112* instruction set that writing the code to emulate these in the kernel is113* not too difficult.114*115* Return probes may require several tracepoints to trace every return site,116* and, conversely, each tracepoint may activate several probes (the entry117* and offset 0 probes, for example). To solve this muliplexing problem,118* tracepoints contain lists of probes to activate and probes contain lists119* of tracepoints to enable. If a probe is activated, it adds its ID to120* existing tracepoints or creates new ones as necessary.121*122* Most probes are activated _before_ the instruction is executed, but return123* probes are activated _after_ the effects of the last instruction of the124* function are visible. Return probes must be fired _after_ we have125* single-stepped the instruction whereas all other probes are fired126* beforehand.127*128*129* Lock Ordering130* -------------131*132* The lock ordering below -- both internally and with respect to the DTrace133* framework -- is a little tricky and bears some explanation. Each provider134* has a lock (ftp_mtx) that protects its members including reference counts135* for enabled probes (ftp_rcount), consumers actively creating probes136* (ftp_ccount) and USDT consumers (ftp_mcount); all three prevent a provider137* from being freed. A provider is looked up by taking the bucket lock for the138* provider hash table, and is returned with its lock held. The provider lock139* may be taken in functions invoked by the DTrace framework, but may not be140* held while calling functions in the DTrace framework.141*142* To ensure consistency over multiple calls to the DTrace framework, the143* creation lock (ftp_cmtx) should be held. Naturally, the creation lock may144* not be taken when holding the provider lock as that would create a cyclic145* lock ordering. In situations where one would naturally take the provider146* lock and then the creation lock, we instead up a reference count to prevent147* the provider from disappearing, drop the provider lock, and acquire the148* creation lock.149*150* Briefly:151* bucket lock before provider lock152* DTrace before provider lock153* creation lock before DTrace154* never hold the provider lock and creation lock simultaneously155*/156157static d_open_t fasttrap_open;158static d_ioctl_t fasttrap_ioctl;159160static struct cdevsw fasttrap_cdevsw = {161.d_version = D_VERSION,162.d_open = fasttrap_open,163.d_ioctl = fasttrap_ioctl,164.d_name = "fasttrap",165};166static struct cdev *fasttrap_cdev;167static dtrace_meta_provider_id_t fasttrap_meta_id;168169static struct proc *fasttrap_cleanup_proc;170static struct mtx fasttrap_cleanup_mtx;171static uint_t fasttrap_cleanup_work, fasttrap_cleanup_drain, fasttrap_cleanup_cv;172173/*174* Generation count on modifications to the global tracepoint lookup table.175*/176static volatile uint64_t fasttrap_mod_gen;177178/*179* When the fasttrap provider is loaded, fasttrap_max is set to either180* FASTTRAP_MAX_DEFAULT, or the value for fasttrap-max-probes in the181* fasttrap.conf file (Illumos), or the value provied in the loader.conf (FreeBSD).182* Each time a probe is created, fasttrap_total is incremented by the number183* of tracepoints that may be associated with that probe; fasttrap_total is capped184* at fasttrap_max.185*/186#define FASTTRAP_MAX_DEFAULT 250000187static uint32_t fasttrap_max = FASTTRAP_MAX_DEFAULT;188static uint32_t fasttrap_total;189190/*191* Copyright (c) 2011, Joyent, Inc. All rights reserved.192*/193194#define FASTTRAP_TPOINTS_DEFAULT_SIZE 0x4000195#define FASTTRAP_PROVIDERS_DEFAULT_SIZE 0x100196#define FASTTRAP_PROCS_DEFAULT_SIZE 0x100197198#define FASTTRAP_PID_NAME "pid"199200fasttrap_hash_t fasttrap_tpoints;201static fasttrap_hash_t fasttrap_provs;202static fasttrap_hash_t fasttrap_procs;203204static uint64_t fasttrap_pid_count; /* pid ref count */205static kmutex_t fasttrap_count_mtx; /* lock on ref count */206207#define FASTTRAP_ENABLE_FAIL 1208#define FASTTRAP_ENABLE_PARTIAL 2209210static int fasttrap_tracepoint_enable(proc_t *, fasttrap_probe_t *, uint_t);211static void fasttrap_tracepoint_disable(proc_t *, fasttrap_probe_t *, uint_t);212213static fasttrap_provider_t *fasttrap_provider_lookup(pid_t, const char *,214const dtrace_pattr_t *);215static void fasttrap_provider_retire(pid_t, const char *, int);216static void fasttrap_provider_free(fasttrap_provider_t *);217218static fasttrap_proc_t *fasttrap_proc_lookup(pid_t);219static void fasttrap_proc_release(fasttrap_proc_t *);220221#ifndef illumos222static void fasttrap_thread_dtor(void *, struct thread *);223#endif224225#define FASTTRAP_PROVS_INDEX(pid, name) \226((fasttrap_hash_str(name) + (pid)) & fasttrap_provs.fth_mask)227228#define FASTTRAP_PROCS_INDEX(pid) ((pid) & fasttrap_procs.fth_mask)229230#ifndef illumos231struct rmlock fasttrap_tp_lock;232static eventhandler_tag fasttrap_thread_dtor_tag;233#endif234235static unsigned long tpoints_hash_size = FASTTRAP_TPOINTS_DEFAULT_SIZE;236237#ifdef __FreeBSD__238SYSCTL_DECL(_kern_dtrace);239SYSCTL_NODE(_kern_dtrace, OID_AUTO, fasttrap, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,240"DTrace fasttrap parameters");241SYSCTL_UINT(_kern_dtrace_fasttrap, OID_AUTO, max_probes, CTLFLAG_RWTUN, &fasttrap_max,242FASTTRAP_MAX_DEFAULT, "Maximum number of fasttrap probes");243SYSCTL_ULONG(_kern_dtrace_fasttrap, OID_AUTO, tpoints_hash_size, CTLFLAG_RDTUN, &tpoints_hash_size,244FASTTRAP_TPOINTS_DEFAULT_SIZE, "Size of the tracepoint hash table");245#endif246247static int248fasttrap_highbit(ulong_t i)249{250int h = 1;251252if (i == 0)253return (0);254#ifdef _LP64255if (i & 0xffffffff00000000ul) {256h += 32; i >>= 32;257}258#endif259if (i & 0xffff0000) {260h += 16; i >>= 16;261}262if (i & 0xff00) {263h += 8; i >>= 8;264}265if (i & 0xf0) {266h += 4; i >>= 4;267}268if (i & 0xc) {269h += 2; i >>= 2;270}271if (i & 0x2) {272h += 1;273}274return (h);275}276277static uint_t278fasttrap_hash_str(const char *p)279{280unsigned int g;281uint_t hval = 0;282283while (*p) {284hval = (hval << 4) + *p++;285if ((g = (hval & 0xf0000000)) != 0)286hval ^= g >> 24;287hval &= ~g;288}289return (hval);290}291292void293fasttrap_sigtrap(proc_t *p, kthread_t *t, uintptr_t pc)294{295ksiginfo_t ksi;296297ksiginfo_init(&ksi);298ksi.ksi_signo = SIGTRAP;299ksi.ksi_code = TRAP_DTRACE;300ksi.ksi_addr = (caddr_t)pc;301PROC_LOCK(p);302(void)tdsendsignal(p, t, SIGTRAP, &ksi);303PROC_UNLOCK(p);304}305306#ifndef illumos307/*308* Obtain a chunk of scratch space in the address space of the target process.309*/310fasttrap_scrspace_t *311fasttrap_scraddr(struct thread *td, fasttrap_proc_t *fprc)312{313fasttrap_scrblock_t *scrblk;314fasttrap_scrspace_t *scrspc;315struct proc *p;316vm_offset_t addr;317int error, i;318319scrspc = NULL;320if (td->t_dtrace_sscr != NULL) {321/* If the thread already has scratch space, we're done. */322scrspc = (fasttrap_scrspace_t *)td->t_dtrace_sscr;323return (scrspc);324}325326p = td->td_proc;327328mutex_enter(&fprc->ftpc_mtx);329if (LIST_EMPTY(&fprc->ftpc_fscr)) {330/*331* No scratch space is available, so we'll map a new scratch332* space block into the traced process' address space.333*/334addr = 0;335error = vm_map_find(&p->p_vmspace->vm_map, NULL, 0, &addr,336FASTTRAP_SCRBLOCK_SIZE, 0, VMFS_ANY_SPACE,337VM_PROT_READ | VM_PROT_EXECUTE,338VM_PROT_READ | VM_PROT_EXECUTE, MAP_COPY_ON_WRITE);339if (error != KERN_SUCCESS)340goto done;341342scrblk = malloc(sizeof(*scrblk), M_SOLARIS, M_WAITOK);343scrblk->ftsb_addr = addr;344LIST_INSERT_HEAD(&fprc->ftpc_scrblks, scrblk, ftsb_next);345346/*347* Carve the block up into chunks and put them on the free list.348*/349for (i = 0;350i < FASTTRAP_SCRBLOCK_SIZE / FASTTRAP_SCRSPACE_SIZE; i++) {351scrspc = malloc(sizeof(*scrspc), M_SOLARIS, M_WAITOK);352scrspc->ftss_addr = addr +353i * FASTTRAP_SCRSPACE_SIZE;354LIST_INSERT_HEAD(&fprc->ftpc_fscr, scrspc,355ftss_next);356}357}358359/*360* Take the first scratch chunk off the free list, put it on the361* allocated list, and return its address.362*/363scrspc = LIST_FIRST(&fprc->ftpc_fscr);364LIST_REMOVE(scrspc, ftss_next);365LIST_INSERT_HEAD(&fprc->ftpc_ascr, scrspc, ftss_next);366367/*368* This scratch space is reserved for use by td until the thread exits.369*/370td->t_dtrace_sscr = scrspc;371372done:373mutex_exit(&fprc->ftpc_mtx);374375return (scrspc);376}377378/*379* Return any allocated per-thread scratch space chunks back to the process'380* free list.381*/382static void383fasttrap_thread_dtor(void *arg __unused, struct thread *td)384{385fasttrap_bucket_t *bucket;386fasttrap_proc_t *fprc;387fasttrap_scrspace_t *scrspc;388pid_t pid;389390if (td->t_dtrace_sscr == NULL)391return;392393pid = td->td_proc->p_pid;394bucket = &fasttrap_procs.fth_table[FASTTRAP_PROCS_INDEX(pid)];395fprc = NULL;396397/* Look up the fasttrap process handle for this process. */398mutex_enter(&bucket->ftb_mtx);399for (fprc = bucket->ftb_data; fprc != NULL; fprc = fprc->ftpc_next) {400if (fprc->ftpc_pid == pid) {401mutex_enter(&fprc->ftpc_mtx);402mutex_exit(&bucket->ftb_mtx);403break;404}405}406if (fprc == NULL) {407mutex_exit(&bucket->ftb_mtx);408return;409}410411scrspc = (fasttrap_scrspace_t *)td->t_dtrace_sscr;412LIST_REMOVE(scrspc, ftss_next);413LIST_INSERT_HEAD(&fprc->ftpc_fscr, scrspc, ftss_next);414415mutex_exit(&fprc->ftpc_mtx);416}417#endif418419/*420* This function ensures that no threads are actively using the memory421* associated with probes that were formerly live.422*/423static void424fasttrap_mod_barrier(uint64_t gen)425{426int i;427428if (gen < fasttrap_mod_gen)429return;430431fasttrap_mod_gen++;432433#ifdef illumos434CPU_FOREACH(i) {435mutex_enter(&fasttrap_cpuc_pid_lock[i]);436mutex_exit(&fasttrap_cpuc_pid_lock[i]);437}438#else439rm_wlock(&fasttrap_tp_lock);440rm_wunlock(&fasttrap_tp_lock);441#endif442}443444/*445* This function performs asynchronous cleanup of fasttrap providers. The446* Solaris implementation of this mechanism use a timeout that's activated in447* fasttrap_pid_cleanup(), but this doesn't work in FreeBSD: one may sleep while448* holding the DTrace mutexes, but it is unsafe to sleep in a callout handler.449* Thus we use a dedicated process to perform the cleanup when requested.450*/451/*ARGSUSED*/452static void453fasttrap_pid_cleanup_cb(void *data)454{455fasttrap_provider_t **fpp, *fp;456fasttrap_bucket_t *bucket;457dtrace_provider_id_t provid;458int i, later = 0, rval;459460mtx_lock(&fasttrap_cleanup_mtx);461while (!fasttrap_cleanup_drain || later > 0) {462fasttrap_cleanup_work = 0;463mtx_unlock(&fasttrap_cleanup_mtx);464465later = 0;466467/*468* Iterate over all the providers trying to remove the marked469* ones. If a provider is marked but not retired, we just470* have to take a crack at removing it -- it's no big deal if471* we can't.472*/473for (i = 0; i < fasttrap_provs.fth_nent; i++) {474bucket = &fasttrap_provs.fth_table[i];475mutex_enter(&bucket->ftb_mtx);476fpp = (fasttrap_provider_t **)&bucket->ftb_data;477478while ((fp = *fpp) != NULL) {479if (!fp->ftp_marked) {480fpp = &fp->ftp_next;481continue;482}483484mutex_enter(&fp->ftp_mtx);485486/*487* If this provider has consumers actively488* creating probes (ftp_ccount) or is a USDT489* provider (ftp_mcount), we can't unregister490* or even condense.491*/492if (fp->ftp_ccount != 0 ||493fp->ftp_mcount != 0) {494mutex_exit(&fp->ftp_mtx);495fp->ftp_marked = 0;496continue;497}498499if (!fp->ftp_retired || fp->ftp_rcount != 0)500fp->ftp_marked = 0;501502mutex_exit(&fp->ftp_mtx);503504/*505* If we successfully unregister this506* provider we can remove it from the hash507* chain and free the memory. If our attempt508* to unregister fails and this is a retired509* provider, increment our flag to try again510* pretty soon. If we've consumed more than511* half of our total permitted number of512* probes call dtrace_condense() to try to513* clean out the unenabled probes.514*/515provid = fp->ftp_provid;516if ((rval = dtrace_unregister(provid)) != 0) {517if (fasttrap_total > fasttrap_max / 2)518(void) dtrace_condense(provid);519520if (rval == EAGAIN)521fp->ftp_marked = 1;522523later += fp->ftp_marked;524fpp = &fp->ftp_next;525} else {526*fpp = fp->ftp_next;527fasttrap_provider_free(fp);528}529}530mutex_exit(&bucket->ftb_mtx);531}532mtx_lock(&fasttrap_cleanup_mtx);533534/*535* If we were unable to retire a provider, try again after a536* second. This situation can occur in certain circumstances537* where providers cannot be unregistered even though they have538* no probes enabled because of an execution of dtrace -l or539* something similar.540*/541if (later > 0 || fasttrap_cleanup_work ||542fasttrap_cleanup_drain) {543mtx_unlock(&fasttrap_cleanup_mtx);544pause("ftclean", hz);545mtx_lock(&fasttrap_cleanup_mtx);546} else547mtx_sleep(&fasttrap_cleanup_cv, &fasttrap_cleanup_mtx,5480, "ftcl", 0);549}550551/*552* Wake up the thread in fasttrap_unload() now that we're done.553*/554wakeup(&fasttrap_cleanup_drain);555mtx_unlock(&fasttrap_cleanup_mtx);556557kthread_exit();558}559560/*561* Activates the asynchronous cleanup mechanism.562*/563static void564fasttrap_pid_cleanup(void)565{566567mtx_lock(&fasttrap_cleanup_mtx);568if (!fasttrap_cleanup_work) {569fasttrap_cleanup_work = 1;570wakeup(&fasttrap_cleanup_cv);571}572mtx_unlock(&fasttrap_cleanup_mtx);573}574575/*576* This is called from cfork() via dtrace_fasttrap_fork(). The child577* process's address space is (roughly) a copy of the parent process's so578* we have to remove all the instrumentation we had previously enabled in the579* parent.580*/581static void582fasttrap_fork(proc_t *p, proc_t *cp)583{584#ifndef illumos585fasttrap_scrblock_t *scrblk;586fasttrap_proc_t *fprc = NULL;587#endif588pid_t ppid = p->p_pid;589int error, i;590591ASSERT(curproc == p);592#ifdef illumos593ASSERT(p->p_proc_flag & P_PR_LOCK);594#else595PROC_LOCK_ASSERT(p, MA_OWNED);596#endif597#ifdef illumos598ASSERT(p->p_dtrace_count > 0);599#else600/*601* This check is purposely here instead of in kern_fork.c because,602* for legal resons, we cannot include the dtrace_cddl.h header603* inside kern_fork.c and insert if-clause there.604*/605if (p->p_dtrace_count == 0 && p->p_dtrace_helpers == NULL)606return;607#endif608609ASSERT(cp->p_dtrace_count == 0);610611/*612* This would be simpler and faster if we maintained per-process613* hash tables of enabled tracepoints. It could, however, potentially614* slow down execution of a tracepoint since we'd need to go615* through two levels of indirection. In the future, we should616* consider either maintaining per-process ancillary lists of617* enabled tracepoints or hanging a pointer to a per-process hash618* table of enabled tracepoints off the proc structure.619*/620621/*622* We don't have to worry about the child process disappearing623* because we're in fork().624*/625#ifdef illumos626mtx_lock_spin(&cp->p_slock);627sprlock_proc(cp);628mtx_unlock_spin(&cp->p_slock);629#else630/*631* fasttrap_tracepoint_remove() expects the child process to be632* unlocked and the VM then expects curproc to be unlocked.633*/634_PHOLD(cp);635PROC_UNLOCK(cp);636PROC_UNLOCK(p);637if (p->p_dtrace_count == 0)638goto dup_helpers;639#endif640641/*642* Iterate over every tracepoint looking for ones that belong to the643* parent process, and remove each from the child process.644*/645for (i = 0; i < fasttrap_tpoints.fth_nent; i++) {646fasttrap_tracepoint_t *tp;647fasttrap_bucket_t *bucket = &fasttrap_tpoints.fth_table[i];648649mutex_enter(&bucket->ftb_mtx);650for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {651if (tp->ftt_pid == ppid &&652tp->ftt_proc->ftpc_acount != 0) {653int ret = fasttrap_tracepoint_remove(cp, tp);654ASSERT(ret == 0);655656/*657* The count of active providers can only be658* decremented (i.e. to zero) during exec,659* exit, and removal of a meta provider so it660* should be impossible to drop the count661* mid-fork.662*/663ASSERT(tp->ftt_proc->ftpc_acount != 0);664#ifndef illumos665fprc = tp->ftt_proc;666#endif667}668}669mutex_exit(&bucket->ftb_mtx);670671#ifndef illumos672/*673* Unmap any scratch space inherited from the parent's address674* space.675*/676if (fprc != NULL) {677mutex_enter(&fprc->ftpc_mtx);678LIST_FOREACH(scrblk, &fprc->ftpc_scrblks, ftsb_next) {679error = vm_map_remove(&cp->p_vmspace->vm_map,680scrblk->ftsb_addr,681scrblk->ftsb_addr + FASTTRAP_SCRBLOCK_SIZE);682ASSERT(error == KERN_SUCCESS);683}684mutex_exit(&fprc->ftpc_mtx);685}686#endif687}688689#ifdef illumos690mutex_enter(&cp->p_lock);691sprunlock(cp);692#else693dup_helpers:694if (p->p_dtrace_helpers != NULL)695dtrace_helpers_duplicate(p, cp);696PROC_LOCK(p);697PROC_LOCK(cp);698_PRELE(cp);699#endif700}701702/*703* This is called from proc_exit() or from exec_common() if p_dtrace_probes704* is set on the proc structure to indicate that there is a pid provider705* associated with this process.706*/707static void708fasttrap_exec_exit(proc_t *p)709{710#ifndef illumos711struct thread *td;712#endif713714#ifdef illumos715ASSERT(p == curproc);716#else717PROC_LOCK_ASSERT(p, MA_OWNED);718_PHOLD(p);719/*720* Since struct threads may be recycled, we cannot rely on t_dtrace_sscr721* fields to be zeroed by kdtrace_thread_ctor. Thus we must zero it722* ourselves when a process exits.723*/724FOREACH_THREAD_IN_PROC(p, td)725td->t_dtrace_sscr = NULL;726PROC_UNLOCK(p);727#endif728729/*730* We clean up the pid provider for this process here; user-land731* static probes are handled by the meta-provider remove entry point.732*/733fasttrap_provider_retire(p->p_pid, FASTTRAP_PID_NAME, 0);734#ifndef illumos735if (p->p_dtrace_helpers)736dtrace_helpers_destroy(p);737PROC_LOCK(p);738_PRELE(p);739#endif740}741742743/*ARGSUSED*/744static void745fasttrap_pid_provide(void *arg, dtrace_probedesc_t *desc)746{747/*748* There are no "default" pid probes.749*/750}751752static int753fasttrap_tracepoint_enable(proc_t *p, fasttrap_probe_t *probe, uint_t index)754{755fasttrap_tracepoint_t *tp, *new_tp = NULL;756fasttrap_bucket_t *bucket;757fasttrap_id_t *id;758pid_t pid;759uintptr_t pc;760761ASSERT(index < probe->ftp_ntps);762763pid = probe->ftp_pid;764pc = probe->ftp_tps[index].fit_tp->ftt_pc;765id = &probe->ftp_tps[index].fit_id;766767ASSERT(probe->ftp_tps[index].fit_tp->ftt_pid == pid);768769#ifdef illumos770ASSERT(!(p->p_flag & SVFORK));771#endif772773/*774* Before we make any modifications, make sure we've imposed a barrier775* on the generation in which this probe was last modified.776*/777fasttrap_mod_barrier(probe->ftp_gen);778779bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)];780781/*782* If the tracepoint has already been enabled, just add our id to the783* list of interested probes. This may be our second time through784* this path in which case we'll have constructed the tracepoint we'd785* like to install. If we can't find a match, and have an allocated786* tracepoint ready to go, enable that one now.787*788* A tracepoint whose process is defunct is also considered defunct.789*/790again:791mutex_enter(&bucket->ftb_mtx);792for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {793/*794* Note that it's safe to access the active count on the795* associated proc structure because we know that at least one796* provider (this one) will still be around throughout this797* operation.798*/799if (tp->ftt_pid != pid || tp->ftt_pc != pc ||800tp->ftt_proc->ftpc_acount == 0)801continue;802803/*804* Now that we've found a matching tracepoint, it would be805* a decent idea to confirm that the tracepoint is still806* enabled and the trap instruction hasn't been overwritten.807* Since this is a little hairy, we'll punt for now.808*/809810/*811* This can't be the first interested probe. We don't have812* to worry about another thread being in the midst of813* deleting this tracepoint (which would be the only valid814* reason for a tracepoint to have no interested probes)815* since we're holding P_PR_LOCK for this process.816*/817ASSERT(tp->ftt_ids != NULL || tp->ftt_retids != NULL);818819switch (id->fti_ptype) {820case DTFTP_ENTRY:821case DTFTP_OFFSETS:822case DTFTP_IS_ENABLED:823id->fti_next = tp->ftt_ids;824membar_producer();825tp->ftt_ids = id;826membar_producer();827break;828829case DTFTP_RETURN:830case DTFTP_POST_OFFSETS:831id->fti_next = tp->ftt_retids;832membar_producer();833tp->ftt_retids = id;834membar_producer();835break;836837default:838ASSERT(0);839}840841mutex_exit(&bucket->ftb_mtx);842843if (new_tp != NULL) {844new_tp->ftt_ids = NULL;845new_tp->ftt_retids = NULL;846}847848return (0);849}850851/*852* If we have a good tracepoint ready to go, install it now while853* we have the lock held and no one can screw with us.854*/855if (new_tp != NULL) {856int rc = 0;857858new_tp->ftt_next = bucket->ftb_data;859membar_producer();860bucket->ftb_data = new_tp;861membar_producer();862mutex_exit(&bucket->ftb_mtx);863864/*865* Activate the tracepoint in the ISA-specific manner.866* If this fails, we need to report the failure, but867* indicate that this tracepoint must still be disabled868* by calling fasttrap_tracepoint_disable().869*/870if (fasttrap_tracepoint_install(p, new_tp) != 0)871rc = FASTTRAP_ENABLE_PARTIAL;872873/*874* Increment the count of the number of tracepoints active in875* the victim process.876*/877#ifdef illumos878ASSERT(p->p_proc_flag & P_PR_LOCK);879#endif880p->p_dtrace_count++;881882return (rc);883}884885mutex_exit(&bucket->ftb_mtx);886887/*888* Initialize the tracepoint that's been preallocated with the probe.889*/890new_tp = probe->ftp_tps[index].fit_tp;891892ASSERT(new_tp->ftt_pid == pid);893ASSERT(new_tp->ftt_pc == pc);894ASSERT(new_tp->ftt_proc == probe->ftp_prov->ftp_proc);895ASSERT(new_tp->ftt_ids == NULL);896ASSERT(new_tp->ftt_retids == NULL);897898switch (id->fti_ptype) {899case DTFTP_ENTRY:900case DTFTP_OFFSETS:901case DTFTP_IS_ENABLED:902id->fti_next = NULL;903new_tp->ftt_ids = id;904break;905906case DTFTP_RETURN:907case DTFTP_POST_OFFSETS:908id->fti_next = NULL;909new_tp->ftt_retids = id;910break;911912default:913ASSERT(0);914}915916#ifdef __FreeBSD__917if (SV_PROC_FLAG(p, SV_LP64))918p->p_model = DATAMODEL_LP64;919else920p->p_model = DATAMODEL_ILP32;921#endif922923/*924* If the ISA-dependent initialization goes to plan, go back to the925* beginning and try to install this freshly made tracepoint.926*/927if (fasttrap_tracepoint_init(p, new_tp, pc, id->fti_ptype) == 0)928goto again;929930new_tp->ftt_ids = NULL;931new_tp->ftt_retids = NULL;932933return (FASTTRAP_ENABLE_FAIL);934}935936static void937fasttrap_tracepoint_disable(proc_t *p, fasttrap_probe_t *probe, uint_t index)938{939fasttrap_bucket_t *bucket;940fasttrap_provider_t *provider = probe->ftp_prov;941fasttrap_tracepoint_t **pp, *tp;942fasttrap_id_t *id, **idp = NULL;943pid_t pid;944uintptr_t pc;945946ASSERT(index < probe->ftp_ntps);947948pid = probe->ftp_pid;949pc = probe->ftp_tps[index].fit_tp->ftt_pc;950id = &probe->ftp_tps[index].fit_id;951952ASSERT(probe->ftp_tps[index].fit_tp->ftt_pid == pid);953954/*955* Find the tracepoint and make sure that our id is one of the956* ones registered with it.957*/958bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)];959mutex_enter(&bucket->ftb_mtx);960for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {961if (tp->ftt_pid == pid && tp->ftt_pc == pc &&962tp->ftt_proc == provider->ftp_proc)963break;964}965966/*967* If we somehow lost this tracepoint, we're in a world of hurt.968*/969ASSERT(tp != NULL);970971switch (id->fti_ptype) {972case DTFTP_ENTRY:973case DTFTP_OFFSETS:974case DTFTP_IS_ENABLED:975ASSERT(tp->ftt_ids != NULL);976idp = &tp->ftt_ids;977break;978979case DTFTP_RETURN:980case DTFTP_POST_OFFSETS:981ASSERT(tp->ftt_retids != NULL);982idp = &tp->ftt_retids;983break;984985default:986ASSERT(0);987}988989while ((*idp)->fti_probe != probe) {990idp = &(*idp)->fti_next;991ASSERT(*idp != NULL);992}993994id = *idp;995*idp = id->fti_next;996membar_producer();997998ASSERT(id->fti_probe == probe);9991000/*1001* If there are other registered enablings of this tracepoint, we're1002* all done, but if this was the last probe assocated with this1003* this tracepoint, we need to remove and free it.1004*/1005if (tp->ftt_ids != NULL || tp->ftt_retids != NULL) {10061007/*1008* If the current probe's tracepoint is in use, swap it1009* for an unused tracepoint.1010*/1011if (tp == probe->ftp_tps[index].fit_tp) {1012fasttrap_probe_t *tmp_probe;1013fasttrap_tracepoint_t **tmp_tp;1014uint_t tmp_index;10151016if (tp->ftt_ids != NULL) {1017tmp_probe = tp->ftt_ids->fti_probe;1018/* LINTED - alignment */1019tmp_index = FASTTRAP_ID_INDEX(tp->ftt_ids);1020tmp_tp = &tmp_probe->ftp_tps[tmp_index].fit_tp;1021} else {1022tmp_probe = tp->ftt_retids->fti_probe;1023/* LINTED - alignment */1024tmp_index = FASTTRAP_ID_INDEX(tp->ftt_retids);1025tmp_tp = &tmp_probe->ftp_tps[tmp_index].fit_tp;1026}10271028ASSERT(*tmp_tp != NULL);1029ASSERT(*tmp_tp != probe->ftp_tps[index].fit_tp);1030ASSERT((*tmp_tp)->ftt_ids == NULL);1031ASSERT((*tmp_tp)->ftt_retids == NULL);10321033probe->ftp_tps[index].fit_tp = *tmp_tp;1034*tmp_tp = tp;1035}10361037mutex_exit(&bucket->ftb_mtx);10381039/*1040* Tag the modified probe with the generation in which it was1041* changed.1042*/1043probe->ftp_gen = fasttrap_mod_gen;1044return;1045}10461047mutex_exit(&bucket->ftb_mtx);10481049/*1050* We can't safely remove the tracepoint from the set of active1051* tracepoints until we've actually removed the fasttrap instruction1052* from the process's text. We can, however, operate on this1053* tracepoint secure in the knowledge that no other thread is going to1054* be looking at it since we hold P_PR_LOCK on the process if it's1055* live or we hold the provider lock on the process if it's dead and1056* gone.1057*/10581059/*1060* We only need to remove the actual instruction if we're looking1061* at an existing process1062*/1063if (p != NULL) {1064/*1065* If we fail to restore the instruction we need to kill1066* this process since it's in a completely unrecoverable1067* state.1068*/1069if (fasttrap_tracepoint_remove(p, tp) != 0)1070fasttrap_sigtrap(p, NULL, pc);10711072/*1073* Decrement the count of the number of tracepoints active1074* in the victim process.1075*/1076#ifdef illumos1077ASSERT(p->p_proc_flag & P_PR_LOCK);1078#endif1079p->p_dtrace_count--;10801081atomic_add_rel_64(&p->p_fasttrap_tp_gen, 1);1082}10831084/*1085* Remove the probe from the hash table of active tracepoints.1086*/1087mutex_enter(&bucket->ftb_mtx);1088pp = (fasttrap_tracepoint_t **)&bucket->ftb_data;1089ASSERT(*pp != NULL);1090while (*pp != tp) {1091pp = &(*pp)->ftt_next;1092ASSERT(*pp != NULL);1093}10941095*pp = tp->ftt_next;1096membar_producer();10971098mutex_exit(&bucket->ftb_mtx);10991100/*1101* Tag the modified probe with the generation in which it was changed.1102*/1103probe->ftp_gen = fasttrap_mod_gen;1104}11051106static void1107fasttrap_enable_callbacks(void)1108{1109/*1110* We don't have to play the rw lock game here because we're1111* providing something rather than taking something away --1112* we can be sure that no threads have tried to follow this1113* function pointer yet.1114*/1115mutex_enter(&fasttrap_count_mtx);1116if (fasttrap_pid_count == 0) {1117ASSERT(dtrace_pid_probe_ptr == NULL);1118ASSERT(dtrace_return_probe_ptr == NULL);1119dtrace_pid_probe_ptr = &fasttrap_pid_probe;1120dtrace_return_probe_ptr = &fasttrap_return_probe;1121}1122ASSERT(dtrace_pid_probe_ptr == &fasttrap_pid_probe);1123ASSERT(dtrace_return_probe_ptr == &fasttrap_return_probe);1124fasttrap_pid_count++;1125mutex_exit(&fasttrap_count_mtx);1126}11271128static void1129fasttrap_disable_callbacks(void)1130{1131mutex_enter(&fasttrap_count_mtx);1132ASSERT(fasttrap_pid_count > 0);1133fasttrap_pid_count--;1134if (fasttrap_pid_count == 0) {1135/*1136* Synchronize with the breakpoint handler, which is careful to1137* enable interrupts only after loading the hook pointer.1138*/1139dtrace_sync();1140dtrace_pid_probe_ptr = NULL;1141dtrace_return_probe_ptr = NULL;1142}1143mutex_exit(&fasttrap_count_mtx);1144}11451146/*ARGSUSED*/1147static void1148fasttrap_pid_enable(void *arg, dtrace_id_t id, void *parg)1149{1150fasttrap_probe_t *probe = parg;1151proc_t *p = NULL;1152int i, rc;11531154ASSERT(probe != NULL);1155ASSERT(!probe->ftp_enabled);1156ASSERT(id == probe->ftp_id);1157#ifdef illumos1158ASSERT(MUTEX_HELD(&cpu_lock));1159#endif11601161/*1162* Increment the count of enabled probes on this probe's provider;1163* the provider can't go away while the probe still exists. We1164* must increment this even if we aren't able to properly enable1165* this probe.1166*/1167mutex_enter(&probe->ftp_prov->ftp_mtx);1168probe->ftp_prov->ftp_rcount++;1169mutex_exit(&probe->ftp_prov->ftp_mtx);11701171/*1172* If this probe's provider is retired (meaning it was valid in a1173* previously exec'ed incarnation of this address space), bail out. The1174* provider can't go away while we're in this code path.1175*/1176if (probe->ftp_prov->ftp_retired)1177return;11781179/*1180* If we can't find the process, it may be that we're in the context of1181* a fork in which the traced process is being born and we're copying1182* USDT probes. Otherwise, the process is gone so bail.1183*/1184#ifdef illumos1185if ((p = sprlock(probe->ftp_pid)) == NULL) {1186if ((curproc->p_flag & SFORKING) == 0)1187return;11881189mutex_enter(&pidlock);1190p = prfind(probe->ftp_pid);11911192if (p == NULL) {1193/*1194* So it's not that the target process is being born,1195* it's that it isn't there at all (and we simply1196* happen to be forking). Anyway, we know that the1197* target is definitely gone, so bail out.1198*/1199mutex_exit(&pidlock);1200return (0);1201}12021203/*1204* Confirm that curproc is indeed forking the process in which1205* we're trying to enable probes.1206*/1207ASSERT(p->p_parent == curproc);1208ASSERT(p->p_stat == SIDL);12091210mutex_enter(&p->p_lock);1211mutex_exit(&pidlock);12121213sprlock_proc(p);1214}12151216ASSERT(!(p->p_flag & SVFORK));1217mutex_exit(&p->p_lock);1218#else1219if (pget(probe->ftp_pid, PGET_HOLD | PGET_NOTWEXIT, &p) != 0)1220return;1221#endif12221223/*1224* We have to enable the trap entry point before any user threads have1225* the chance to execute the trap instruction we're about to place1226* in their process's text.1227*/1228fasttrap_enable_callbacks();12291230/*1231* Enable all the tracepoints and add this probe's id to each1232* tracepoint's list of active probes.1233*/1234for (i = 0; i < probe->ftp_ntps; i++) {1235if ((rc = fasttrap_tracepoint_enable(p, probe, i)) != 0) {1236/*1237* If enabling the tracepoint failed completely,1238* we don't have to disable it; if the failure1239* was only partial we must disable it.1240*/1241if (rc == FASTTRAP_ENABLE_FAIL)1242i--;1243else1244ASSERT(rc == FASTTRAP_ENABLE_PARTIAL);12451246/*1247* Back up and pull out all the tracepoints we've1248* created so far for this probe.1249*/1250while (i >= 0) {1251fasttrap_tracepoint_disable(p, probe, i);1252i--;1253}12541255#ifdef illumos1256mutex_enter(&p->p_lock);1257sprunlock(p);1258#else1259PRELE(p);1260#endif12611262/*1263* Since we're not actually enabling this probe,1264* drop our reference on the trap table entry.1265*/1266fasttrap_disable_callbacks();1267return;1268}1269}1270#ifdef illumos1271mutex_enter(&p->p_lock);1272sprunlock(p);1273#else1274PRELE(p);1275#endif12761277probe->ftp_enabled = 1;1278}12791280/*ARGSUSED*/1281static void1282fasttrap_pid_disable(void *arg, dtrace_id_t id, void *parg)1283{1284fasttrap_probe_t *probe = parg;1285fasttrap_provider_t *provider = probe->ftp_prov;1286proc_t *p;1287int i, whack = 0;12881289ASSERT(id == probe->ftp_id);12901291mutex_enter(&provider->ftp_mtx);12921293/*1294* We won't be able to acquire a /proc-esque lock on the process1295* iff the process is dead and gone. In this case, we rely on the1296* provider lock as a point of mutual exclusion to prevent other1297* DTrace consumers from disabling this probe.1298*/1299if (pget(probe->ftp_pid, PGET_HOLD | PGET_NOTWEXIT, &p) != 0)1300p = NULL;13011302/*1303* Disable all the associated tracepoints (for fully enabled probes).1304*/1305if (probe->ftp_enabled) {1306for (i = 0; i < probe->ftp_ntps; i++) {1307fasttrap_tracepoint_disable(p, probe, i);1308}1309}13101311ASSERT(provider->ftp_rcount > 0);1312provider->ftp_rcount--;13131314if (p != NULL) {1315/*1316* Even though we may not be able to remove it entirely, we1317* mark this retired provider to get a chance to remove some1318* of the associated probes.1319*/1320if (provider->ftp_retired && !provider->ftp_marked)1321whack = provider->ftp_marked = 1;1322mutex_exit(&provider->ftp_mtx);1323} else {1324/*1325* If the process is dead, we're just waiting for the1326* last probe to be disabled to be able to free it.1327*/1328if (provider->ftp_rcount == 0 && !provider->ftp_marked)1329whack = provider->ftp_marked = 1;1330mutex_exit(&provider->ftp_mtx);1331}13321333if (whack)1334fasttrap_pid_cleanup();13351336#ifdef __FreeBSD__1337if (p != NULL)1338PRELE(p);1339#endif1340if (!probe->ftp_enabled)1341return;13421343probe->ftp_enabled = 0;13441345#ifdef illumos1346ASSERT(MUTEX_HELD(&cpu_lock));1347#endif1348fasttrap_disable_callbacks();1349}13501351/*ARGSUSED*/1352static void1353fasttrap_pid_getargdesc(void *arg, dtrace_id_t id, void *parg,1354dtrace_argdesc_t *desc)1355{1356fasttrap_probe_t *probe = parg;1357char *str;1358int i, ndx;13591360desc->dtargd_native[0] = '\0';1361desc->dtargd_xlate[0] = '\0';13621363if (probe->ftp_prov->ftp_retired != 0 ||1364desc->dtargd_ndx >= probe->ftp_nargs) {1365desc->dtargd_ndx = DTRACE_ARGNONE;1366return;1367}13681369ndx = (probe->ftp_argmap != NULL) ?1370probe->ftp_argmap[desc->dtargd_ndx] : desc->dtargd_ndx;13711372str = probe->ftp_ntypes;1373for (i = 0; i < ndx; i++) {1374str += strlen(str) + 1;1375}13761377ASSERT(strlen(str + 1) < sizeof (desc->dtargd_native));1378(void) strcpy(desc->dtargd_native, str);13791380if (probe->ftp_xtypes == NULL)1381return;13821383str = probe->ftp_xtypes;1384for (i = 0; i < desc->dtargd_ndx; i++) {1385str += strlen(str) + 1;1386}13871388ASSERT(strlen(str + 1) < sizeof (desc->dtargd_xlate));1389(void) strcpy(desc->dtargd_xlate, str);1390}13911392/*ARGSUSED*/1393static void1394fasttrap_pid_destroy(void *arg, dtrace_id_t id, void *parg)1395{1396fasttrap_probe_t *probe = parg;1397int i;1398size_t size;13991400ASSERT(probe != NULL);1401ASSERT(!probe->ftp_enabled);1402ASSERT(fasttrap_total >= probe->ftp_ntps);14031404atomic_add_32(&fasttrap_total, -probe->ftp_ntps);1405size = offsetof(fasttrap_probe_t, ftp_tps[probe->ftp_ntps]);14061407if (probe->ftp_gen + 1 >= fasttrap_mod_gen)1408fasttrap_mod_barrier(probe->ftp_gen);14091410for (i = 0; i < probe->ftp_ntps; i++) {1411kmem_free(probe->ftp_tps[i].fit_tp,1412sizeof (fasttrap_tracepoint_t));1413}14141415kmem_free(probe, size);1416}141714181419static const dtrace_pattr_t pid_attr = {1420{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },1421{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },1422{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },1423{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },1424{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },1425};14261427static dtrace_pops_t pid_pops = {1428.dtps_provide = fasttrap_pid_provide,1429.dtps_provide_module = NULL,1430.dtps_enable = fasttrap_pid_enable,1431.dtps_disable = fasttrap_pid_disable,1432.dtps_suspend = NULL,1433.dtps_resume = NULL,1434.dtps_getargdesc = fasttrap_pid_getargdesc,1435.dtps_getargval = fasttrap_pid_getarg,1436.dtps_usermode = NULL,1437.dtps_destroy = fasttrap_pid_destroy1438};14391440static dtrace_pops_t usdt_pops = {1441.dtps_provide = fasttrap_pid_provide,1442.dtps_provide_module = NULL,1443.dtps_enable = fasttrap_pid_enable,1444.dtps_disable = fasttrap_pid_disable,1445.dtps_suspend = NULL,1446.dtps_resume = NULL,1447.dtps_getargdesc = fasttrap_pid_getargdesc,1448.dtps_getargval = fasttrap_usdt_getarg,1449.dtps_usermode = NULL,1450.dtps_destroy = fasttrap_pid_destroy1451};14521453static fasttrap_proc_t *1454fasttrap_proc_lookup(pid_t pid)1455{1456fasttrap_bucket_t *bucket;1457fasttrap_proc_t *fprc, *new_fprc;145814591460bucket = &fasttrap_procs.fth_table[FASTTRAP_PROCS_INDEX(pid)];1461mutex_enter(&bucket->ftb_mtx);14621463for (fprc = bucket->ftb_data; fprc != NULL; fprc = fprc->ftpc_next) {1464if (fprc->ftpc_pid == pid && fprc->ftpc_acount != 0) {1465mutex_enter(&fprc->ftpc_mtx);1466mutex_exit(&bucket->ftb_mtx);1467fprc->ftpc_rcount++;1468atomic_inc_64(&fprc->ftpc_acount);1469ASSERT(fprc->ftpc_acount <= fprc->ftpc_rcount);1470mutex_exit(&fprc->ftpc_mtx);14711472return (fprc);1473}1474}14751476/*1477* Drop the bucket lock so we don't try to perform a sleeping1478* allocation under it.1479*/1480mutex_exit(&bucket->ftb_mtx);14811482new_fprc = kmem_zalloc(sizeof (fasttrap_proc_t), KM_SLEEP);1483new_fprc->ftpc_pid = pid;1484new_fprc->ftpc_rcount = 1;1485new_fprc->ftpc_acount = 1;1486#ifndef illumos1487mutex_init(&new_fprc->ftpc_mtx, "fasttrap proc mtx", MUTEX_DEFAULT,1488NULL);1489#endif14901491mutex_enter(&bucket->ftb_mtx);14921493/*1494* Take another lap through the list to make sure a proc hasn't1495* been created for this pid while we weren't under the bucket lock.1496*/1497for (fprc = bucket->ftb_data; fprc != NULL; fprc = fprc->ftpc_next) {1498if (fprc->ftpc_pid == pid && fprc->ftpc_acount != 0) {1499mutex_enter(&fprc->ftpc_mtx);1500mutex_exit(&bucket->ftb_mtx);1501fprc->ftpc_rcount++;1502atomic_inc_64(&fprc->ftpc_acount);1503ASSERT(fprc->ftpc_acount <= fprc->ftpc_rcount);1504mutex_exit(&fprc->ftpc_mtx);15051506kmem_free(new_fprc, sizeof (fasttrap_proc_t));15071508return (fprc);1509}1510}15111512new_fprc->ftpc_next = bucket->ftb_data;1513bucket->ftb_data = new_fprc;15141515mutex_exit(&bucket->ftb_mtx);15161517return (new_fprc);1518}15191520static void1521fasttrap_proc_release(fasttrap_proc_t *proc)1522{1523fasttrap_bucket_t *bucket;1524fasttrap_proc_t *fprc, **fprcp;1525pid_t pid = proc->ftpc_pid;1526#ifndef illumos1527fasttrap_scrblock_t *scrblk, *scrblktmp;1528fasttrap_scrspace_t *scrspc, *scrspctmp;1529struct proc *p;1530struct thread *td;1531#endif15321533mutex_enter(&proc->ftpc_mtx);15341535ASSERT(proc->ftpc_rcount != 0);1536ASSERT(proc->ftpc_acount <= proc->ftpc_rcount);15371538if (--proc->ftpc_rcount != 0) {1539mutex_exit(&proc->ftpc_mtx);1540return;1541}15421543#ifndef illumos1544/*1545* Free all structures used to manage per-thread scratch space.1546*/1547LIST_FOREACH_SAFE(scrblk, &proc->ftpc_scrblks, ftsb_next,1548scrblktmp) {1549LIST_REMOVE(scrblk, ftsb_next);1550free(scrblk, M_SOLARIS);1551}1552LIST_FOREACH_SAFE(scrspc, &proc->ftpc_fscr, ftss_next, scrspctmp) {1553LIST_REMOVE(scrspc, ftss_next);1554free(scrspc, M_SOLARIS);1555}1556LIST_FOREACH_SAFE(scrspc, &proc->ftpc_ascr, ftss_next, scrspctmp) {1557LIST_REMOVE(scrspc, ftss_next);1558free(scrspc, M_SOLARIS);1559}15601561if ((p = pfind(pid)) != NULL) {1562FOREACH_THREAD_IN_PROC(p, td)1563td->t_dtrace_sscr = NULL;1564PROC_UNLOCK(p);1565}1566#endif15671568mutex_exit(&proc->ftpc_mtx);15691570/*1571* There should definitely be no live providers associated with this1572* process at this point.1573*/1574ASSERT(proc->ftpc_acount == 0);15751576bucket = &fasttrap_procs.fth_table[FASTTRAP_PROCS_INDEX(pid)];1577mutex_enter(&bucket->ftb_mtx);15781579fprcp = (fasttrap_proc_t **)&bucket->ftb_data;1580while ((fprc = *fprcp) != NULL) {1581if (fprc == proc)1582break;15831584fprcp = &fprc->ftpc_next;1585}15861587/*1588* Something strange has happened if we can't find the proc.1589*/1590ASSERT(fprc != NULL);15911592*fprcp = fprc->ftpc_next;15931594mutex_exit(&bucket->ftb_mtx);15951596kmem_free(fprc, sizeof (fasttrap_proc_t));1597}15981599/*1600* Lookup a fasttrap-managed provider based on its name and associated pid.1601* If the pattr argument is non-NULL, this function instantiates the provider1602* if it doesn't exist otherwise it returns NULL. The provider is returned1603* with its lock held.1604*/1605static fasttrap_provider_t *1606fasttrap_provider_lookup(pid_t pid, const char *name,1607const dtrace_pattr_t *pattr)1608{1609fasttrap_provider_t *fp, *new_fp = NULL;1610fasttrap_bucket_t *bucket;1611char provname[DTRACE_PROVNAMELEN];1612proc_t *p;1613cred_t *cred;16141615ASSERT(strlen(name) < sizeof (fp->ftp_name));1616ASSERT(pattr != NULL);16171618bucket = &fasttrap_provs.fth_table[FASTTRAP_PROVS_INDEX(pid, name)];1619mutex_enter(&bucket->ftb_mtx);16201621/*1622* Take a lap through the list and return the match if we find it.1623*/1624for (fp = bucket->ftb_data; fp != NULL; fp = fp->ftp_next) {1625if (fp->ftp_pid == pid && strcmp(fp->ftp_name, name) == 0 &&1626!fp->ftp_retired) {1627mutex_enter(&fp->ftp_mtx);1628mutex_exit(&bucket->ftb_mtx);1629return (fp);1630}1631}16321633/*1634* Drop the bucket lock so we don't try to perform a sleeping1635* allocation under it.1636*/1637mutex_exit(&bucket->ftb_mtx);16381639/*1640* Make sure the process exists, isn't a child created as the result1641* of a vfork(2), and isn't a zombie (but may be in fork).1642*/1643if ((p = pfind(pid)) == NULL)1644return (NULL);16451646/*1647* Increment p_dtrace_probes so that the process knows to inform us1648* when it exits or execs. fasttrap_provider_free() decrements this1649* when we're done with this provider.1650*/1651p->p_dtrace_probes++;16521653/*1654* Grab the credentials for this process so we have1655* something to pass to dtrace_register().1656*/1657PROC_LOCK_ASSERT(p, MA_OWNED);1658crhold(p->p_ucred);1659cred = p->p_ucred;1660PROC_UNLOCK(p);16611662new_fp = kmem_zalloc(sizeof (fasttrap_provider_t), KM_SLEEP);1663new_fp->ftp_pid = pid;1664new_fp->ftp_proc = fasttrap_proc_lookup(pid);1665#ifndef illumos1666mutex_init(&new_fp->ftp_mtx, "provider mtx", MUTEX_DEFAULT, NULL);1667mutex_init(&new_fp->ftp_cmtx, "lock on creating", MUTEX_DEFAULT, NULL);1668#endif16691670ASSERT(new_fp->ftp_proc != NULL);16711672mutex_enter(&bucket->ftb_mtx);16731674/*1675* Take another lap through the list to make sure a provider hasn't1676* been created for this pid while we weren't under the bucket lock.1677*/1678for (fp = bucket->ftb_data; fp != NULL; fp = fp->ftp_next) {1679if (fp->ftp_pid == pid && strcmp(fp->ftp_name, name) == 0 &&1680!fp->ftp_retired) {1681mutex_enter(&fp->ftp_mtx);1682mutex_exit(&bucket->ftb_mtx);1683fasttrap_provider_free(new_fp);1684crfree(cred);1685return (fp);1686}1687}16881689(void) strcpy(new_fp->ftp_name, name);16901691/*1692* Fail and return NULL if either the provider name is too long1693* or we fail to register this new provider with the DTrace1694* framework. Note that this is the only place we ever construct1695* the full provider name -- we keep it in pieces in the provider1696* structure.1697*/1698if (snprintf(provname, sizeof (provname), "%s%u", name, (uint_t)pid) >=1699sizeof (provname) ||1700dtrace_register(provname, pattr,1701DTRACE_PRIV_PROC | DTRACE_PRIV_OWNER | DTRACE_PRIV_ZONEOWNER, cred,1702pattr == &pid_attr ? &pid_pops : &usdt_pops, new_fp,1703&new_fp->ftp_provid) != 0) {1704mutex_exit(&bucket->ftb_mtx);1705fasttrap_provider_free(new_fp);1706crfree(cred);1707return (NULL);1708}17091710new_fp->ftp_next = bucket->ftb_data;1711bucket->ftb_data = new_fp;17121713mutex_enter(&new_fp->ftp_mtx);1714mutex_exit(&bucket->ftb_mtx);17151716crfree(cred);1717return (new_fp);1718}17191720static void1721fasttrap_provider_free(fasttrap_provider_t *provider)1722{1723pid_t pid = provider->ftp_pid;1724proc_t *p;17251726/*1727* There need to be no associated enabled probes, no consumers1728* creating probes, and no meta providers referencing this provider.1729*/1730ASSERT(provider->ftp_rcount == 0);1731ASSERT(provider->ftp_ccount == 0);1732ASSERT(provider->ftp_mcount == 0);17331734/*1735* If this provider hasn't been retired, we need to explicitly drop the1736* count of active providers on the associated process structure.1737*/1738if (!provider->ftp_retired) {1739atomic_dec_64(&provider->ftp_proc->ftpc_acount);1740ASSERT(provider->ftp_proc->ftpc_acount <1741provider->ftp_proc->ftpc_rcount);1742}17431744fasttrap_proc_release(provider->ftp_proc);17451746#ifndef illumos1747mutex_destroy(&provider->ftp_mtx);1748mutex_destroy(&provider->ftp_cmtx);1749#endif1750kmem_free(provider, sizeof (fasttrap_provider_t));17511752/*1753* Decrement p_dtrace_probes on the process whose provider we're1754* freeing. We don't have to worry about clobbering somone else's1755* modifications to it because we have locked the bucket that1756* corresponds to this process's hash chain in the provider hash1757* table. Don't sweat it if we can't find the process.1758*/1759if ((p = pfind(pid)) == NULL) {1760return;1761}17621763p->p_dtrace_probes--;1764#ifndef illumos1765PROC_UNLOCK(p);1766#endif1767}17681769static void1770fasttrap_provider_retire(pid_t pid, const char *name, int mprov)1771{1772fasttrap_provider_t *fp;1773fasttrap_bucket_t *bucket;1774dtrace_provider_id_t provid;17751776ASSERT(strlen(name) < sizeof (fp->ftp_name));17771778bucket = &fasttrap_provs.fth_table[FASTTRAP_PROVS_INDEX(pid, name)];1779mutex_enter(&bucket->ftb_mtx);17801781for (fp = bucket->ftb_data; fp != NULL; fp = fp->ftp_next) {1782if (fp->ftp_pid == pid && strcmp(fp->ftp_name, name) == 0 &&1783!fp->ftp_retired)1784break;1785}17861787if (fp == NULL) {1788mutex_exit(&bucket->ftb_mtx);1789return;1790}17911792mutex_enter(&fp->ftp_mtx);1793ASSERT(!mprov || fp->ftp_mcount > 0);1794if (mprov && --fp->ftp_mcount != 0) {1795mutex_exit(&fp->ftp_mtx);1796mutex_exit(&bucket->ftb_mtx);1797return;1798}17991800/*1801* Mark the provider to be removed in our post-processing step, mark it1802* retired, and drop the active count on its proc. Marking it indicates1803* that we should try to remove it; setting the retired flag indicates1804* that we're done with this provider; dropping the active the proc1805* releases our hold, and when this reaches zero (as it will during1806* exit or exec) the proc and associated providers become defunct.1807*1808* We obviously need to take the bucket lock before the provider lock1809* to perform the lookup, but we need to drop the provider lock1810* before calling into the DTrace framework since we acquire the1811* provider lock in callbacks invoked from the DTrace framework. The1812* bucket lock therefore protects the integrity of the provider hash1813* table.1814*/1815atomic_dec_64(&fp->ftp_proc->ftpc_acount);1816ASSERT(fp->ftp_proc->ftpc_acount < fp->ftp_proc->ftpc_rcount);18171818fp->ftp_retired = 1;1819fp->ftp_marked = 1;1820provid = fp->ftp_provid;1821mutex_exit(&fp->ftp_mtx);18221823/*1824* We don't have to worry about invalidating the same provider twice1825* since fasttrap_provider_lookup() will ignore provider that have1826* been marked as retired.1827*/1828dtrace_invalidate(provid);18291830mutex_exit(&bucket->ftb_mtx);18311832fasttrap_pid_cleanup();1833}18341835static int1836fasttrap_uint32_cmp(const void *ap, const void *bp)1837{1838return (*(const uint32_t *)ap - *(const uint32_t *)bp);1839}18401841static int1842fasttrap_uint64_cmp(const void *ap, const void *bp)1843{1844return (*(const uint64_t *)ap - *(const uint64_t *)bp);1845}18461847static int1848fasttrap_add_probe(fasttrap_probe_spec_t *pdata)1849{1850fasttrap_provider_t *provider;1851fasttrap_probe_t *pp;1852fasttrap_tracepoint_t *tp;1853char *name;1854int i, aframes = 0, whack;18551856/*1857* There needs to be at least one desired trace point.1858*/1859if (pdata->ftps_noffs == 0)1860return (EINVAL);18611862switch (pdata->ftps_type) {1863case DTFTP_ENTRY:1864name = "entry";1865aframes = FASTTRAP_ENTRY_AFRAMES;1866break;1867case DTFTP_RETURN:1868name = "return";1869aframes = FASTTRAP_RETURN_AFRAMES;1870break;1871case DTFTP_OFFSETS:1872name = NULL;1873break;1874default:1875return (EINVAL);1876}18771878if ((provider = fasttrap_provider_lookup(pdata->ftps_pid,1879FASTTRAP_PID_NAME, &pid_attr)) == NULL)1880return (ESRCH);18811882/*1883* Increment this reference count to indicate that a consumer is1884* actively adding a new probe associated with this provider. This1885* prevents the provider from being deleted -- we'll need to check1886* for pending deletions when we drop this reference count.1887*/1888provider->ftp_ccount++;1889mutex_exit(&provider->ftp_mtx);18901891/*1892* Grab the creation lock to ensure consistency between calls to1893* dtrace_probe_lookup() and dtrace_probe_create() in the face of1894* other threads creating probes. We must drop the provider lock1895* before taking this lock to avoid a three-way deadlock with the1896* DTrace framework.1897*/1898mutex_enter(&provider->ftp_cmtx);18991900if (name == NULL) {1901for (i = 0; i < pdata->ftps_noffs; i++) {1902char name_str[17];19031904(void) sprintf(name_str, "%llx",1905(unsigned long long)pdata->ftps_offs[i]);19061907if (dtrace_probe_lookup(provider->ftp_provid,1908pdata->ftps_mod, pdata->ftps_func, name_str) != 0)1909continue;19101911atomic_inc_32(&fasttrap_total);19121913if (fasttrap_total > fasttrap_max) {1914atomic_dec_32(&fasttrap_total);1915goto no_mem;1916}19171918pp = kmem_zalloc(sizeof (fasttrap_probe_t), KM_SLEEP);19191920pp->ftp_prov = provider;1921pp->ftp_faddr = pdata->ftps_pc;1922pp->ftp_fsize = pdata->ftps_size;1923pp->ftp_pid = pdata->ftps_pid;1924pp->ftp_ntps = 1;19251926tp = kmem_zalloc(sizeof (fasttrap_tracepoint_t),1927KM_SLEEP);19281929tp->ftt_proc = provider->ftp_proc;1930tp->ftt_pc = pdata->ftps_offs[i] + pdata->ftps_pc;1931tp->ftt_pid = pdata->ftps_pid;19321933pp->ftp_tps[0].fit_tp = tp;1934pp->ftp_tps[0].fit_id.fti_probe = pp;1935pp->ftp_tps[0].fit_id.fti_ptype = pdata->ftps_type;19361937pp->ftp_id = dtrace_probe_create(provider->ftp_provid,1938pdata->ftps_mod, pdata->ftps_func, name_str,1939FASTTRAP_OFFSET_AFRAMES, pp);1940}19411942} else if (dtrace_probe_lookup(provider->ftp_provid, pdata->ftps_mod,1943pdata->ftps_func, name) == 0) {1944atomic_add_32(&fasttrap_total, pdata->ftps_noffs);19451946if (fasttrap_total > fasttrap_max) {1947atomic_add_32(&fasttrap_total, -pdata->ftps_noffs);1948goto no_mem;1949}19501951/*1952* Make sure all tracepoint program counter values are unique.1953* We later assume that each probe has exactly one tracepoint1954* for a given pc.1955*/1956qsort(pdata->ftps_offs, pdata->ftps_noffs,1957sizeof (uint64_t), fasttrap_uint64_cmp);1958for (i = 1; i < pdata->ftps_noffs; i++) {1959if (pdata->ftps_offs[i] > pdata->ftps_offs[i - 1])1960continue;19611962atomic_add_32(&fasttrap_total, -pdata->ftps_noffs);1963goto no_mem;1964}19651966ASSERT(pdata->ftps_noffs > 0);1967pp = kmem_zalloc(offsetof(fasttrap_probe_t,1968ftp_tps[pdata->ftps_noffs]), KM_SLEEP);19691970pp->ftp_prov = provider;1971pp->ftp_faddr = pdata->ftps_pc;1972pp->ftp_fsize = pdata->ftps_size;1973pp->ftp_pid = pdata->ftps_pid;1974pp->ftp_ntps = pdata->ftps_noffs;19751976for (i = 0; i < pdata->ftps_noffs; i++) {1977tp = kmem_zalloc(sizeof (fasttrap_tracepoint_t),1978KM_SLEEP);19791980tp->ftt_proc = provider->ftp_proc;1981tp->ftt_pc = pdata->ftps_offs[i] + pdata->ftps_pc;1982tp->ftt_pid = pdata->ftps_pid;19831984pp->ftp_tps[i].fit_tp = tp;1985pp->ftp_tps[i].fit_id.fti_probe = pp;1986pp->ftp_tps[i].fit_id.fti_ptype = pdata->ftps_type;1987}19881989pp->ftp_id = dtrace_probe_create(provider->ftp_provid,1990pdata->ftps_mod, pdata->ftps_func, name, aframes, pp);1991}19921993mutex_exit(&provider->ftp_cmtx);19941995/*1996* We know that the provider is still valid since we incremented the1997* creation reference count. If someone tried to clean up this provider1998* while we were using it (e.g. because the process called exec(2) or1999* exit(2)), take note of that and try to clean it up now.2000*/2001mutex_enter(&provider->ftp_mtx);2002provider->ftp_ccount--;2003whack = provider->ftp_retired;2004mutex_exit(&provider->ftp_mtx);20052006if (whack)2007fasttrap_pid_cleanup();20082009return (0);20102011no_mem:2012/*2013* If we've exhausted the allowable resources, we'll try to remove2014* this provider to free some up. This is to cover the case where2015* the user has accidentally created many more probes than was2016* intended (e.g. pid123:::).2017*/2018mutex_exit(&provider->ftp_cmtx);2019mutex_enter(&provider->ftp_mtx);2020provider->ftp_ccount--;2021provider->ftp_marked = 1;2022mutex_exit(&provider->ftp_mtx);20232024fasttrap_pid_cleanup();20252026return (ENOMEM);2027}20282029/*ARGSUSED*/2030static void *2031fasttrap_meta_provide(void *arg, dtrace_helper_provdesc_t *dhpv, pid_t pid)2032{2033fasttrap_provider_t *provider;20342035/*2036* A 32-bit unsigned integer (like a pid for example) can be2037* expressed in 10 or fewer decimal digits. Make sure that we'll2038* have enough space for the provider name.2039*/2040if (strlen(dhpv->dthpv_provname) + 10 >=2041sizeof (provider->ftp_name)) {2042printf("failed to instantiate provider %s: "2043"name too long to accomodate pid", dhpv->dthpv_provname);2044return (NULL);2045}20462047/*2048* Don't let folks spoof the true pid provider.2049*/2050if (strcmp(dhpv->dthpv_provname, FASTTRAP_PID_NAME) == 0) {2051printf("failed to instantiate provider %s: "2052"%s is an invalid name", dhpv->dthpv_provname,2053FASTTRAP_PID_NAME);2054return (NULL);2055}20562057/*2058* The highest stability class that fasttrap supports is ISA; cap2059* the stability of the new provider accordingly.2060*/2061if (dhpv->dthpv_pattr.dtpa_provider.dtat_class > DTRACE_CLASS_ISA)2062dhpv->dthpv_pattr.dtpa_provider.dtat_class = DTRACE_CLASS_ISA;2063if (dhpv->dthpv_pattr.dtpa_mod.dtat_class > DTRACE_CLASS_ISA)2064dhpv->dthpv_pattr.dtpa_mod.dtat_class = DTRACE_CLASS_ISA;2065if (dhpv->dthpv_pattr.dtpa_func.dtat_class > DTRACE_CLASS_ISA)2066dhpv->dthpv_pattr.dtpa_func.dtat_class = DTRACE_CLASS_ISA;2067if (dhpv->dthpv_pattr.dtpa_name.dtat_class > DTRACE_CLASS_ISA)2068dhpv->dthpv_pattr.dtpa_name.dtat_class = DTRACE_CLASS_ISA;2069if (dhpv->dthpv_pattr.dtpa_args.dtat_class > DTRACE_CLASS_ISA)2070dhpv->dthpv_pattr.dtpa_args.dtat_class = DTRACE_CLASS_ISA;20712072if ((provider = fasttrap_provider_lookup(pid, dhpv->dthpv_provname,2073&dhpv->dthpv_pattr)) == NULL) {2074printf("failed to instantiate provider %s for "2075"process %u", dhpv->dthpv_provname, (uint_t)pid);2076return (NULL);2077}20782079/*2080* Up the meta provider count so this provider isn't removed until2081* the meta provider has been told to remove it.2082*/2083provider->ftp_mcount++;20842085mutex_exit(&provider->ftp_mtx);20862087return (provider);2088}20892090/*2091* We know a few things about our context here: we know that the probe being2092* created doesn't already exist (DTrace won't load DOF at the same address2093* twice, even if explicitly told to do so) and we know that we are2094* single-threaded with respect to the meta provider machinery. Knowing that2095* this is a new probe and that there is no way for us to race with another2096* operation on this provider allows us an important optimization: we need not2097* lookup a probe before adding it. Saving this lookup is important because2098* this code is in the fork path for processes with USDT probes, and lookups2099* here are potentially very expensive because of long hash conflicts on2100* module, function and name (DTrace doesn't hash on provider name).2101*/2102/*ARGSUSED*/2103static void2104fasttrap_meta_create_probe(void *arg, void *parg,2105dtrace_helper_probedesc_t *dhpb)2106{2107fasttrap_provider_t *provider = parg;2108fasttrap_probe_t *pp;2109fasttrap_tracepoint_t *tp;2110int i, j;2111uint32_t ntps;21122113/*2114* Since the meta provider count is non-zero we don't have to worry2115* about this provider disappearing.2116*/2117ASSERT(provider->ftp_mcount > 0);21182119/*2120* The offsets must be unique.2121*/2122qsort(dhpb->dthpb_offs, dhpb->dthpb_noffs, sizeof (uint32_t),2123fasttrap_uint32_cmp);2124for (i = 1; i < dhpb->dthpb_noffs; i++) {2125if (dhpb->dthpb_base + dhpb->dthpb_offs[i] <=2126dhpb->dthpb_base + dhpb->dthpb_offs[i - 1])2127return;2128}21292130qsort(dhpb->dthpb_enoffs, dhpb->dthpb_nenoffs, sizeof (uint32_t),2131fasttrap_uint32_cmp);2132for (i = 1; i < dhpb->dthpb_nenoffs; i++) {2133if (dhpb->dthpb_base + dhpb->dthpb_enoffs[i] <=2134dhpb->dthpb_base + dhpb->dthpb_enoffs[i - 1])2135return;2136}21372138ntps = dhpb->dthpb_noffs + dhpb->dthpb_nenoffs;2139ASSERT(ntps > 0);21402141atomic_add_32(&fasttrap_total, ntps);21422143if (fasttrap_total > fasttrap_max) {2144atomic_add_32(&fasttrap_total, -ntps);2145return;2146}21472148pp = kmem_zalloc(offsetof(fasttrap_probe_t, ftp_tps[ntps]), KM_SLEEP);21492150pp->ftp_prov = provider;2151pp->ftp_pid = provider->ftp_pid;2152pp->ftp_ntps = ntps;2153pp->ftp_nargs = dhpb->dthpb_xargc;2154pp->ftp_xtypes = dhpb->dthpb_xtypes;2155pp->ftp_ntypes = dhpb->dthpb_ntypes;21562157/*2158* First create a tracepoint for each actual point of interest.2159*/2160for (i = 0; i < dhpb->dthpb_noffs; i++) {2161tp = kmem_zalloc(sizeof (fasttrap_tracepoint_t), KM_SLEEP);21622163tp->ftt_proc = provider->ftp_proc;2164tp->ftt_pc = dhpb->dthpb_base + dhpb->dthpb_offs[i];2165tp->ftt_pid = provider->ftp_pid;21662167pp->ftp_tps[i].fit_tp = tp;2168pp->ftp_tps[i].fit_id.fti_probe = pp;2169pp->ftp_tps[i].fit_id.fti_ptype = DTFTP_OFFSETS;2170}21712172/*2173* Then create a tracepoint for each is-enabled point.2174*/2175for (j = 0; i < ntps; i++, j++) {2176tp = kmem_zalloc(sizeof (fasttrap_tracepoint_t), KM_SLEEP);21772178tp->ftt_proc = provider->ftp_proc;2179tp->ftt_pc = dhpb->dthpb_base + dhpb->dthpb_enoffs[j];2180tp->ftt_pid = provider->ftp_pid;21812182pp->ftp_tps[i].fit_tp = tp;2183pp->ftp_tps[i].fit_id.fti_probe = pp;2184pp->ftp_tps[i].fit_id.fti_ptype = DTFTP_IS_ENABLED;2185}21862187/*2188* If the arguments are shuffled around we set the argument remapping2189* table. Later, when the probe fires, we only remap the arguments2190* if the table is non-NULL.2191*/2192for (i = 0; i < dhpb->dthpb_xargc; i++) {2193if (dhpb->dthpb_args[i] != i) {2194pp->ftp_argmap = dhpb->dthpb_args;2195break;2196}2197}21982199/*2200* The probe is fully constructed -- register it with DTrace.2201*/2202pp->ftp_id = dtrace_probe_create(provider->ftp_provid, dhpb->dthpb_mod,2203dhpb->dthpb_func, dhpb->dthpb_name, FASTTRAP_OFFSET_AFRAMES, pp);2204}22052206/*ARGSUSED*/2207static void2208fasttrap_meta_remove(void *arg, dtrace_helper_provdesc_t *dhpv, pid_t pid)2209{2210/*2211* Clean up the USDT provider. There may be active consumers of the2212* provider busy adding probes, no damage will actually befall the2213* provider until that count has dropped to zero. This just puts2214* the provider on death row.2215*/2216fasttrap_provider_retire(pid, dhpv->dthpv_provname, 1);2217}22182219static dtrace_mops_t fasttrap_mops = {2220.dtms_create_probe = fasttrap_meta_create_probe,2221.dtms_provide_pid = fasttrap_meta_provide,2222.dtms_remove_pid = fasttrap_meta_remove2223};22242225/*ARGSUSED*/2226static int2227fasttrap_open(struct cdev *dev __unused, int oflags __unused,2228int devtype __unused, struct thread *td __unused)2229{2230return (0);2231}22322233/*ARGSUSED*/2234static int2235fasttrap_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int fflag,2236struct thread *td)2237{2238if (!dtrace_attached())2239return (EAGAIN);22402241if (cmd == FASTTRAPIOC_MAKEPROBE) {2242fasttrap_probe_spec_t *uprobe = *(fasttrap_probe_spec_t **)arg;2243fasttrap_probe_spec_t *probe;2244uint64_t noffs;2245size_t size;2246int ret, err;22472248if (copyin(&uprobe->ftps_noffs, &noffs,2249sizeof (uprobe->ftps_noffs)))2250return (EFAULT);22512252/*2253* Probes must have at least one tracepoint.2254*/2255if (noffs == 0)2256return (EINVAL);22572258size = sizeof (fasttrap_probe_spec_t) +2259sizeof (probe->ftps_offs[0]) * (noffs - 1);22602261if (size > 1024 * 1024)2262return (ENOMEM);22632264probe = kmem_alloc(size, KM_SLEEP);22652266if (copyin(uprobe, probe, size) != 0 ||2267probe->ftps_noffs != noffs) {2268kmem_free(probe, size);2269return (EFAULT);2270}22712272/*2273* Verify that the function and module strings contain no2274* funny characters.2275*/2276if (u8_validate(probe->ftps_func, strlen(probe->ftps_func),2277NULL, U8_VALIDATE_ENTIRE, &err) < 0) {2278ret = EINVAL;2279goto err;2280}22812282if (u8_validate(probe->ftps_mod, strlen(probe->ftps_mod),2283NULL, U8_VALIDATE_ENTIRE, &err) < 0) {2284ret = EINVAL;2285goto err;2286}22872288#ifdef notyet2289if (!PRIV_POLICY_CHOICE(cr, PRIV_ALL, B_FALSE)) {2290proc_t *p;2291pid_t pid = probe->ftps_pid;22922293mutex_enter(&pidlock);2294/*2295* Report an error if the process doesn't exist2296* or is actively being birthed.2297*/2298if ((p = pfind(pid)) == NULL || p->p_stat == SIDL) {2299mutex_exit(&pidlock);2300return (ESRCH);2301}2302mutex_enter(&p->p_lock);2303mutex_exit(&pidlock);23042305if ((ret = priv_proc_cred_perm(cr, p, NULL,2306VREAD | VWRITE)) != 0) {2307mutex_exit(&p->p_lock);2308return (ret);2309}2310mutex_exit(&p->p_lock);2311}2312#endif /* notyet */23132314ret = fasttrap_add_probe(probe);2315err:2316kmem_free(probe, size);23172318return (ret);23192320} else if (cmd == FASTTRAPIOC_GETINSTR) {2321fasttrap_instr_query_t instr;2322fasttrap_tracepoint_t *tp;2323uint_t index;2324#ifdef notyet2325int ret;2326#endif23272328if (copyin((void *)arg, &instr, sizeof (instr)) != 0)2329return (EFAULT);23302331#ifdef notyet2332if (!PRIV_POLICY_CHOICE(cr, PRIV_ALL, B_FALSE)) {2333proc_t *p;2334pid_t pid = instr.ftiq_pid;23352336mutex_enter(&pidlock);2337/*2338* Report an error if the process doesn't exist2339* or is actively being birthed.2340*/2341if ((p == pfind(pid)) == NULL || p->p_stat == SIDL) {2342mutex_exit(&pidlock);2343return (ESRCH);2344}2345mutex_enter(&p->p_lock);2346mutex_exit(&pidlock);23472348if ((ret = priv_proc_cred_perm(cr, p, NULL,2349VREAD)) != 0) {2350mutex_exit(&p->p_lock);2351return (ret);2352}23532354mutex_exit(&p->p_lock);2355}2356#endif /* notyet */23572358index = FASTTRAP_TPOINTS_INDEX(instr.ftiq_pid, instr.ftiq_pc);23592360mutex_enter(&fasttrap_tpoints.fth_table[index].ftb_mtx);2361tp = fasttrap_tpoints.fth_table[index].ftb_data;2362while (tp != NULL) {2363if (instr.ftiq_pid == tp->ftt_pid &&2364instr.ftiq_pc == tp->ftt_pc &&2365tp->ftt_proc->ftpc_acount != 0)2366break;23672368tp = tp->ftt_next;2369}23702371if (tp == NULL) {2372mutex_exit(&fasttrap_tpoints.fth_table[index].ftb_mtx);2373return (ENOENT);2374}23752376bcopy(&tp->ftt_instr, &instr.ftiq_instr,2377sizeof (instr.ftiq_instr));2378mutex_exit(&fasttrap_tpoints.fth_table[index].ftb_mtx);23792380if (copyout(&instr, (void *)arg, sizeof (instr)) != 0)2381return (EFAULT);23822383return (0);2384}23852386return (EINVAL);2387}23882389static int2390fasttrap_load(void)2391{2392ulong_t nent;2393int i, ret;23942395/* Create the /dev/dtrace/fasttrap entry. */2396fasttrap_cdev = make_dev(&fasttrap_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,2397"dtrace/fasttrap");23982399mtx_init(&fasttrap_cleanup_mtx, "fasttrap clean", "dtrace", MTX_DEF);2400mutex_init(&fasttrap_count_mtx, "fasttrap count mtx", MUTEX_DEFAULT,2401NULL);24022403#ifdef illumos2404fasttrap_max = ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,2405"fasttrap-max-probes", FASTTRAP_MAX_DEFAULT);2406#endif2407fasttrap_total = 0;24082409/*2410* Conjure up the tracepoints hashtable...2411*/2412#ifdef illumos2413nent = ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,2414"fasttrap-hash-size", FASTTRAP_TPOINTS_DEFAULT_SIZE);2415#else2416nent = tpoints_hash_size;2417#endif24182419if (nent == 0 || nent > 0x1000000)2420nent = FASTTRAP_TPOINTS_DEFAULT_SIZE;24212422tpoints_hash_size = nent;24232424if (ISP2(nent))2425fasttrap_tpoints.fth_nent = nent;2426else2427fasttrap_tpoints.fth_nent = 1 << fasttrap_highbit(nent);2428ASSERT(fasttrap_tpoints.fth_nent > 0);2429fasttrap_tpoints.fth_mask = fasttrap_tpoints.fth_nent - 1;2430fasttrap_tpoints.fth_table = kmem_zalloc(fasttrap_tpoints.fth_nent *2431sizeof (fasttrap_bucket_t), KM_SLEEP);2432#ifndef illumos2433for (i = 0; i < fasttrap_tpoints.fth_nent; i++)2434mutex_init(&fasttrap_tpoints.fth_table[i].ftb_mtx,2435"tracepoints bucket mtx", MUTEX_DEFAULT, NULL);2436#endif24372438/*2439* ... and the providers hash table...2440*/2441nent = FASTTRAP_PROVIDERS_DEFAULT_SIZE;2442if (ISP2(nent))2443fasttrap_provs.fth_nent = nent;2444else2445fasttrap_provs.fth_nent = 1 << fasttrap_highbit(nent);2446ASSERT(fasttrap_provs.fth_nent > 0);2447fasttrap_provs.fth_mask = fasttrap_provs.fth_nent - 1;2448fasttrap_provs.fth_table = kmem_zalloc(fasttrap_provs.fth_nent *2449sizeof (fasttrap_bucket_t), KM_SLEEP);2450#ifndef illumos2451for (i = 0; i < fasttrap_provs.fth_nent; i++)2452mutex_init(&fasttrap_provs.fth_table[i].ftb_mtx,2453"providers bucket mtx", MUTEX_DEFAULT, NULL);2454#endif24552456ret = kproc_create(fasttrap_pid_cleanup_cb, NULL,2457&fasttrap_cleanup_proc, 0, 0, "ftcleanup");2458if (ret != 0) {2459destroy_dev(fasttrap_cdev);2460#ifndef illumos2461for (i = 0; i < fasttrap_provs.fth_nent; i++)2462mutex_destroy(&fasttrap_provs.fth_table[i].ftb_mtx);2463for (i = 0; i < fasttrap_tpoints.fth_nent; i++)2464mutex_destroy(&fasttrap_tpoints.fth_table[i].ftb_mtx);2465#endif2466kmem_free(fasttrap_provs.fth_table, fasttrap_provs.fth_nent *2467sizeof (fasttrap_bucket_t));2468mtx_destroy(&fasttrap_cleanup_mtx);2469mutex_destroy(&fasttrap_count_mtx);2470return (ret);2471}247224732474/*2475* ... and the procs hash table.2476*/2477nent = FASTTRAP_PROCS_DEFAULT_SIZE;2478if (ISP2(nent))2479fasttrap_procs.fth_nent = nent;2480else2481fasttrap_procs.fth_nent = 1 << fasttrap_highbit(nent);2482ASSERT(fasttrap_procs.fth_nent > 0);2483fasttrap_procs.fth_mask = fasttrap_procs.fth_nent - 1;2484fasttrap_procs.fth_table = kmem_zalloc(fasttrap_procs.fth_nent *2485sizeof (fasttrap_bucket_t), KM_SLEEP);2486#ifndef illumos2487for (i = 0; i < fasttrap_procs.fth_nent; i++)2488mutex_init(&fasttrap_procs.fth_table[i].ftb_mtx,2489"processes bucket mtx", MUTEX_DEFAULT, NULL);24902491rm_init(&fasttrap_tp_lock, "fasttrap tracepoint");24922493/*2494* This event handler must run before kdtrace_thread_dtor() since it2495* accesses the thread's struct kdtrace_thread.2496*/2497fasttrap_thread_dtor_tag = EVENTHANDLER_REGISTER(thread_dtor,2498fasttrap_thread_dtor, NULL, EVENTHANDLER_PRI_FIRST);2499#endif25002501/*2502* Install our hooks into fork(2), exec(2), and exit(2).2503*/2504dtrace_fasttrap_fork = &fasttrap_fork;2505dtrace_fasttrap_exit = &fasttrap_exec_exit;2506dtrace_fasttrap_exec = &fasttrap_exec_exit;25072508(void) dtrace_meta_register("fasttrap", &fasttrap_mops, NULL,2509&fasttrap_meta_id);25102511return (0);2512}25132514static int2515fasttrap_unload(void)2516{2517int i, fail = 0;25182519/*2520* Unregister the meta-provider to make sure no new fasttrap-2521* managed providers come along while we're trying to close up2522* shop. If we fail to detach, we'll need to re-register as a2523* meta-provider. We can fail to unregister as a meta-provider2524* if providers we manage still exist.2525*/2526if (fasttrap_meta_id != DTRACE_METAPROVNONE &&2527dtrace_meta_unregister(fasttrap_meta_id) != 0)2528return (-1);25292530/*2531* Iterate over all of our providers. If there's still a process2532* that corresponds to that pid, fail to detach.2533*/2534for (i = 0; i < fasttrap_provs.fth_nent; i++) {2535fasttrap_provider_t **fpp, *fp;2536fasttrap_bucket_t *bucket = &fasttrap_provs.fth_table[i];25372538mutex_enter(&bucket->ftb_mtx);2539fpp = (fasttrap_provider_t **)&bucket->ftb_data;2540while ((fp = *fpp) != NULL) {2541/*2542* Acquire and release the lock as a simple way of2543* waiting for any other consumer to finish with2544* this provider. A thread must first acquire the2545* bucket lock so there's no chance of another thread2546* blocking on the provider's lock.2547*/2548mutex_enter(&fp->ftp_mtx);2549mutex_exit(&fp->ftp_mtx);25502551if (dtrace_unregister(fp->ftp_provid) != 0) {2552fail = 1;2553fpp = &fp->ftp_next;2554} else {2555*fpp = fp->ftp_next;2556fasttrap_provider_free(fp);2557}2558}25592560mutex_exit(&bucket->ftb_mtx);2561}25622563if (fail) {2564(void) dtrace_meta_register("fasttrap", &fasttrap_mops, NULL,2565&fasttrap_meta_id);25662567return (-1);2568}25692570/*2571* Stop new processes from entering these hooks now, before the2572* fasttrap_cleanup thread runs. That way all processes will hopefully2573* be out of these hooks before we free fasttrap_provs.fth_table2574*/2575ASSERT(dtrace_fasttrap_fork == &fasttrap_fork);2576dtrace_fasttrap_fork = NULL;25772578ASSERT(dtrace_fasttrap_exec == &fasttrap_exec_exit);2579dtrace_fasttrap_exec = NULL;25802581ASSERT(dtrace_fasttrap_exit == &fasttrap_exec_exit);2582dtrace_fasttrap_exit = NULL;25832584mtx_lock(&fasttrap_cleanup_mtx);2585fasttrap_cleanup_drain = 1;2586/* Wait for the cleanup thread to finish up and signal us. */2587wakeup(&fasttrap_cleanup_cv);2588mtx_sleep(&fasttrap_cleanup_drain, &fasttrap_cleanup_mtx, 0, "ftcld",25890);2590fasttrap_cleanup_proc = NULL;2591mtx_destroy(&fasttrap_cleanup_mtx);25922593#ifdef DEBUG2594mutex_enter(&fasttrap_count_mtx);2595ASSERT(fasttrap_pid_count == 0);2596mutex_exit(&fasttrap_count_mtx);2597#endif25982599#ifndef illumos2600EVENTHANDLER_DEREGISTER(thread_dtor, fasttrap_thread_dtor_tag);26012602for (i = 0; i < fasttrap_tpoints.fth_nent; i++)2603mutex_destroy(&fasttrap_tpoints.fth_table[i].ftb_mtx);2604for (i = 0; i < fasttrap_provs.fth_nent; i++)2605mutex_destroy(&fasttrap_provs.fth_table[i].ftb_mtx);2606for (i = 0; i < fasttrap_procs.fth_nent; i++)2607mutex_destroy(&fasttrap_procs.fth_table[i].ftb_mtx);2608#endif2609kmem_free(fasttrap_tpoints.fth_table,2610fasttrap_tpoints.fth_nent * sizeof (fasttrap_bucket_t));2611fasttrap_tpoints.fth_nent = 0;26122613kmem_free(fasttrap_provs.fth_table,2614fasttrap_provs.fth_nent * sizeof (fasttrap_bucket_t));2615fasttrap_provs.fth_nent = 0;26162617kmem_free(fasttrap_procs.fth_table,2618fasttrap_procs.fth_nent * sizeof (fasttrap_bucket_t));2619fasttrap_procs.fth_nent = 0;26202621#ifndef illumos2622destroy_dev(fasttrap_cdev);2623mutex_destroy(&fasttrap_count_mtx);2624rm_destroy(&fasttrap_tp_lock);2625#endif26262627return (0);2628}26292630/* ARGSUSED */2631static int2632fasttrap_modevent(module_t mod __unused, int type, void *data __unused)2633{2634int error = 0;26352636switch (type) {2637case MOD_LOAD:2638break;26392640case MOD_UNLOAD:2641break;26422643case MOD_SHUTDOWN:2644break;26452646default:2647error = EOPNOTSUPP;2648break;2649}2650return (error);2651}26522653SYSINIT(fasttrap_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, fasttrap_load,2654NULL);2655SYSUNINIT(fasttrap_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY,2656fasttrap_unload, NULL);26572658DEV_MODULE(fasttrap, fasttrap_modevent, NULL);2659MODULE_VERSION(fasttrap, 1);2660MODULE_DEPEND(fasttrap, dtrace, 1, 1, 1);2661MODULE_DEPEND(fasttrap, opensolaris, 1, 1, 1);266226632664