]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
dtrace: Ensure that USDT probes are carried over correctly across fork().
authorKris Van Hees <kris.van.hees@oracle.com>
Thu, 29 Aug 2013 22:07:21 +0000 (18:07 -0400)
committerKris Van Hees <kris.van.hees@oracle.com>
Thu, 5 Sep 2013 20:57:17 +0000 (16:57 -0400)
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 <kris.van.hees@oracle.com>
dtrace/dtrace_dev.c
dtrace/dtrace_dif.c
dtrace/dtrace_dof.c
dtrace/dtrace_hash.c
dtrace/dtrace_isa.c
dtrace/fasttrap_dev.c
dtrace/include/dtrace/dtrace_impl.h

index 79507d3929df48e14116c96389c86e4aa1b75adc..8d267856d6e019af285b8bb3f95eea256b6a731d 100644 (file)
@@ -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);
 }
index cd30f6fed80b2db0df8158848e28f3449d7e7aef..e2fa3365d481b54856d768b6f5269e5bddc33338 100644 (file)
@@ -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.
index ee70ded7358df3897ac64ae53a41bf71466ac1ef..179c65f28d52a974ae45c86e17f912b21222c3bb 100644 (file)
@@ -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;
index 1d6a278725a75abadb920819f466d10ff4409a20..f34144c6869c3708fef99bfe866d17ec6a60d32f 100644 (file)
@@ -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;
index 189d170581697adb3c57336b2e6d1e3bf21f7c3e..05761668508c9920c61ed5db8fb694925e0bc57a 100644 (file)
@@ -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);
 
index 833685f6f90813790ddba909042179ab1f5f55bd..08c639ae5e38752c15b4c1f8d95ec0d3b4a86c89 100644 (file)
@@ -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;
 
index 73c27c18ed0769311cf505cbd579d56a02fdd987..f7eda9188eb03d5d46e5aa934ebcd66b4bf1f515 100644 (file)
@@ -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);