tracing: Add enable_hist/disable_hist triggers

Similar to enable_event/disable_event triggers, these triggers enable
and disable the aggregation of events into maps rather than enabling
and disabling their writing into the trace buffer.

They can be used to automatically start and stop hist triggers based
on a matching filter condition.

If there's a paused hist trigger on system:event, the following would
start it when the filter condition was hit:

  # echo enable_hist:system:event [ if filter] > event/trigger

And the following would disable a running system:event hist trigger:

  # echo disable_hist:system:event [ if filter] > event/trigger

See Documentation/trace/events.txt for real examples.

Link: http://lkml.kernel.org/r/f812f086e52c8b7c8ad5443487375e03c96a601f.1457029949.git.tom.zanussi@linux.intel.com

Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Reviewed-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
This commit is contained in:
Tom Zanussi 2016-03-03 12:54:55 -06:00 committed by Steven Rostedt
parent 6a475cb17f
commit d0bad49bb0
5 changed files with 196 additions and 31 deletions

View File

@ -408,6 +408,7 @@ enum event_trigger_type {
ETT_STACKTRACE = (1 << 2), ETT_STACKTRACE = (1 << 2),
ETT_EVENT_ENABLE = (1 << 3), ETT_EVENT_ENABLE = (1 << 3),
ETT_EVENT_HIST = (1 << 4), ETT_EVENT_HIST = (1 << 4),
ETT_HIST_ENABLE = (1 << 5),
}; };
extern int filter_match_preds(struct event_filter *filter, void *rec); extern int filter_match_preds(struct event_filter *filter, void *rec);

View File

@ -3807,6 +3807,10 @@ static const char readme_msg[] =
"\t trigger: traceon, traceoff\n" "\t trigger: traceon, traceoff\n"
"\t enable_event:<system>:<event>\n" "\t enable_event:<system>:<event>\n"
"\t disable_event:<system>:<event>\n" "\t disable_event:<system>:<event>\n"
#ifdef CONFIG_HIST_TRIGGERS
"\t enable_hist:<system>:<event>\n"
"\t disable_hist:<system>:<event>\n"
#endif
#ifdef CONFIG_STACKTRACE #ifdef CONFIG_STACKTRACE
"\t\t stacktrace\n" "\t\t stacktrace\n"
#endif #endif
@ -3867,6 +3871,10 @@ static const char readme_msg[] =
"\t The 'clear' parameter will clear the contents of a running\n" "\t The 'clear' parameter will clear the contents of a running\n"
"\t hist trigger and leave its current paused/active state\n" "\t hist trigger and leave its current paused/active state\n"
"\t unchanged.\n\n" "\t unchanged.\n\n"
"\t The enable_hist and disable_hist triggers can be used to\n"
"\t have one event conditionally start and stop another event's\n"
"\t already-attached hist trigger. The syntax is analagous to\n"
"\t the enable_event and disable_event triggers.\n"
#endif #endif
; ;

View File

