Commit 61424318 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Steven Rostedt

tracing/kprobes: Support module init function probing

To support probing module init functions, kprobe-tracer allows
user to define a probe on non-existed function when it is given
with a module name. This also enables user to set a probe on
a function on a specific module, even if a same name (but different)
function is locally defined in another module.

The module name must be in the front of function name and separated
by a ':'. e.g. btrfs:btrfs_init_sysfs
Signed-off-by: default avatarMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Link: http://lkml.kernel.org/r/20110627072656.6528.89970.stgit@fedora15Signed-off-by: default avatarSteven Rostedt <rostedt@goodmis.org>
parent bc81d48d
...@@ -22,14 +22,15 @@ current_tracer. Instead of that, add probe points via ...@@ -22,14 +22,15 @@ current_tracer. Instead of that, add probe points via
Synopsis of kprobe_events Synopsis of kprobe_events
------------------------- -------------------------
p[:[GRP/]EVENT] SYMBOL[+offs]|MEMADDR [FETCHARGS] : Set a probe p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe
r[:[GRP/]EVENT] SYMBOL[+0] [FETCHARGS] : Set a return probe r[:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe
-:[GRP/]EVENT : Clear a probe -:[GRP/]EVENT : Clear a probe
GRP : Group name. If omitted, use "kprobes" for it. GRP : Group name. If omitted, use "kprobes" for it.
EVENT : Event name. If omitted, the event name is generated EVENT : Event name. If omitted, the event name is generated
based on SYMBOL+offs or MEMADDR. based on SYM+offs or MEMADDR.
SYMBOL[+offs] : Symbol+offset where the probe is inserted. MOD : Module name which has given SYM.
SYM[+offs] : Symbol+offset where the probe is inserted.
MEMADDR : Address where the probe is inserted. MEMADDR : Address where the probe is inserted.
FETCHARGS : Arguments. Each probe can have up to 128 args. FETCHARGS : Arguments. Each probe can have up to 128 args.
......
...@@ -536,6 +536,7 @@ struct probe_arg { ...@@ -536,6 +536,7 @@ struct probe_arg {
/* Flags for trace_probe */ /* Flags for trace_probe */
#define TP_FLAG_TRACE 1 #define TP_FLAG_TRACE 1
#define TP_FLAG_PROFILE 2 #define TP_FLAG_PROFILE 2
#define TP_FLAG_REGISTERED 4
struct trace_probe { struct trace_probe {
struct list_head list; struct list_head list;
...@@ -565,6 +566,39 @@ static __kprobes const char *trace_probe_symbol(struct trace_probe *tp) ...@@ -565,6 +566,39 @@ static __kprobes const char *trace_probe_symbol(struct trace_probe *tp)
return tp->symbol ? tp->symbol : "unknown"; return tp->symbol ? tp->symbol : "unknown";
} }
static __kprobes unsigned long trace_probe_offset(struct trace_probe *tp)
{
return tp->rp.kp.offset;
}
static __kprobes bool trace_probe_is_enabled(struct trace_probe *tp)
{
return !!(tp->flags & (TP_FLAG_TRACE | TP_FLAG_PROFILE));
}
static __kprobes bool trace_probe_is_registered(struct trace_probe *tp)
{
return !!(tp->flags & TP_FLAG_REGISTERED);
}
static __kprobes bool trace_probe_has_gone(struct trace_probe *tp)
{
return !!(kprobe_gone(&tp->rp.kp));
}
static __kprobes bool trace_probe_within_module(struct trace_probe *tp,
struct module *mod)
{
int len = strlen(mod->name);
const char *name = trace_probe_symbol(tp);
return strncmp(mod->name, name, len) == 0 && name[len] == ':';
}
static __kprobes bool trace_probe_is_on_module(struct trace_probe *tp)
{
return !!strchr(trace_probe_symbol(tp), ':');
}
static int register_probe_event(struct trace_probe *tp); static int register_probe_event(struct trace_probe *tp);
static void unregister_probe_event(struct trace_probe *tp); static void unregister_probe_event(struct trace_probe *tp);
...@@ -689,7 +723,8 @@ static int enable_trace_probe(struct trace_probe *tp, int flag) ...@@ -689,7 +723,8 @@ static int enable_trace_probe(struct trace_probe *tp, int flag)
int ret = 0; int ret = 0;
tp->flags |= flag; tp->flags |= flag;
if (tp->flags & (TP_FLAG_TRACE | TP_FLAG_PROFILE)) { if (trace_probe_is_enabled(tp) && trace_probe_is_registered(tp) &&
!trace_probe_has_gone(tp)) {
if (trace_probe_is_return(tp)) if (trace_probe_is_return(tp))
ret = enable_kretprobe(&tp->rp); ret = enable_kretprobe(&tp->rp);
else else
...@@ -703,7 +738,7 @@ static int enable_trace_probe(struct trace_probe *tp, int flag) ...@@ -703,7 +738,7 @@ static int enable_trace_probe(struct trace_probe *tp, int flag)
static void disable_trace_probe(struct trace_probe *tp, int flag) static void disable_trace_probe(struct trace_probe *tp, int flag)
{ {
tp->flags &= ~flag; tp->flags &= ~flag;
if (!(tp->flags & (TP_FLAG_TRACE | TP_FLAG_PROFILE))) { if (!trace_probe_is_enabled(tp) && trace_probe_is_registered(tp)) {
if (trace_probe_is_return(tp)) if (trace_probe_is_return(tp))
disable_kretprobe(&tp->rp); disable_kretprobe(&tp->rp);
else else
...@@ -711,13 +746,64 @@ static void disable_trace_probe(struct trace_probe *tp, int flag) ...@@ -711,13 +746,64 @@ static void disable_trace_probe(struct trace_probe *tp, int flag)
} }
} }
/* Unregister a trace_probe and probe_event: call with locking probe_lock */ /* Internal register function - just handle k*probes and flags */
static void unregister_trace_probe(struct trace_probe *tp) static int __register_trace_probe(struct trace_probe *tp)
{ {
int ret;
if (trace_probe_is_registered(tp))
return -EINVAL;
/* Set/clear disabled flag according to tp->flag */
if (trace_probe_is_enabled(tp))
tp->rp.kp.flags &= ~KPROBE_FLAG_DISABLED;
else
tp->rp.kp.flags |= KPROBE_FLAG_DISABLED;
if (trace_probe_is_return(tp)) if (trace_probe_is_return(tp))
unregister_kretprobe(&tp->rp); ret = register_kretprobe(&tp->rp);
else else
unregister_kprobe(&tp->rp.kp); ret = register_kprobe(&tp->rp.kp);
if (ret == 0)
tp->flags |= TP_FLAG_REGISTERED;
else {
pr_warning("Could not insert probe at %s+%lu: %d\n",
trace_probe_symbol(tp), trace_probe_offset(tp), ret);
if (ret == -ENOENT && trace_probe_is_on_module(tp)) {
pr_warning("This probe might be able to register after"
"target module is loaded. Continue.\n");
ret = 0;
} else if (ret == -EILSEQ) {
pr_warning("Probing address(0x%p) is not an "
"instruction boundary.\n",
tp->rp.kp.addr);
ret = -EINVAL;
}
}
return ret;
}
/* Internal unregister function - just handle k*probes and flags */
static void __unregister_trace_probe(struct trace_probe *tp)
{
if (trace_probe_is_registered(tp)) {
if (trace_probe_is_return(tp))
unregister_kretprobe(&tp->rp);
else
unregister_kprobe(&tp->rp.kp);
tp->flags &= ~TP_FLAG_REGISTERED;
/* Cleanup kprobe for reuse */
if (tp->rp.kp.symbol_name)
tp->rp.kp.addr = NULL;
}
}
/* Unregister a trace_probe and probe_event: call with locking probe_lock */
static void unregister_trace_probe(struct trace_probe *tp)
{
__unregister_trace_probe(tp);
list_del(&tp->list); list_del(&tp->list);
unregister_probe_event(tp); unregister_probe_event(tp);
} }
...@@ -730,41 +816,65 @@ static int register_trace_probe(struct trace_probe *tp) ...@@ -730,41 +816,65 @@ static int register_trace_probe(struct trace_probe *tp)
mutex_lock(&probe_lock); mutex_lock(&probe_lock);
/* register as an event */ /* Delete old (same name) event if exist */
old_tp = find_trace_probe(tp->call.name, tp->call.class->system); old_tp = find_trace_probe(tp->call.name, tp->call.class->system);
if (old_tp) { if (old_tp) {
/* delete old event */
unregister_trace_probe(old_tp); unregister_trace_probe(old_tp);
free_trace_probe(old_tp); free_trace_probe(old_tp);
} }
/* Register new event */
ret = register_probe_event(tp); ret = register_probe_event(tp);
if (ret) { if (ret) {
pr_warning("Failed to register probe event(%d)\n", ret); pr_warning("Failed to register probe event(%d)\n", ret);
goto end; goto end;
} }
tp->rp.kp.flags |= KPROBE_FLAG_DISABLED; /* Register k*probe */
if (trace_probe_is_return(tp)) ret = __register_trace_probe(tp);
ret = register_kretprobe(&tp->rp); if (ret < 0)
else
ret = register_kprobe(&tp->rp.kp);
if (ret) {
pr_warning("Could not insert probe(%d)\n", ret);
if (ret == -EILSEQ) {
pr_warning("Probing address(0x%p) is not an "
"instruction boundary.\n",
tp->rp.kp.addr);
ret = -EINVAL;
}
unregister_probe_event(tp); unregister_probe_event(tp);
} else else
list_add_tail(&tp->list, &probe_list); list_add_tail(&tp->list, &probe_list);
end: end:
mutex_unlock(&probe_lock); mutex_unlock(&probe_lock);
return ret; return ret;
} }
/* Module notifier call back, checking event on the module */
static int trace_probe_module_callback(struct notifier_block *nb,
unsigned long val, void *data)
{
struct module *mod = data;
struct trace_probe *tp;
int ret;
if (val != MODULE_STATE_COMING)
return NOTIFY_DONE;
/* Update probes on coming module */
mutex_lock(&probe_lock);
list_for_each_entry(tp, &probe_list, list) {
if (trace_probe_within_module(tp, mod)) {
__unregister_trace_probe(tp);
ret = __register_trace_probe(tp);
if (ret)
pr_warning("Failed to re-register probe %s on"
"%s: %d\n",
tp->call.name, mod->name, ret);
}
}
mutex_unlock(&probe_lock);
return NOTIFY_DONE;
}
static struct notifier_block trace_probe_module_nb = {
.notifier_call = trace_probe_module_callback,
.priority = 1 /* Invoked after kprobe module callback */
};
/* Split symbol and offset. */ /* Split symbol and offset. */
static int split_symbol_offset(char *symbol, unsigned long *offset) static int split_symbol_offset(char *symbol, unsigned long *offset)
{ {
...@@ -990,8 +1100,8 @@ static int create_trace_probe(int argc, char **argv) ...@@ -990,8 +1100,8 @@ static int create_trace_probe(int argc, char **argv)
{ {
/* /*
* Argument syntax: * Argument syntax:
* - Add kprobe: p[:[GRP/]EVENT] KSYM[+OFFS]|KADDR [FETCHARGS] * - Add kprobe: p[:[GRP/]EVENT] [MOD:]KSYM[+OFFS]|KADDR [FETCHARGS]
* - Add kretprobe: r[:[GRP/]EVENT] KSYM[+0] [FETCHARGS] * - Add kretprobe: r[:[GRP/]EVENT] [MOD:]KSYM[+0] [FETCHARGS]
* Fetch args: * Fetch args:
* $retval : fetch return value * $retval : fetch return value
* $stack : fetch stack address * $stack : fetch stack address
...@@ -1186,7 +1296,6 @@ static void release_all_trace_probes(void) ...@@ -1186,7 +1296,6 @@ static void release_all_trace_probes(void)
mutex_unlock(&probe_lock); mutex_unlock(&probe_lock);
} }
/* Probes listing interfaces */ /* Probes listing interfaces */
static void *probes_seq_start(struct seq_file *m, loff_t *pos) static void *probes_seq_start(struct seq_file *m, loff_t *pos)
{ {
...@@ -1827,6 +1936,9 @@ static __init int init_kprobe_trace(void) ...@@ -1827,6 +1936,9 @@ static __init int init_kprobe_trace(void)
struct dentry *d_tracer; struct dentry *d_tracer;
struct dentry *entry; struct dentry *entry;
if (register_module_notifier(&trace_probe_module_nb))
return -EINVAL;
d_tracer = tracing_init_dentry(); d_tracer = tracing_init_dentry();
if (!d_tracer) if (!d_tracer)
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment