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: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
This commit is contained in:
parent
67e9a9ee47
commit
57a7e6de9e
@ -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)
|
|||||||
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)
|
|||||||
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 @@ end:
|
|||||||
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
|
|||||||
|
|
||||||
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,
|
|||||||
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[])
|
|||||||
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-^
|
|||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user