@ -1166,8 +1166,10 @@ extern const struct file_operations event_hist_fops;
#ifdef CONFIG_HIST_TRIGGERS #ifdef CONFIG_HIST_TRIGGERS
extern int register_trigger_hist_cmd(void); extern int register_trigger_hist_cmd(void);
extern int register_trigger_hist_enable_disable_cmds(void);
#else #else
static inline int register_trigger_hist_cmd(void) { return 0; } static inline int register_trigger_hist_cmd(void) { return 0; }
static inline int register_trigger_hist_enable_disable_cmds(void) { return 0; }
#endif #endif
extern int register_trigger_cmds(void); extern int register_trigger_cmds(void);
@ -1185,6 +1187,34 @@ struct event_trigger_data {
struct list_head list; struct list_head list;
}; };
/* Avoid typos */
#define ENABLE_EVENT_STR "enable_event"
#define DISABLE_EVENT_STR "disable_event"
#define ENABLE_HIST_STR "enable_hist"
#define DISABLE_HIST_STR "disable_hist"
struct enable_trigger_data {
struct trace_event_file *file;
bool enable;
bool hist;
};
extern int event_enable_trigger_print(struct seq_file *m,
struct event_trigger_ops *ops,
struct event_trigger_data *data);
extern void event_enable_trigger_free(struct event_trigger_ops *ops,
struct event_trigger_data *data);
extern int event_enable_trigger_func(struct event_command *cmd_ops,
struct trace_event_file *file,
char *glob, char *cmd, char *param);
extern int event_enable_register_trigger(char *glob,
struct event_trigger_ops *ops,
struct event_trigger_data *data,
struct trace_event_file *file);
extern void event_enable_unregister_trigger(char *glob,
struct event_trigger_ops *ops,
struct event_trigger_data *test,
struct trace_event_file *file);
extern void trigger_data_free(struct event_trigger_data *data); extern void trigger_data_free(struct event_trigger_data *data);
extern int event_trigger_init(struct event_trigger_ops *ops, extern int event_trigger_init(struct event_trigger_ops *ops,
struct event_trigger_data *data); struct event_trigger_data *data);
@ -1198,6 +1228,8 @@ extern int set_trigger_filter(char *filter_str,
struct event_trigger_data *trigger_data, struct event_trigger_data *trigger_data,
struct trace_event_file *file); struct trace_event_file *file);
extern int register_event_command(struct event_command *cmd); extern int register_event_command(struct event_command *cmd);
extern int unregister_event_command(struct event_command *cmd);
extern int register_trigger_hist_enable_disable_cmds(void);
/** /**
* struct event_trigger_ops - callbacks for trace event triggers * struct event_trigger_ops - callbacks for trace event triggers

View File

@ -1393,3 +1393,118 @@ __init int register_trigger_hist_cmd(void)
return ret; return ret;
} }
static void
hist_enable_trigger(struct event_trigger_data *data, void *rec)
{
struct enable_trigger_data *enable_data = data->private_data;
struct event_trigger_data *test;
list_for_each_entry_rcu(test, &enable_data->file->triggers, list) {
if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
if (enable_data->enable)
test->paused = false;
else
test->paused = true;
break;
}
}
}
static void
hist_enable_count_trigger(struct event_trigger_data *data, void *rec)
{
if (!data->count)
return;
if (data->count != -1)
(data->count)--;
hist_enable_trigger(data, rec);
}
static struct event_trigger_ops hist_enable_trigger_ops = {
.func = hist_enable_trigger,
.print = event_enable_trigger_print,
.init = event_trigger_init,
.free = event_enable_trigger_free,
};
static struct event_trigger_ops hist_enable_count_trigger_ops = {
.func = hist_enable_count_trigger,
.print = event_enable_trigger_print,
.init = event_trigger_init,
.free = event_enable_trigger_free,
};
static struct event_trigger_ops hist_disable_trigger_ops = {
.func = hist_enable_trigger,
.print = event_enable_trigger_print,
.init = event_trigger_init,
.free = event_enable_trigger_free,
};
static struct event_trigger_ops hist_disable_count_trigger_ops = {
.func = hist_enable_count_trigger,
.print = event_enable_trigger_print,
.init = event_trigger_init,
.free = event_enable_trigger_free,
};
static struct event_trigger_ops *
hist_enable_get_trigger_ops(char *cmd, char *param)
{
struct event_trigger_ops *ops;
bool enable;
enable = (strcmp(cmd, ENABLE_HIST_STR) == 0);
if (enable)
ops = param ? &hist_enable_count_trigger_ops :
&hist_enable_trigger_ops;
else
ops = param ? &hist_disable_count_trigger_ops :
&hist_disable_trigger_ops;
return ops;
}
static struct event_command trigger_hist_enable_cmd = {
.name = ENABLE_HIST_STR,
.trigger_type = ETT_HIST_ENABLE,
.func = event_enable_trigger_func,
.reg = event_enable_register_trigger,
.unreg = event_enable_unregister_trigger,
.get_trigger_ops = hist_enable_get_trigger_ops,
.set_filter = set_trigger_filter,
};
static struct event_command trigger_hist_disable_cmd = {
.name = DISABLE_HIST_STR,
.trigger_type = ETT_HIST_ENABLE,
.func = event_enable_trigger_func,
.reg = event_enable_register_trigger,
.unreg = event_enable_unregister_trigger,
.get_trigger_ops = hist_enable_get_trigger_ops,
.set_filter = set_trigger_filter,
};
static __init void unregister_trigger_hist_enable_disable_cmds(void)
{
unregister_event_command(&trigger_hist_enable_cmd);
unregister_event_command(&trigger_hist_disable_cmd);
}
__init int register_trigger_hist_enable_disable_cmds(void)
{
int ret;
ret = register_event_command(&trigger_hist_enable_cmd);
if (WARN_ON(ret < 0))
return ret;
ret = register_event_command(&trigger_hist_disable_cmd);
if (WARN_ON(ret < 0))
unregister_trigger_hist_enable_disable_cmds();
return ret;
}

View File

@ -347,7 +347,7 @@ __init int register_event_command(struct event_command *cmd)
* Currently we only unregister event commands from __init, so mark * Currently we only unregister event commands from __init, so mark
* this __init too. * this __init too.
*/ */
static __init int unregister_event_command(struct event_command *cmd) __init int unregister_event_command(struct event_command *cmd)
{ {
struct event_command *p, *n; struct event_command *p, *n;
int ret = -ENODEV; int ret = -ENODEV;
@ -1062,15 +1062,6 @@ static __init void unregister_trigger_traceon_traceoff_cmds(void)
unregister_event_command(&trigger_traceoff_cmd); unregister_event_command(&trigger_traceoff_cmd);
} }
/* Avoid typos */
#define ENABLE_EVENT_STR "enable_event"
#define DISABLE_EVENT_STR "disable_event"
struct enable_trigger_data {
struct trace_event_file *file;
bool enable;
};
static void static void
event_enable_trigger(struct event_trigger_data *data, void *rec) event_enable_trigger(struct event_trigger_data *data, void *rec)
{ {
@ -1100,14 +1091,16 @@ event_enable_count_trigger(struct event_trigger_data *data, void *rec)
event_enable_trigger(data, rec); event_enable_trigger(data, rec);
} }
static int int event_enable_trigger_print(struct seq_file *m,
event_enable_trigger_print(struct seq_file *m, struct event_trigger_ops *ops, struct event_trigger_ops *ops,
struct event_trigger_data *data) struct event_trigger_data *data)
{ {
struct enable_trigger_data *enable_data = data->private_data; struct enable_trigger_data *enable_data = data->private_data;
seq_printf(m, "%s:%s:%s", seq_printf(m, "%s:%s:%s",
enable_data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR, enable_data->hist ?
(enable_data->enable ? ENABLE_HIST_STR : DISABLE_HIST_STR) :
(enable_data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR),
enable_data->file->event_call->class->system, enable_data->file->event_call->class->system,
trace_event_name(enable_data->file->event_call)); trace_event_name(enable_data->file->event_call));
@ -1124,9 +1117,8 @@ event_enable_trigger_print(struct seq_file *m, struct event_trigger_ops *ops,
return 0; return 0;
} }
static void void event_enable_trigger_free(struct event_trigger_ops *ops,
event_enable_trigger_free(struct event_trigger_ops *ops, struct event_trigger_data *data)
struct event_trigger_data *data)
{ {
struct enable_trigger_data *enable_data = data->private_data; struct enable_trigger_data *enable_data = data->private_data;
@ -1171,10 +1163,9 @@ static struct event_trigger_ops event_disable_count_trigger_ops = {
.free = event_enable_trigger_free, .free = event_enable_trigger_free,
}; };
static int int event_enable_trigger_func(struct event_command *cmd_ops,
event_enable_trigger_func(struct event_command *cmd_ops, struct trace_event_file *file,
struct trace_event_file *file, char *glob, char *cmd, char *param)
char *glob, char *cmd, char *param)
{ {
struct trace_event_file *event_enable_file; struct trace_event_file *event_enable_file;
struct enable_trigger_data *enable_data; struct enable_trigger_data *enable_data;
@ -1183,6 +1174,7 @@ event_enable_trigger_func(struct event_command *cmd_ops,
struct trace_array *tr = file->tr; struct trace_array *tr = file->tr;
const char *system; const char *system;
const char *event; const char *event;
bool hist = false;
char *trigger; char *trigger;
char *number; char *number;
bool enable; bool enable;
@ -1207,8 +1199,15 @@ event_enable_trigger_func(struct event_command *cmd_ops,
if (!event_enable_file) if (!event_enable_file)
goto out; goto out;
enable = strcmp(cmd, ENABLE_EVENT_STR) == 0; #ifdef CONFIG_HIST_TRIGGERS
hist = ((strcmp(cmd, ENABLE_HIST_STR) == 0) ||
(strcmp(cmd, DISABLE_HIST_STR) == 0));
enable = ((strcmp(cmd, ENABLE_EVENT_STR) == 0) ||
(strcmp(cmd, ENABLE_HIST_STR) == 0));
#else
enable = strcmp(cmd, ENABLE_EVENT_STR) == 0;
#endif
trigger_ops = cmd_ops->get_trigger_ops(cmd, trigger); trigger_ops = cmd_ops->get_trigger_ops(cmd, trigger);
ret = -ENOMEM; ret = -ENOMEM;
@ -1228,6 +1227,7 @@ event_enable_trigger_func(struct event_command *cmd_ops,
INIT_LIST_HEAD(&trigger_data->list); INIT_LIST_HEAD(&trigger_data->list);
RCU_INIT_POINTER(trigger_data->filter, NULL); RCU_INIT_POINTER(trigger_data->filter, NULL);
enable_data->hist = hist;
enable_data->enable = enable; enable_data->enable = enable;
enable_data->file = event_enable_file; enable_data->file = event_enable_file;
trigger_data->private_data = enable_data; trigger_data->private_data = enable_data;
@ -1305,10 +1305,10 @@ event_enable_trigger_func(struct event_command *cmd_ops,
goto out; goto out;
} }
static int event_enable_register_trigger(char *glob, int event_enable_register_trigger(char *glob,
struct event_trigger_ops *ops, struct event_trigger_ops *ops,
struct event_trigger_data *data, struct event_trigger_data *data,
struct trace_event_file *file) struct trace_event_file *file)
{ {
struct enable_trigger_data *enable_data = data->private_data; struct enable_trigger_data *enable_data = data->private_data;
struct enable_trigger_data *test_enable_data; struct enable_trigger_data *test_enable_data;
@ -1318,6 +1318,8 @@ static int event_enable_register_trigger(char *glob,
list_for_each_entry_rcu(test, &file->triggers, list) { list_for_each_entry_rcu(test, &file->triggers, list) {
test_enable_data = test->private_data; test_enable_data = test->private_data;
if (test_enable_data && if (test_enable_data &&
(test->cmd_ops->trigger_type ==
data->cmd_ops->trigger_type) &&
(test_enable_data->file == enable_data->file)) { (test_enable_data->file == enable_data->file)) {
ret = -EEXIST; ret = -EEXIST;
goto out; goto out;
@ -1343,10 +1345,10 @@ out:
return ret; return ret;
} }
static void event_enable_unregister_trigger(char *glob, void event_enable_unregister_trigger(char *glob,
struct event_trigger_ops *ops, struct event_trigger_ops *ops,
struct event_trigger_data *test, struct event_trigger_data *test,
struct trace_event_file *file) struct trace_event_file *file)
{ {
struct enable_trigger_data *test_enable_data = test->private_data; struct enable_trigger_data *test_enable_data = test->private_data;
struct enable_trigger_data *enable_data; struct enable_trigger_data *enable_data;
@ -1356,6 +1358,8 @@ static void event_enable_unregister_trigger(char *glob,
list_for_each_entry_rcu(data, &file->triggers, list) { list_for_each_entry_rcu(data, &file->triggers, list) {
enable_data = data->private_data; enable_data = data->private_data;
if (enable_data && if (enable_data &&
(data->cmd_ops->trigger_type ==
test->cmd_ops->trigger_type) &&
(enable_data->file == test_enable_data->file)) { (enable_data->file == test_enable_data->file)) {
unregistered = true; unregistered = true;
list_del_rcu(&data->list); list_del_rcu(&data->list);
@ -1375,8 +1379,12 @@ event_enable_get_trigger_ops(char *cmd, char *param)
struct event_trigger_ops *ops; struct event_trigger_ops *ops;
bool enable; bool enable;
#ifdef CONFIG_HIST_TRIGGERS
enable = ((strcmp(cmd, ENABLE_EVENT_STR) == 0) ||
(strcmp(cmd, ENABLE_HIST_STR) == 0));
#else
enable = strcmp(cmd, ENABLE_EVENT_STR) == 0; enable = strcmp(cmd, ENABLE_EVENT_STR) == 0;
#endif
if (enable) if (enable)
ops = param ? &event_enable_count_trigger_ops : ops = param ? &event_enable_count_trigger_ops :
&event_enable_trigger_ops; &event_enable_trigger_ops;
@ -1447,6 +1455,7 @@ __init int register_trigger_cmds(void)
register_trigger_snapshot_cmd(); register_trigger_snapshot_cmd();
register_trigger_stacktrace_cmd(); register_trigger_stacktrace_cmd();
register_trigger_enable_disable_cmds(); register_trigger_enable_disable_cmds();
register_trigger_hist_enable_disable_cmds();
register_trigger_hist_cmd(); register_trigger_hist_cmd();
return 0; return 0;