tracing/fprobe: Support raw tracepoints on future loaded modules

Support raw tracepoint events on future loaded (unloaded) modules.
This allows user to create raw tracepoint events which can be used from
module's __init functions.

Note: since the kernel does not have any information about the tracepoints
in the unloaded modules, fprobe events can not check whether the tracepoint
exists nor extend the BTF based arguments.

Link: https://lore.kernel.org/all/172397780593.286558.18360375226968537828.stgit@devnote2/Suggested-by: default avatarMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: default avatarMasami Hiramatsu (Google) <mhiramat@kernel.org>
parent 67e9a9ee
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#define FPROBE_EVENT_SYSTEM "fprobes" #define FPROBE_EVENT_SYSTEM "fprobes"
#define TRACEPOINT_EVENT_SYSTEM "tracepoints" #define TRACEPOINT_EVENT_SYSTEM "tracepoints"
#define RETHOOK_MAXACTIVE_MAX 4096 #define RETHOOK_MAXACTIVE_MAX 4096
#define TRACEPOINT_STUB ERR_PTR(-ENOENT)
static int trace_fprobe_create(const char *raw_command); static int trace_fprobe_create(const char *raw_command);
static int trace_fprobe_show(struct seq_file *m, struct dyn_event *ev); static int trace_fprobe_show(struct seq_file *m, struct dyn_event *ev);
...@@ -674,6 +675,24 @@ static int unregister_fprobe_event(struct trace_fprobe *tf) ...@@ -674,6 +675,24 @@ static int unregister_fprobe_event(struct trace_fprobe *tf)
return trace_probe_unregister_event_call(&tf->tp); return trace_probe_unregister_event_call(&tf->tp);
} }
static int __regsiter_tracepoint_fprobe(struct trace_fprobe *tf)
{
struct tracepoint *tpoint = tf->tpoint;
unsigned long ip = (unsigned long)tpoint->probestub;
int ret;
/*
* Here, we do 2 steps to enable fprobe on a tracepoint.
* At first, put __probestub_##TP function on the tracepoint
* and put a fprobe on the stub function.
*/
ret = tracepoint_probe_register_prio_may_exist(tpoint,
tpoint->probestub, NULL, 0);
if (ret < 0)
return ret;
return register_fprobe_ips(&tf->fp, &ip, 1);
}
/* Internal register function - just handle fprobe and flags */ /* Internal register function - just handle fprobe and flags */
static int __register_trace_fprobe(struct trace_fprobe *tf) static int __register_trace_fprobe(struct trace_fprobe *tf)
{ {
...@@ -700,18 +719,12 @@ static int __register_trace_fprobe(struct trace_fprobe *tf) ...@@ -700,18 +719,12 @@ static int __register_trace_fprobe(struct trace_fprobe *tf)
tf->fp.flags |= FPROBE_FL_DISABLED; tf->fp.flags |= FPROBE_FL_DISABLED;
if (trace_fprobe_is_tracepoint(tf)) { if (trace_fprobe_is_tracepoint(tf)) {
struct tracepoint *tpoint = tf->tpoint;
unsigned long ip = (unsigned long)tpoint->probestub; /* This tracepoint is not loaded yet */
/* if (tf->tpoint == TRACEPOINT_STUB)
* Here, we do 2 steps to enable fprobe on a tracepoint. return 0;
* At first, put __probestub_##TP function on the tracepoint
* and put a fprobe on the stub function. return __regsiter_tracepoint_fprobe(tf);
*/
ret = tracepoint_probe_register_prio_may_exist(tpoint,
tpoint->probestub, NULL, 0);
if (ret < 0)
return ret;
return register_fprobe_ips(&tf->fp, &ip, 1);
} }
/* TODO: handle filter, nofilter or symbol list */ /* TODO: handle filter, nofilter or symbol list */
...@@ -864,36 +877,6 @@ static int register_trace_fprobe(struct trace_fprobe *tf) ...@@ -864,36 +877,6 @@ static int register_trace_fprobe(struct trace_fprobe *tf)
return ret; return ret;
} }
#ifdef CONFIG_MODULES
static int __tracepoint_probe_module_cb(struct notifier_block *self,
unsigned long val, void *data)
{
struct tp_module *tp_mod = data;
struct trace_fprobe *tf;
struct dyn_event *pos;
if (val != MODULE_STATE_GOING)
return NOTIFY_DONE;
mutex_lock(&event_mutex);
for_each_trace_fprobe(tf, pos) {
if (tp_mod->mod == tf->mod) {
tracepoint_probe_unregister(tf->tpoint,
tf->tpoint->probestub, NULL);
tf->tpoint = NULL;
tf->mod = NULL;
}
}
mutex_unlock(&event_mutex);
return NOTIFY_DONE;
}
static struct notifier_block tracepoint_module_nb = {
.notifier_call = __tracepoint_probe_module_cb,
};
#endif /* CONFIG_MODULES */
struct __find_tracepoint_cb_data { struct __find_tracepoint_cb_data {
const char *tp_name; const char *tp_name;
struct tracepoint *tpoint; struct tracepoint *tpoint;
...@@ -906,12 +889,14 @@ static void __find_tracepoint_module_cb(struct tracepoint *tp, struct module *mo ...@@ -906,12 +889,14 @@ static void __find_tracepoint_module_cb(struct tracepoint *tp, struct module *mo
if (!data->tpoint && !strcmp(data->tp_name, tp->name)) { if (!data->tpoint && !strcmp(data->tp_name, tp->name)) {
data->tpoint = tp; data->tpoint = tp;
if (!data->mod) {
data->mod = mod; data->mod = mod;
if (!try_module_get(data->mod)) { if (!try_module_get(data->mod)) {
data->tpoint = NULL; data->tpoint = NULL;
data->mod = NULL; data->mod = NULL;
} }
} }
}
} }
static void __find_tracepoint_cb(struct tracepoint *tp, void *priv) static void __find_tracepoint_cb(struct tracepoint *tp, void *priv)
...@@ -947,6 +932,67 @@ static struct tracepoint *find_tracepoint(const char *tp_name, ...@@ -947,6 +932,67 @@ static struct tracepoint *find_tracepoint(const char *tp_name,
return data.tpoint; return data.tpoint;
} }
#ifdef CONFIG_MODULES
static void reenable_trace_fprobe(struct trace_fprobe *tf)
{
struct trace_probe *tp = &tf->tp;
list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) {
__enable_trace_fprobe(tf);
}
}
static struct tracepoint *find_tracepoint_in_module(struct module *mod,
const char *tp_name)
{
struct __find_tracepoint_cb_data data = {
.tp_name = tp_name,
.mod = mod,
};
for_each_tracepoint_in_module(mod, __find_tracepoint_module_cb, &data);
return data.tpoint;
}
static int __tracepoint_probe_module_cb(struct notifier_block *self,
unsigned long val, void *data)
{
struct tp_module *tp_mod = data;
struct tracepoint *tpoint;
struct trace_fprobe *tf;
struct dyn_event *pos;
if (val != MODULE_STATE_GOING && val != MODULE_STATE_COMING)
return NOTIFY_DONE;
mutex_lock(&event_mutex);
for_each_trace_fprobe(tf, pos) {
if (val == MODULE_STATE_COMING && tf->tpoint == TRACEPOINT_STUB) {
tpoint = find_tracepoint_in_module(tp_mod->mod, tf->symbol);
if (tpoint) {
tf->tpoint = tpoint;
tf->mod = tp_mod->mod;
if (!WARN_ON_ONCE(__regsiter_tracepoint_fprobe(tf)) &&
trace_probe_is_enabled(&tf->tp))
reenable_trace_fprobe(tf);
}
} else if (val == MODULE_STATE_GOING && tp_mod->mod == tf->mod) {
tracepoint_probe_unregister(tf->tpoint,
tf->tpoint->probestub, NULL);
tf->tpoint = NULL;
tf->mod = NULL;
}
}
mutex_unlock(&event_mutex);
return NOTIFY_DONE;
}
static struct notifier_block tracepoint_module_nb = {
.notifier_call = __tracepoint_probe_module_cb,
};
#endif /* CONFIG_MODULES */
static int parse_symbol_and_return(int argc, const char *argv[], static int parse_symbol_and_return(int argc, const char *argv[],
char **symbol, bool *is_return, char **symbol, bool *is_return,
bool is_tracepoint) bool is_tracepoint)
...@@ -1113,14 +1159,19 @@ static int __trace_fprobe_create(int argc, const char *argv[]) ...@@ -1113,14 +1159,19 @@ static int __trace_fprobe_create(int argc, const char *argv[])
if (is_tracepoint) { if (is_tracepoint) {
ctx.flags |= TPARG_FL_TPOINT; ctx.flags |= TPARG_FL_TPOINT;
tpoint = find_tracepoint(symbol, &tp_mod); tpoint = find_tracepoint(symbol, &tp_mod);
if (!tpoint) { if (tpoint) {
ctx.funcname = kallsyms_lookup(
(unsigned long)tpoint->probestub,
NULL, NULL, NULL, sbuf);
} else if (IS_ENABLED(CONFIG_MODULES)) {
/* This *may* be loaded afterwards */
tpoint = TRACEPOINT_STUB;
ctx.funcname = symbol;
} else {
trace_probe_log_set_index(1); trace_probe_log_set_index(1);
trace_probe_log_err(0, NO_TRACEPOINT); trace_probe_log_err(0, NO_TRACEPOINT);
goto parse_error; goto parse_error;
} }
ctx.funcname = kallsyms_lookup(
(unsigned long)tpoint->probestub,
NULL, NULL, NULL, sbuf);
} else } else
ctx.funcname = symbol; ctx.funcname = symbol;
......
...@@ -9,7 +9,6 @@ check_error() { # command-with-error-pos-by-^ ...@@ -9,7 +9,6 @@ check_error() { # command-with-error-pos-by-^
check_error 't^100 kfree' # BAD_MAXACT_TYPE check_error 't^100 kfree' # BAD_MAXACT_TYPE
check_error 't ^non_exist_tracepoint' # NO_TRACEPOINT
check_error 't:^/bar kfree' # NO_GROUP_NAME check_error 't:^/bar kfree' # NO_GROUP_NAME
check_error 't:^12345678901234567890123456789012345678901234567890123456789012345/bar kfree' # GROUP_TOO_LONG check_error 't:^12345678901234567890123456789012345678901234567890123456789012345/bar kfree' # GROUP_TOO_LONG
......
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