📄 fasttrap.c
字号:
static void *fasttrap_meta_provide(void *arg, dtrace_helper_provdesc_t *dhpv, pid_t pid){ fasttrap_provider_t *provider; /* * 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. */ if (strlen(dhpv->dthpv_provname) + 10 >= sizeof (provider->ftp_name)) { cmn_err(CE_WARN, "failed to instantiate provider %s: " "name too long to accomodate pid", dhpv->dthpv_provname); return (NULL); } /* * Don't let folks spoof the true pid provider. */ if (strcmp(dhpv->dthpv_provname, FASTTRAP_PID_NAME) == 0) { cmn_err(CE_WARN, "failed to instantiate provider %s: " "%s is an invalid name", dhpv->dthpv_provname, FASTTRAP_PID_NAME); return (NULL); } /* * The highest stability class that fasttrap supports is ISA; cap * the stability of the new provider accordingly. */ if (dhpv->dthpv_pattr.dtpa_provider.dtat_class >= DTRACE_CLASS_COMMON) dhpv->dthpv_pattr.dtpa_provider.dtat_class = DTRACE_CLASS_ISA; if (dhpv->dthpv_pattr.dtpa_mod.dtat_class >= DTRACE_CLASS_COMMON) dhpv->dthpv_pattr.dtpa_mod.dtat_class = DTRACE_CLASS_ISA; if (dhpv->dthpv_pattr.dtpa_func.dtat_class >= DTRACE_CLASS_COMMON) dhpv->dthpv_pattr.dtpa_func.dtat_class = DTRACE_CLASS_ISA; if (dhpv->dthpv_pattr.dtpa_name.dtat_class >= DTRACE_CLASS_COMMON) dhpv->dthpv_pattr.dtpa_name.dtat_class = DTRACE_CLASS_ISA; if (dhpv->dthpv_pattr.dtpa_args.dtat_class >= DTRACE_CLASS_COMMON) dhpv->dthpv_pattr.dtpa_args.dtat_class = DTRACE_CLASS_ISA; if ((provider = fasttrap_provider_lookup(pid, dhpv->dthpv_provname, &dhpv->dthpv_pattr)) == NULL) { cmn_err(CE_WARN, "failed to instantiate provider %s for " "process %u", dhpv->dthpv_provname, (uint_t)pid); return (NULL); } /* * We elevate the consumer count here to ensure that this provider * isn't removed until after the meta provider has been told to * remove it. */ provider->ftp_ccount++; mutex_exit(&provider->ftp_mtx); return (provider);}/*ARGSUSED*/static voidfasttrap_meta_create_probe(void *arg, void *parg, dtrace_helper_probedesc_t *dhpb){ fasttrap_provider_t *provider = parg; fasttrap_probe_t *pp; fasttrap_tracepoint_t *tp; size_t size; int i; mutex_enter(&provider->ftp_mtx); if (dtrace_probe_lookup(provider->ftp_provid, dhpb->dthpb_mod, dhpb->dthpb_func, dhpb->dthpb_name) != 0) { mutex_exit(&provider->ftp_mtx); return; } atomic_add_32(&fasttrap_total, dhpb->dthpb_noffs); if (fasttrap_total > fasttrap_max) { atomic_add_32(&fasttrap_total, -dhpb->dthpb_noffs); mutex_exit(&provider->ftp_mtx); return; } size = sizeof (fasttrap_probe_t) + sizeof (pp->ftp_tps[0]) * (dhpb->dthpb_noffs - 1); pp = kmem_zalloc(size, KM_SLEEP); pp->ftp_prov = provider; pp->ftp_pid = provider->ftp_pid; pp->ftp_ntps = dhpb->dthpb_noffs;#ifdef __sparc pp->ftp_type = DTFTP_POST_OFFSETS;#else pp->ftp_type = DTFTP_OFFSETS;#endif pp->ftp_nargs = dhpb->dthpb_xargc; pp->ftp_xtypes = dhpb->dthpb_xtypes; pp->ftp_ntypes = dhpb->dthpb_ntypes; for (i = 0; i < pp->ftp_ntps; i++) { tp = kmem_zalloc(sizeof (fasttrap_tracepoint_t), KM_SLEEP); tp->ftt_prov = provider; tp->ftt_pc = dhpb->dthpb_base + dhpb->dthpb_offs[i]; tp->ftt_pid = provider->ftp_pid; pp->ftp_tps[i].fit_tp = tp; pp->ftp_tps[i].fit_id.fti_probe = pp; } /* * 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) { pp->ftp_argmap = dhpb->dthpb_args; break; } } /* * The probe is fully constructed -- register it with DTrace. */ pp->ftp_id = dtrace_probe_create(provider->ftp_provid, dhpb->dthpb_mod, dhpb->dthpb_func, dhpb->dthpb_name, FASTTRAP_OFFSET_AFRAMES, pp); mutex_exit(&provider->ftp_mtx);}/*ARGSUSED*/static voidfasttrap_meta_remove(void *arg, dtrace_helper_provdesc_t *dhpv, pid_t pid){ fasttrap_provider_t *provider; if ((provider = fasttrap_provider_lookup(pid, dhpv->dthpv_provname, NULL)) != NULL) { /* * Drop the consumer count now that we're done with this * provider. If there are no other consumers retire it now. */ if (--provider->ftp_ccount == 0) fasttrap_provider_retire(provider); else mutex_exit(&provider->ftp_mtx); }}static dtrace_mops_t fasttrap_mops = { fasttrap_meta_create_probe, fasttrap_meta_provide, fasttrap_meta_remove};/*ARGSUSED*/static intfasttrap_open(dev_t *devp, int flag, int otyp, cred_t *cred_p){ return (0);}/*ARGSUSED*/static intfasttrap_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv){ if (!dtrace_attached()) return (EAGAIN); if (cmd == FASTTRAPIOC_MAKEPROBE) { fasttrap_probe_spec_t *uprobe = (void *)arg; fasttrap_probe_spec_t *probe; uint64_t noffs; size_t size; int ret; char *c; if (copyin(&uprobe->ftps_noffs, &noffs, sizeof (uprobe->ftps_noffs))) return (EFAULT); /* * Probes must have at least one tracepoint. */ if (noffs == 0) return (EINVAL); size = sizeof (fasttrap_probe_spec_t) + sizeof (probe->ftps_offs[0]) * (noffs - 1); if (size > 1024 * 1024) return (ENOMEM); probe = kmem_alloc(size, KM_SLEEP); if (copyin(uprobe, probe, size) != 0) { kmem_free(probe, size); return (EFAULT); } /* * Verify that the function and module strings contain no * funny characters. */ for (c = &probe->ftps_func[0]; *c != '\0'; c++) { if (*c < 0x20 || 0x7f <= *c) { ret = EINVAL; goto err; } } for (c = &probe->ftps_mod[0]; *c != '\0'; c++) { if (*c < 0x20 || 0x7f <= *c) { ret = EINVAL; goto err; } } if (!PRIV_POLICY_CHOICE(cr, PRIV_ALL, B_FALSE)) { proc_t *p; pid_t pid = probe->ftps_pid; mutex_enter(&pidlock); /* * Report an error if the process doesn't exist * or is actively being birthed. */ if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) { mutex_exit(&pidlock); return (ESRCH); } mutex_enter(&p->p_lock); mutex_exit(&pidlock); if ((ret = priv_proc_cred_perm(cr, p, NULL, VREAD | VWRITE)) != 0) { mutex_exit(&p->p_lock); return (ret); } mutex_exit(&p->p_lock); } ret = fasttrap_add_probe(probe);err: kmem_free(probe, size); return (ret); } else if (cmd == FASTTRAPIOC_GETINSTR) { fasttrap_instr_query_t instr; fasttrap_tracepoint_t *tp; uint_t index; int ret; if (copyin((void *)arg, &instr, sizeof (instr)) != 0) return (EFAULT); if (!PRIV_POLICY_CHOICE(cr, PRIV_ALL, B_FALSE)) { proc_t *p; pid_t pid = instr.ftiq_pid; mutex_enter(&pidlock); /* * Report an error if the process doesn't exist * or is actively being birthed. */ if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) { mutex_exit(&pidlock); return (ESRCH); } mutex_enter(&p->p_lock); mutex_exit(&pidlock); if ((ret = priv_proc_cred_perm(cr, p, NULL, VREAD)) != 0) { mutex_exit(&p->p_lock); return (ret); } mutex_exit(&p->p_lock); } index = FASTTRAP_TPOINTS_INDEX(instr.ftiq_pid, instr.ftiq_pc); mutex_enter(&fasttrap_tpoints.fth_table[index].ftb_mtx); tp = fasttrap_tpoints.fth_table[index].ftb_data; while (tp != NULL) { if (instr.ftiq_pid == tp->ftt_pid && instr.ftiq_pc == tp->ftt_pc && !tp->ftt_prov->ftp_defunct) break; tp = tp->ftt_next; } if (tp == NULL) { mutex_exit(&fasttrap_tpoints.fth_table[index].ftb_mtx); return (ENOENT); } bcopy(&tp->ftt_instr, &instr.ftiq_instr, sizeof (instr.ftiq_instr)); mutex_exit(&fasttrap_tpoints.fth_table[index].ftb_mtx); if (copyout(&instr, (void *)arg, sizeof (instr)) != 0) return (EFAULT); return (0); } return (EINVAL);}static struct cb_ops fasttrap_cb_ops = { fasttrap_open, /* open */ nodev, /* close */ nulldev, /* strategy */ nulldev, /* print */ nodev, /* dump */ nodev, /* read */ nodev, /* write */ fasttrap_ioctl, /* ioctl */ nodev, /* devmap */ nodev, /* mmap */ nodev, /* segmap */ nochpoll, /* poll */ ddi_prop_op, /* cb_prop_op */ 0, /* streamtab */ D_NEW | D_MP /* Driver compatibility flag */};/*ARGSUSED*/static intfasttrap_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result){ 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 intfasttrap_attach(dev_info_t *devi, ddi_attach_cmd_t cmd){ ulong_t nent; switch (cmd) { case DDI_ATTACH: break; case DDI_RESUME: return (DDI_SUCCESS); default: return (DDI_FAILURE); } if (ddi_create_minor_node(devi, "fasttrap", S_IFCHR, 0, DDI_PSEUDO, NULL) == DDI_FAILURE || dtrace_register("fasttrap", &fasttrap_attr, DTRACE_PRIV_USER, 0, &fasttrap_pops, NULL, &fasttrap_id) != 0) { ddi_remove_minor_node(devi, NULL); return (DDI_FAILURE); } ddi_report_dev(devi); fasttrap_devi = devi; /* * Install our hooks into fork(2), exec(2), and exit(2). */ dtrace_fasttrap_fork_ptr = &fasttrap_fork; dtrace_fasttrap_exit_ptr = &fasttrap_exec_exit; dtrace_fasttrap_exec_ptr = &fasttrap_exec_exit; fasttrap_max = ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, "fasttrap-max-probes", FASTTRAP_MAX_DEFAULT); fasttrap_total = 0; /* * Conjure up the tracepoints hashtable... */ nent = ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, "fasttrap-hash-size", FASTTRAP_TPOINTS_DEFAULT_SIZE); if (nent <= 0 || nent > 0x1000000) nent = FASTTRAP_TPOINTS_DEFAULT_SIZE; if ((nent & (nent - 1)) == 0) fasttrap_tpoints.fth_nent = nent; else fasttrap_tpoints.fth_nent = 1 << fasttrap_highbit(nent); ASSERT(fasttrap_tpoints.fth_nent > 0); fasttrap_tpoints.fth_mask = fasttrap_tpoints.fth_nent - 1; fasttrap_tpoints.fth_table = kmem_zalloc(fasttrap_tpoints.fth_nent * sizeof (fasttrap_bucket_t), KM_SLEEP); /* * ... and the providers hash table. */ nent = FASTTRAP_PROVIDERS_DEFAULT_SIZE; if ((nent & (nent - 1)) == 0) fasttrap_provs.fth_nent = nent; else fasttrap_provs.fth_nent = 1 << fasttrap_highbit(nent); ASSERT(fasttrap_provs.fth_nent > 0); fasttrap_provs.fth_mask = fasttrap_provs.fth_nent - 1; fasttrap_provs.fth_table = kmem_zalloc(fasttrap_provs.fth_nent * sizeof (fasttrap_bucket_t), KM_SLEEP); (void) dtrace_meta_register("fasttrap", &fasttrap_mops, NULL, &fasttrap_meta_id); return (DDI_SUCCESS);}static intfasttrap_detach(dev_info_t *devi, ddi_detach_cmd_t cmd){ int i, fail = 0; timeout_id_t tmp; 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 && dtrace_meta_unregister(fasttrap_meta_id) != 0) 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. */ mutex_enter(&fasttrap_cleanup_mtx); fasttrap_cleanup_work = 0; while (fasttrap_timeout != (timeout_id_t)1) { tmp = fasttrap_timeout; fasttrap_timeout = (timeout_id_t)1; if (tmp != 0) { mutex_exit(&fasttrap_cleanup_mtx); (void) untimeout(tmp); mutex_enter(&fasttrap_cleanup_mtx); } } fasttrap_cleanup_work = 0; mutex_exit(&fasttrap_cleanup_mtx); /* * 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++) { fasttrap_provider_t **fpp, *fp; fasttrap_bucket_t *bucket = &fasttrap_provs.fth_table[i];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -