mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-23 16:53:58 -05:00
tracing/eprobe: Add eprobe filter support
Add the filter option to the event probe. This is useful if user wants to derive a new event based on the condition of the original event. E.g. echo 'e:egroup/stat_runtime_4core sched/sched_stat_runtime \ runtime=$runtime:u32 if cpu < 4' >> ../dynamic_events Then it can filter the events only on first 4 cores. Note that the fields used for 'if' must be the fields in the original events, not eprobe events. Link: https://lkml.kernel.org/r/165932114513.2850673.2592206685744598080.stgit@devnote2 Cc: Tzvetomir Stoyanov <tz.stoyanov@gmail.com> Cc: Ingo Molnar <mingo@kernel.org> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
This commit is contained in:
parent
f76349cf41
commit
752be5c5c9
2 changed files with 98 additions and 9 deletions
|
@ -26,6 +26,9 @@ struct trace_eprobe {
|
||||||
/* tracepoint event */
|
/* tracepoint event */
|
||||||
const char *event_name;
|
const char *event_name;
|
||||||
|
|
||||||
|
/* filter string for the tracepoint */
|
||||||
|
char *filter_str;
|
||||||
|
|
||||||
struct trace_event_call *event;
|
struct trace_event_call *event;
|
||||||
|
|
||||||
struct dyn_event devent;
|
struct dyn_event devent;
|
||||||
|
@ -664,14 +667,15 @@ static struct event_trigger_data *
|
||||||
new_eprobe_trigger(struct trace_eprobe *ep, struct trace_event_file *file)
|
new_eprobe_trigger(struct trace_eprobe *ep, struct trace_event_file *file)
|
||||||
{
|
{
|
||||||
struct event_trigger_data *trigger;
|
struct event_trigger_data *trigger;
|
||||||
|
struct event_filter *filter = NULL;
|
||||||
struct eprobe_data *edata;
|
struct eprobe_data *edata;
|
||||||
|
int ret;
|
||||||
|
|
||||||
edata = kzalloc(sizeof(*edata), GFP_KERNEL);
|
edata = kzalloc(sizeof(*edata), GFP_KERNEL);
|
||||||
trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
|
trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
|
||||||
if (!trigger || !edata) {
|
if (!trigger || !edata) {
|
||||||
kfree(edata);
|
ret = -ENOMEM;
|
||||||
kfree(trigger);
|
goto error;
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trigger->flags = EVENT_TRIGGER_FL_PROBE;
|
trigger->flags = EVENT_TRIGGER_FL_PROBE;
|
||||||
|
@ -686,13 +690,25 @@ new_eprobe_trigger(struct trace_eprobe *ep, struct trace_event_file *file)
|
||||||
trigger->cmd_ops = &event_trigger_cmd;
|
trigger->cmd_ops = &event_trigger_cmd;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&trigger->list);
|
INIT_LIST_HEAD(&trigger->list);
|
||||||
RCU_INIT_POINTER(trigger->filter, NULL);
|
|
||||||
|
if (ep->filter_str) {
|
||||||
|
ret = create_event_filter(file->tr, file->event_call,
|
||||||
|
ep->filter_str, false, &filter);
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
RCU_INIT_POINTER(trigger->filter, filter);
|
||||||
|
|
||||||
edata->file = file;
|
edata->file = file;
|
||||||
edata->ep = ep;
|
edata->ep = ep;
|
||||||
trigger->private_data = edata;
|
trigger->private_data = edata;
|
||||||
|
|
||||||
return trigger;
|
return trigger;
|
||||||
|
error:
|
||||||
|
free_event_filter(filter);
|
||||||
|
kfree(edata);
|
||||||
|
kfree(trigger);
|
||||||
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int enable_eprobe(struct trace_eprobe *ep,
|
static int enable_eprobe(struct trace_eprobe *ep,
|
||||||
|
@ -726,6 +742,7 @@ static int disable_eprobe(struct trace_eprobe *ep,
|
||||||
{
|
{
|
||||||
struct event_trigger_data *trigger = NULL, *iter;
|
struct event_trigger_data *trigger = NULL, *iter;
|
||||||
struct trace_event_file *file;
|
struct trace_event_file *file;
|
||||||
|
struct event_filter *filter;
|
||||||
struct eprobe_data *edata;
|
struct eprobe_data *edata;
|
||||||
|
|
||||||
file = find_event_file(tr, ep->event_system, ep->event_name);
|
file = find_event_file(tr, ep->event_system, ep->event_name);
|
||||||
|
@ -752,6 +769,10 @@ static int disable_eprobe(struct trace_eprobe *ep,
|
||||||
/* Make sure nothing is using the edata or trigger */
|
/* Make sure nothing is using the edata or trigger */
|
||||||
tracepoint_synchronize_unregister();
|
tracepoint_synchronize_unregister();
|
||||||
|
|
||||||
|
filter = rcu_access_pointer(trigger->filter);
|
||||||
|
|
||||||
|
if (filter)
|
||||||
|
free_event_filter(filter);
|
||||||
kfree(edata);
|
kfree(edata);
|
||||||
kfree(trigger);
|
kfree(trigger);
|
||||||
|
|
||||||
|
@ -927,12 +948,62 @@ static int trace_eprobe_tp_update_arg(struct trace_eprobe *ep, const char *argv[
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int trace_eprobe_parse_filter(struct trace_eprobe *ep, int argc, const char *argv[])
|
||||||
|
{
|
||||||
|
struct event_filter *dummy;
|
||||||
|
int i, ret, len = 0;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
if (argc == 0) {
|
||||||
|
trace_probe_log_err(0, NO_EP_FILTER);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Recover the filter string */
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
len += strlen(argv[i]) + 1;
|
||||||
|
|
||||||
|
ep->filter_str = kzalloc(len, GFP_KERNEL);
|
||||||
|
if (!ep->filter_str)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
p = ep->filter_str;
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
ret = snprintf(p, len, "%s ", argv[i]);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
if (ret > len) {
|
||||||
|
ret = -E2BIG;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
p += ret;
|
||||||
|
len -= ret;
|
||||||
|
}
|
||||||
|
p[-1] = '\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure the filter string can be parsed correctly. Note, this
|
||||||
|
* filter string is for the original event, not for the eprobe.
|
||||||
|
*/
|
||||||
|
ret = create_event_filter(top_trace_array(), ep->event, ep->filter_str,
|
||||||
|
true, &dummy);
|
||||||
|
free_event_filter(dummy);
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
kfree(ep->filter_str);
|
||||||
|
ep->filter_str = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int __trace_eprobe_create(int argc, const char *argv[])
|
static int __trace_eprobe_create(int argc, const char *argv[])
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Argument syntax:
|
* Argument syntax:
|
||||||
* e[:[GRP/][ENAME]] SYSTEM.EVENT [FETCHARGS]
|
* e[:[GRP/][ENAME]] SYSTEM.EVENT [FETCHARGS] [if FILTER]
|
||||||
* Fetch args:
|
* Fetch args (no space):
|
||||||
* <name>=$<field>[:TYPE]
|
* <name>=$<field>[:TYPE]
|
||||||
*/
|
*/
|
||||||
const char *event = NULL, *group = EPROBE_EVENT_SYSTEM;
|
const char *event = NULL, *group = EPROBE_EVENT_SYSTEM;
|
||||||
|
@ -942,8 +1013,8 @@ static int __trace_eprobe_create(int argc, const char *argv[])
|
||||||
char buf1[MAX_EVENT_NAME_LEN];
|
char buf1[MAX_EVENT_NAME_LEN];
|
||||||
char buf2[MAX_EVENT_NAME_LEN];
|
char buf2[MAX_EVENT_NAME_LEN];
|
||||||
char gbuf[MAX_EVENT_NAME_LEN];
|
char gbuf[MAX_EVENT_NAME_LEN];
|
||||||
int ret = 0;
|
int ret = 0, filter_idx = 0;
|
||||||
int i;
|
int i, filter_cnt;
|
||||||
|
|
||||||
if (argc < 2 || argv[0][0] != 'e')
|
if (argc < 2 || argv[0][0] != 'e')
|
||||||
return -ECANCELED;
|
return -ECANCELED;
|
||||||
|
@ -973,6 +1044,15 @@ static int __trace_eprobe_create(int argc, const char *argv[])
|
||||||
event = buf1;
|
event = buf1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 2; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], "if")) {
|
||||||
|
filter_idx = i + 1;
|
||||||
|
filter_cnt = argc - filter_idx;
|
||||||
|
argc = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mutex_lock(&event_mutex);
|
mutex_lock(&event_mutex);
|
||||||
event_call = find_and_get_event(sys_name, sys_event);
|
event_call = find_and_get_event(sys_name, sys_event);
|
||||||
ep = alloc_event_probe(group, event, event_call, argc - 2);
|
ep = alloc_event_probe(group, event, event_call, argc - 2);
|
||||||
|
@ -988,6 +1068,14 @@ static int __trace_eprobe_create(int argc, const char *argv[])
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filter_idx) {
|
||||||
|
trace_probe_log_set_index(filter_idx);
|
||||||
|
ret = trace_eprobe_parse_filter(ep, filter_cnt, argv + filter_idx);
|
||||||
|
if (ret)
|
||||||
|
goto parse_error;
|
||||||
|
} else
|
||||||
|
ep->filter_str = NULL;
|
||||||
|
|
||||||
argc -= 2; argv += 2;
|
argc -= 2; argv += 2;
|
||||||
/* parse arguments */
|
/* parse arguments */
|
||||||
for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
|
for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
|
||||||
|
|
|
@ -445,7 +445,8 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
|
||||||
C(SAME_PROBE, "There is already the exact same probe event"),\
|
C(SAME_PROBE, "There is already the exact same probe event"),\
|
||||||
C(NO_EVENT_INFO, "This requires both group and event name to attach"),\
|
C(NO_EVENT_INFO, "This requires both group and event name to attach"),\
|
||||||
C(BAD_ATTACH_EVENT, "Attached event does not exist"),\
|
C(BAD_ATTACH_EVENT, "Attached event does not exist"),\
|
||||||
C(BAD_ATTACH_ARG, "Attached event does not have this field"),
|
C(BAD_ATTACH_ARG, "Attached event does not have this field"),\
|
||||||
|
C(NO_EP_FILTER, "No filter rule after 'if'"),
|
||||||
|
|
||||||
#undef C
|
#undef C
|
||||||
#define C(a, b) TP_ERR_##a
|
#define C(a, b) TP_ERR_##a
|
||||||
|
|
Loading…
Add table
Reference in a new issue