From 397943fa073c73eb68315b71b1338c4f76db6193 Mon Sep 17 00:00:00 2001 From: Kris Van Hees Date: Thu, 29 Aug 2013 18:07:21 -0400 Subject: [PATCH] dtrace: Ensure that USDT probes are carried over correctly across fork(). When a process forks, its child will have a copy of the address space of the parent, and therefore any enabled USDT probes from the parent will also fire for the child. In order for those probe firings to be valid, the child must have its own pid-specific providers (created by duplicating the parent's providers). This commit also adds some additional cleanup. Orabug: 17346878 Signed-off-by: Kris Van Hees --- dtrace/dtrace_dev.c | 36 ++++++++++- dtrace/dtrace_dif.c | 47 ++++++++++++++- dtrace/dtrace_dof.c | 92 +++++++++++++++++++++++++++-- dtrace/dtrace_hash.c | 13 ++++ dtrace/dtrace_isa.c | 6 ++ dtrace/fasttrap_dev.c | 31 +++++----- dtrace/include/dtrace/dtrace_impl.h | 5 ++ 7 files changed, 210 insertions(+), 20 deletions(-) diff --git a/dtrace/dtrace_dev.c b/dtrace/dtrace_dev.c index 79507d3929df..8d267856d6e0 100644 --- a/dtrace/dtrace_dev.c +++ b/dtrace/dtrace_dev.c @@ -1393,6 +1393,7 @@ int dtrace_dev_init(void) dtrace_modload = dtrace_module_loaded; dtrace_modunload = dtrace_module_unloaded; dtrace_helpers_cleanup = dtrace_helpers_destroy; + dtrace_helpers_fork = dtrace_helpers_duplicate; #ifdef FIXME dtrace_cpu_init = dtrace_cpu_setup_initial; dtrace_cpustart_init = dtrace_suspend; @@ -1506,9 +1507,42 @@ int dtrace_dev_init(void) void dtrace_dev_exit(void) { + mutex_lock(&cpu_lock); + mutex_lock(&dtrace_provider_lock); + mutex_lock(&dtrace_lock); + + dtrace_unregister((dtrace_provider_id_t)dtrace_provider); + dtrace_provider = NULL; + + dtrace_probe_exit(); + + dtrace_modload = NULL; + dtrace_modunload = NULL; + dtrace_helpers_cleanup = NULL; + dtrace_helpers_fork = NULL; +#ifdef FIXME + dtrace_cpu_init = NULL; + dtrace_cpustart_init = NULL; + dtrace_cpustart_fini = NULL; + dtrace_debugger_init = NULL; + dtrace_debugger_fini = NULL; + + unregister_cpu_setup_func((cpu_setup_func_t *)dtrace_cpu_setup, NULL); +#endif + + mutex_unlock(&cpu_lock); + + dtrace_hash_destroy(dtrace_bymod); + dtrace_hash_destroy(dtrace_byfunc); + dtrace_hash_destroy(dtrace_byname); + dtrace_bymod = NULL; + dtrace_byfunc = NULL; + dtrace_byname = NULL; + kmem_cache_destroy(dtrace_state_cache); misc_deregister(&helper_dev); misc_deregister(&dtrace_dev); - dtrace_probe_exit(); + mutex_unlock(&dtrace_lock); + mutex_unlock(&dtrace_provider_lock); } diff --git a/dtrace/dtrace_dif.c b/dtrace/dtrace_dif.c index cd30f6fed80b..e2fa3365d481 100644 --- a/dtrace/dtrace_dif.c +++ b/dtrace/dtrace_dif.c @@ -941,6 +941,51 @@ void dtrace_difo_init(dtrace_difo_t *dp, dtrace_vstate_t *vstate) dtrace_difo_hold(dp); } +dtrace_difo_t * dtrace_difo_duplicate(dtrace_difo_t *dp, + dtrace_vstate_t *vstate) +{ + dtrace_difo_t *new; + size_t sz; + + ASSERT(dp->dtdo_buf != NULL); + ASSERT(dp->dtdo_refcnt != 0); + + new = vzalloc(sizeof(dtrace_difo_t)); + + ASSERT(dp->dtdo_buf != NULL); + sz = dp->dtdo_len * sizeof(dif_instr_t); + new->dtdo_buf = vmalloc(sz); + memcpy(new->dtdo_buf, dp->dtdo_buf, sz); + new->dtdo_len = dp->dtdo_len; + + if (dp->dtdo_strtab != NULL) { + ASSERT(dp->dtdo_strlen != 0); + new->dtdo_strtab = vmalloc(dp->dtdo_strlen); + memcpy(new->dtdo_strtab, dp->dtdo_strtab, dp->dtdo_strlen); + new->dtdo_strlen = dp->dtdo_strlen; + } + + if (dp->dtdo_inttab != NULL) { + ASSERT(dp->dtdo_intlen != 0); + sz = dp->dtdo_intlen * sizeof(uint64_t); + new->dtdo_inttab = vmalloc(sz); + memcpy(new->dtdo_inttab, dp->dtdo_inttab, sz); + new->dtdo_intlen = dp->dtdo_intlen; + } + + if (dp->dtdo_vartab != NULL) { + ASSERT(dp->dtdo_varlen != 0); + sz = dp->dtdo_varlen * sizeof(dtrace_difv_t); + new->dtdo_vartab = vmalloc(sz); + memcpy(new->dtdo_vartab, dp->dtdo_vartab, sz); + new->dtdo_varlen = dp->dtdo_varlen; + } + + dtrace_difo_init(new, vstate); + + return new; +} + void dtrace_difo_destroy(dtrace_difo_t *dp, dtrace_vstate_t *vstate) { int i; @@ -2275,7 +2320,7 @@ static uint64_t dtrace_dif_variable(dtrace_mstate_t *mstate, } } -#define DTRACE_V4MAPPED_OFFSET (sizeof (uint32_t) * 3) +#define DTRACE_V4MAPPED_OFFSET (sizeof(uint32_t) * 3) /* * Emulate the execution of DTrace ID subroutines invoked by the call opcode. diff --git a/dtrace/dtrace_dof.c b/dtrace/dtrace_dof.c index ee70ded7358d..179c65f28d52 100644 --- a/dtrace/dtrace_dof.c +++ b/dtrace/dtrace_dof.c @@ -1800,7 +1800,7 @@ void dtrace_helper_provide(dof_helper_t *dhp, pid_t pid) dtrace_enabling_matchall(); } -static void dtrace_helper_provider_register(struct task_struct *curr, +static void dtrace_helper_provider_register(struct task_struct *tsk, dtrace_helpers_t *dth, dof_helper_t *dofhp) { @@ -1821,7 +1821,7 @@ static void dtrace_helper_provider_register(struct task_struct *curr, if (dth->dthps_next == NULL && dth->dthps_prev == NULL && dtrace_deferred_pid != dth) { dth->dthps_deferred = 1; - dth->dthps_pid = current->pid; + dth->dthps_pid = tsk->pid; dth->dthps_next = dtrace_deferred_pid; dth->dthps_prev = NULL; if (dtrace_deferred_pid != NULL) @@ -1838,7 +1838,7 @@ static void dtrace_helper_provider_register(struct task_struct *curr, */ mutex_unlock(&dtrace_lock); - dtrace_helper_provide(dofhp, current->pid); + dtrace_helper_provide(dofhp, tsk->pid); } else { /* * Otherwise, just pass all the helper provider descriptions @@ -1850,7 +1850,7 @@ static void dtrace_helper_provider_register(struct task_struct *curr, for (i = 0; i < dth->dthps_nprovs; i++) { dtrace_helper_provide(&dth->dthps_provs[i]->dthp_prov, - current->pid); + tsk->pid); } } @@ -2059,6 +2059,90 @@ void dtrace_helpers_destroy(struct task_struct *tsk) mutex_unlock(&dtrace_lock); } +void dtrace_helpers_duplicate(struct task_struct *from, struct task_struct *to) +{ + dtrace_helpers_t *help, *newhelp; + dtrace_helper_action_t *helper, *new, *last; + dtrace_difo_t *dp; + dtrace_vstate_t *vstate; + int i, j, sz, hasprovs = 0; + + mutex_lock(&dtrace_lock); + + ASSERT(from->dtrace_helpers != NULL); + ASSERT(dtrace_helpers > 0); + + help = from->dtrace_helpers; + newhelp = dtrace_helpers_create(to); + + ASSERT(to->dtrace_helpers != NULL); + + newhelp->dthps_generation = help->dthps_generation; + vstate = &newhelp->dthps_vstate; + + /* + * Duplicate the helper actions. + */ + for (i = 0; i < DTRACE_NHELPER_ACTIONS; i++) { + if ((helper = help->dthps_actions[i]) == NULL) + continue; + + for (last = NULL; helper != NULL; helper = helper->dtha_next) { + new = vzalloc(sizeof(dtrace_helper_action_t)); + new->dtha_generation = helper->dtha_generation; + + if ((dp = helper->dtha_predicate) != NULL) { + dp = dtrace_difo_duplicate(dp, vstate); + new->dtha_predicate = dp; + } + + new->dtha_nactions = helper->dtha_nactions; + sz = sizeof(dtrace_difo_t *) * new->dtha_nactions; + new->dtha_actions = vmalloc(sz); + + for (j = 0; j < new->dtha_nactions; j++) { + dtrace_difo_t *dp = helper->dtha_actions[j]; + + ASSERT(dp != NULL); + + dp = dtrace_difo_duplicate(dp, vstate); + new->dtha_actions[j] = dp; + } + + if (last != NULL) + last->dtha_next = new; + else + newhelp->dthps_actions[i] = new; + + last = new; + } + } + + /* + * Duplicate the helper providers and register them with the + * DTrace framework. + */ + if (help->dthps_nprovs > 0) { + newhelp->dthps_nprovs = help->dthps_nprovs; + newhelp->dthps_maxprovs = help->dthps_nprovs; + newhelp->dthps_provs = vmalloc( + newhelp->dthps_nprovs * + sizeof(dtrace_helper_provider_t *)); + + for (i = 0; i < newhelp->dthps_nprovs; i++) { + newhelp->dthps_provs[i] = help->dthps_provs[i]; + newhelp->dthps_provs[i]->dthp_ref++; + } + + hasprovs = 1; + } + + mutex_unlock(&dtrace_lock); + + if (hasprovs) + dtrace_helper_provider_register(to, newhelp, NULL); +} + int dtrace_helper_destroygen(int gen) { struct task_struct *p = current; diff --git a/dtrace/dtrace_hash.c b/dtrace/dtrace_hash.c index 1d6a278725a7..f34144c6869c 100644 --- a/dtrace/dtrace_hash.c +++ b/dtrace/dtrace_hash.c @@ -77,6 +77,19 @@ dtrace_hash_t *dtrace_hash_create(uintptr_t stroffs, uintptr_t nextoffs, return hash; } +void dtrace_hash_destroy(dtrace_hash_t *hash) +{ +#ifdef DEBUG + int i; + + for (i = 0; i < hash->dth_size; i++) + ASSERT(hash->dth_tab[i] == NULL); +#endif + + vfree(hash->dth_tab); + vfree(hash); +} + static int dtrace_hash_resize(dtrace_hash_t *hash) { int size = hash->dth_size, i, ndx; diff --git a/dtrace/dtrace_isa.c b/dtrace/dtrace_isa.c index 189d17058169..05761668508c 100644 --- a/dtrace/dtrace_isa.c +++ b/dtrace/dtrace_isa.c @@ -123,6 +123,12 @@ uint64_t dtrace_getarg(int argno, int aframes) asm volatile("movq %%rbp,%0" : "=m"(bp)); +#if 0 +for (i = 0, st = (uint64_t *)bp; i < 180; i++) pr_info("stack[%3d] %p = %llx\n", i, &st[i], st[i]); +#endif +#if 0 +for (i = 0, st = (uint64_t *)bp; i < 16; i++) { pr_info("BP at frame %d: %p -> (%llx, %llx)\n", i, st, st[0], st[1]); if (st[0] == 0) break; st = (uint64_t *)st[0]; } +#endif for (i = 0; i < aframes; i++) bp = *((unsigned long *)bp); diff --git a/dtrace/fasttrap_dev.c b/dtrace/fasttrap_dev.c index 833685f6f908..08c639ae5e38 100644 --- a/dtrace/fasttrap_dev.c +++ b/dtrace/fasttrap_dev.c @@ -110,6 +110,7 @@ static void fasttrap_usdt_args64(fasttrap_probe_t *probe, struct pt_regs *regs, while (i < argc) argv[i++] = 0; +for (i = 0; i < argc; i++) pr_info("%s: argv[%2d] = %lx\n", __FUNCTION__, i, argv[i]); } static int fasttrap_pid_probe(fasttrap_machtp_t *mtp, struct pt_regs *regs) { @@ -118,6 +119,19 @@ static int fasttrap_pid_probe(fasttrap_machtp_t *mtp, struct pt_regs *regs) { fasttrap_id_t *id; int is_enabled = 0; + /* + * Verify that this probe event is actually related to the current + * task. If not, ignore it. + * + * TODO: The underlying probe mechanism should register a single + * handler for the (inode, offset) combination. When the handler + * is called, it should run through a list of fasttrap + * tracepoints associated with the OS-level probe, looking for + * one that is related to the current task. + */ + if (tp->ftt_pid != current->pid) + return 0; + if (atomic64_read(&tp->ftt_proc->ftpc_acount) == 0) return 0; @@ -1114,7 +1128,7 @@ static fasttrap_provider_t *fasttrap_provider_lookup(pid_t pid, fasttrap_bucket_t *bucket; char provname[DTRACE_PROVNAMELEN]; struct task_struct *p; - const cred_t *cred; + const cred_t *cred = NULL; ASSERT(strlen(name) < sizeof (fp->ftp_name)); ASSERT(pa != NULL); @@ -1210,10 +1224,10 @@ static fasttrap_provider_t *fasttrap_provider_lookup(pid_t pid, return new_fp; fail: - put_cred(cred); - if (proc) fasttrap_proc_release(proc); + if (cred) + put_cred(cred); if (p) unregister_pid_provider(pid); @@ -1560,7 +1574,6 @@ int fasttrap_dev_init(void) } #ifdef FIXME - dtrace_fasttrap_fork_ptr = &fasttrap_fork; dtrace_fasttrap_exit_ptr = &fasttrap_exec_exit; dtrace_fasttrap_exec_ptr = &fasttrap_exec_exit; #endif @@ -1708,17 +1721,7 @@ void fasttrap_dev_exit(void) vfree(fasttrap_procs.fth_table); 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(). - */ #ifdef FIXME - ASSERT(dtrace_fasttrap_fork_ptr == &fasttrap_fork); - dtrace_fasttrap_fork_ptr = NULL; - ASSERT(dtrace_fasttrap_exec_ptr == &fasttrap_exec_exit); dtrace_fasttrap_exec_ptr = NULL; diff --git a/dtrace/include/dtrace/dtrace_impl.h b/dtrace/include/dtrace/dtrace_impl.h index 73c27c18ed07..f7eda9188eb0 100644 --- a/dtrace/include/dtrace/dtrace_impl.h +++ b/dtrace/include/dtrace/dtrace_impl.h @@ -453,6 +453,7 @@ extern void dtrace_aggregate(dtrace_aggregation_t *, dtrace_buffer_t *, */ extern dtrace_hash_t *dtrace_hash_create(uintptr_t, uintptr_t, uintptr_t); +extern void dtrace_hash_destroy(dtrace_hash_t *); extern int dtrace_hash_add(dtrace_hash_t *, dtrace_probe_t *); extern dtrace_probe_t *dtrace_hash_lookup(dtrace_hash_t *, dtrace_probe_t *); extern int dtrace_hash_collisions(dtrace_hash_t *, dtrace_probe_t *); @@ -546,6 +547,8 @@ extern int dtrace_difo_validate_helper(dtrace_difo_t *); extern int dtrace_difo_cacheable(dtrace_difo_t *); extern void dtrace_difo_hold(dtrace_difo_t *); extern void dtrace_difo_init(dtrace_difo_t *, dtrace_vstate_t *); +extern dtrace_difo_t * dtrace_difo_duplicate(dtrace_difo_t *, + dtrace_vstate_t *); extern void dtrace_difo_release(dtrace_difo_t *, dtrace_vstate_t *); extern uint64_t dtrace_dif_emulate(dtrace_difo_t *, dtrace_mstate_t *, @@ -577,6 +580,8 @@ extern void dtrace_actdesc_release(dtrace_actdesc_t *, dtrace_vstate_t *); * DTrace Helper Functions */ extern void dtrace_helpers_destroy(struct task_struct *); +extern void dtrace_helpers_duplicate(struct task_struct *, + struct task_struct *); extern uint64_t dtrace_helper(int, dtrace_mstate_t *, dtrace_state_t *, uint64_t, uint64_t); -- 2.50.1