From 18bf76a8ef4d148c19732d552f253864f4235fc5 Mon Sep 17 00:00:00 2001 From: Kris Van Hees Date: Wed, 23 Jan 2013 04:53:14 -0500 Subject: [PATCH] dtrace: USDT implementation (Phase 1) This rather large patch provides the implementation for USDT support at the DTrace kernel core level, and the fasttrap provider level. It ensures that executables can register their embedded providers (with USDT probes), that the probes are visible to the dtrace userspace utility, and that probes are properly removed upon executable completion, execve() invocation, or any unexpected executable termination. The following parts are provided by this patch: - meta-provider support (dtrace_ptofapi) - helper ioctl interface (dtrace_dev) - DIF validation for helper objects (dtrace_dif) - DOF processing for helpers for provider and probe definitions (dtrace_dof) - fasttrap meta-provider for USDT only (fasttrap*) The dtrace_helper.c file was removed because this code belongs in dtrace_dof.c instead. Minimal changes were made to the core kernel in exec.c, sched.h, exit.c, and fork.c to add support for process-specific helpers (and those encapsulate providers and probes). Signed-off-by: Kris Van Hees --- fs/exec.c | 3 ++ include/linux/dtrace_ioctl.h | 2 +- include/linux/dtrace_os.h | 10 +++- include/linux/fasttrap.h | 50 +++++++++++++++++++ include/linux/sched.h | 1 + kernel/dtrace/dtrace_os.c | 96 ++++++++++++++++++++++++++++++++++++ kernel/exit.c | 5 ++ kernel/fork.c | 2 + 8 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 include/linux/fasttrap.h diff --git a/fs/exec.c b/fs/exec.c index 36f4f76763c14..98144eb184fdf 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -1589,6 +1590,8 @@ static int do_execveat_common(int fd, struct filename *filename, /* execve succeeded */ #ifdef CONFIG_DTRACE + dtrace_task_cleanup(current); /* get rid of probes from old ... */ + dtrace_task_init(current); /* ... be ready for probes from new */ current->dtrace_psinfo = dtrace_psinfo_alloc(current); #endif current->fs->in_exec = 0; diff --git a/include/linux/dtrace_ioctl.h b/include/linux/dtrace_ioctl.h index 3e343ff60118e..7bec9b6fe7a2d 100644 --- a/include/linux/dtrace_ioctl.h +++ b/include/linux/dtrace_ioctl.h @@ -25,7 +25,7 @@ #define DTRACEIOC_REPLICATE _IOR(DTRACEIOC, 18, void *) #define DTRACEHIOC 0xd8 -#define DTRACEHIOC_ADD _IOW(DTRACEHIOC, 1, int) +#define DTRACEHIOC_ADD _IOW(DTRACEHIOC, 1, dof_hdr_t) #define DTRACEHIOC_REMOVE _IOW(DTRACEHIOC, 2, int) #define DTRACEHIOC_ADDDOF _IOW(DTRACEHIOC, 3, dof_helper_t) diff --git a/include/linux/dtrace_os.h b/include/linux/dtrace_os.h index 4a828489770ba..da541b304b268 100644 --- a/include/linux/dtrace_os.h +++ b/include/linux/dtrace_os.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011, 2012, 2013 Oracle Corporation */ +/* Copyright (C) 2011, 2012, 2013 Oracle, Inc. */ #ifndef _DTRACE_OS_H_ #define _DTRACE_OS_H_ @@ -72,4 +72,12 @@ typedef struct stacktrace_state { extern void dtrace_stacktrace(stacktrace_state_t *); +extern struct task_struct *register_pid_provider(pid_t); +extern void unregister_pid_provider(pid_t); +extern void dtrace_task_init(struct task_struct *tsk); +extern void dtrace_task_cleanup(struct task_struct *tsk); + +extern void (*dtrace_helpers_cleanup)(struct task_struct *); +extern void (*dtrace_fasttrap_probes_cleanup)(struct task_struct *); + #endif /* _DTRACE_OS_H_ */ diff --git a/include/linux/fasttrap.h b/include/linux/fasttrap.h new file mode 100644 index 0000000000000..75b2f52eb5226 --- /dev/null +++ b/include/linux/fasttrap.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2011, 2012, 2013 Oracle, Inc. */ + +#ifndef _FASTTRAP_H_ +#define _FASTTRAP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(CONFIG_DTRACE) || defined(CONFIG_DTRACE_MODULE) + +#define FASTTRAPIOC (('m' << 24) | ('r' << 16) | ('f' << 8)) +#define FASTTRAPIOC_MAKEPROBE (FASTTRAPIOC | 1) +#define FASTTRAPIOC_GETINSTR (FASTTRAPIOC | 2) + +typedef enum fasttrap_probe_type { + DTFTP_NONE = 0, + DTFTP_ENTRY, + DTFTP_RETURN, + DTFTP_OFFSETS, + DTFTP_POST_OFFSETS, + DTFTP_IS_ENABLED +} fasttrap_probe_type_t; + +typedef struct fasttrap_probe_spec { + pid_t ftps_pid; + fasttrap_probe_type_t ftps_type; + char ftps_func[DTRACE_FUNCNAMELEN]; + char ftps_mod[DTRACE_MODNAMELEN]; + uint64_t ftps_pc; + uint64_t ftps_size; + uint64_t ftps_noffs; + uint64_t ftps_offs[1]; +} fasttrap_probe_spec_t; + +typedef uint8_t fasttrap_instr_t; + +typedef struct fasttrap_instr_query { + uint64_t ftiq_pc; + pid_t ftiq_pid; + fasttrap_instr_t ftiq_instr; +} fasttrap_instr_query_t; + +#endif /* CONFIG_DTRACE */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FASTTRAP_H_ */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 2b89630bbfc49..9ca7eae1a8553 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1724,6 +1724,7 @@ struct task_struct { dtrace_psinfo_t *dtrace_psinfo; void *dtrace_helpers; + uint32_t dtrace_probes; #endif #ifdef CONFIG_UPROBES struct uprobe_task *utask; diff --git a/kernel/dtrace/dtrace_os.c b/kernel/dtrace/dtrace_os.c index f55bfd0a024e3..ce13292d57fdc 100644 --- a/kernel/dtrace/dtrace_os.c +++ b/kernel/dtrace/dtrace_os.c @@ -910,3 +910,99 @@ long dtrace_rt_sigreturn(struct pt_regs *regs) return rc; } + +/*---------------------------------------------------------------------------*\ +(* USER SPACE TRACING (FASTTRAP) SUPPORT *) +\*---------------------------------------------------------------------------*/ +struct task_struct *register_pid_provider(pid_t pid) +{ + struct task_struct *p; + + /* + * Make sure the process exists, (FIXME: isn't a child created as the + * result of a vfork(2)), and isn't a zombie (but may be in fork). + */ + rcu_read_lock(); + read_lock(&tasklist_lock); + if ((p = find_task_by_vpid(pid)) == NULL) { + read_unlock(&tasklist_lock); + rcu_read_unlock(); + return NULL; + } + + get_task_struct(p); + read_unlock(&tasklist_lock); + rcu_read_unlock(); + + if (p->state & TASK_DEAD || + p->exit_state & (EXIT_ZOMBIE | EXIT_DEAD)) { + put_task_struct(p); + return NULL; + } + + /* + * Increment dtrace_probes so that the process knows to inform us + * when it exits or execs. fasttrap_provider_free() decrements this + * when we're done with this provider. + */ + p->dtrace_probes++; + put_task_struct(p); + + return p; +} +EXPORT_SYMBOL(register_pid_provider); + +void unregister_pid_provider(pid_t pid) +{ + struct task_struct *p; + + /* + * Decrement dtrace_probes on the process whose provider we're + * freeing. We don't have to worry about clobbering somone else's + * modifications to it because we have locked the bucket that + * corresponds to this process's hash chain in the provider hash + * table. Don't sweat it if we can't find the process. + */ + rcu_read_lock(); + read_lock(&tasklist_lock); + if ((p = find_task_by_vpid(pid)) == NULL) { + read_unlock(&tasklist_lock); + rcu_read_unlock(); + return; + } + + get_task_struct(p); + read_unlock(&tasklist_lock); + rcu_read_unlock(); + + p->dtrace_probes--; + put_task_struct(p); +} +EXPORT_SYMBOL(unregister_pid_provider); + +void (*dtrace_helpers_cleanup)(struct task_struct *); +EXPORT_SYMBOL(dtrace_helpers_cleanup); +void (*dtrace_fasttrap_probes_cleanup)(struct task_struct *); +EXPORT_SYMBOL(dtrace_fasttrap_probes_cleanup); + +void dtrace_task_init(struct task_struct *tsk) +{ + tsk->dtrace_helpers = NULL; + tsk->dtrace_probes = 0; +} + +void dtrace_task_cleanup(struct task_struct *tsk) +{ + if (likely(dtrace_helpers_cleanup == NULL)) + return; + + if (tsk->dtrace_helpers != NULL) + (*dtrace_helpers_cleanup)(tsk); + + if (tsk->dtrace_probes) { + if (dtrace_fasttrap_probes_cleanup == NULL) + pr_warn("Fasttrap probes, yet no cleanup routine\n"); + else + (*dtrace_fasttrap_probes_cleanup)(tsk); + } +} diff --git a/kernel/exit.c b/kernel/exit.c index 7125b5184bcc6..7ff3c4aed9c6f 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -734,6 +735,10 @@ void do_exit(long code) DTRACE_PROC(lwp__exit); DTRACE_PROC1(exit, int, code & 0x80 ? 3 : code & 0x7f ? 2 : 1); +#ifdef CONFIG_DTRACE + dtrace_task_cleanup(tsk); +#endif + exit_mm(tsk); if (group_dead) diff --git a/kernel/fork.c b/kernel/fork.c index dfd447cf0def4..328f8828b7837 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -76,6 +76,7 @@ #include #include #include +#include #include #include @@ -384,6 +385,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig) #ifdef CONFIG_DTRACE tsk->dtrace_psinfo = NULL; + dtrace_task_init(tsk); #endif return tsk; -- 2.50.1