]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
tracing: Add enable_hist/disable_hist triggers
authorTom Zanussi <tom.zanussi@linux.intel.com>
Thu, 3 Mar 2016 18:54:55 +0000 (12:54 -0600)
committerSteven Rostedt <rostedt@goodmis.org>
Tue, 19 Apr 2016 22:55:57 +0000 (18:55 -0400)
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>
include/linux/trace_events.h
kernel/trace/trace.c
kernel/trace/trace.h
kernel/trace/trace_events_hist.c
kernel/trace/trace_events_trigger.c

index 404603720650dac1abd528eb76923bc89be872ed..5f89a5b0c7e67ad412fbe33b49b13fa12d4187cc 100644 (file)
@@ -408,6 +408,7 @@ enum event_trigger_type {
        ETT_STACKTRACE          = (1 << 2),
        ETT_EVENT_ENABLE        = (1 << 3),
        ETT_EVENT_HIST          = (1 << 4),
+       ETT_HIST_ENABLE         = (1 << 5),
 };
 
 extern int filter_match_preds(struct event_filter *filter, void *rec);
index 2238bfde799baeeba5b5f5b4227b6fe07e01d897..8430145bea12ad7ae9b3b6a7f4ed02ccb03e2892 100644 (file)
@@ -3807,6 +3807,10 @@ static const char readme_msg[] =
        "\t   trigger: traceon, traceoff\n"
        "\t            enable_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
        "\t\t    stacktrace\n"
 #endif
@@ -3867,6 +3871,10 @@ static const char readme_msg[] =
        "\t    The 'clear' parameter will clear the contents of a running\n"
        "\t    hist trigger and leave its current paused/active state\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
 ;
 
index 505f8a45f426d5a2ef55f7339cb4442797002473..cab1f4bfe85bd25b8f80f35e3e3be1d396b0c0c5 100644 (file)
@@ -1166,8 +1166,10 @@ extern const struct file_operations event_hist_fops;
 
 #ifdef CONFIG_HIST_TRIGGERS
 extern int register_trigger_hist_cmd(void);
+extern int register_trigger_hist_enable_disable_cmds(void);
 #else
 static inline int register_trigger_hist_cmd(void) { return 0; }
+static inline int register_trigger_hist_enable_disable_cmds(void) { return 0; }
 #endif
 
 extern int register_trigger_cmds(void);
@@ -1185,6 +1187,34 @@ struct event_trigger_data {
        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 int event_trigger_init(struct event_trigger_ops *ops,
                              struct event_trigger_data *data);
@@ -1198,6 +1228,8 @@ extern int set_trigger_filter(char *filter_str,
                              struct event_trigger_data *trigger_data,
                              struct trace_event_file *file);
 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
index 4f4041d769263d8fc587289203d3806a81418097..5d4f0279244035c6ce48accb4d0bd3d680ed33af 100644 (file)
@@ -1393,3 +1393,118 @@ __init int register_trigger_hist_cmd(void)
 
        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;
+}
index d29092afe0050e38ebc01b054fd8a75582ab48e6..d133f20945662cd9f1e83204c831e305e5f446e6 100644 (file)
@@ -347,7 +347,7 @@ __init int register_event_command(struct event_command *cmd)
  * Currently we only unregister event commands from __init, so mark
  * 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;
        int ret = -ENODEV;
@@ -1062,15 +1062,6 @@ static __init void unregister_trigger_traceon_traceoff_cmds(void)
        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
 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);
 }
 
-static int
-event_enable_trigger_print(struct seq_file *m, struct event_trigger_ops *ops,
-                          struct event_trigger_data *data)
+int event_enable_trigger_print(struct seq_file *m,
+                              struct event_trigger_ops *ops,
+                              struct event_trigger_data *data)
 {
        struct enable_trigger_data *enable_data = data->private_data;
 
        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,
                   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;
 }
 
-static void
-event_enable_trigger_free(struct event_trigger_ops *ops,
-                         struct event_trigger_data *data)
+void event_enable_trigger_free(struct event_trigger_ops *ops,
+                              struct event_trigger_data *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,
 };
 
-static int
-event_enable_trigger_func(struct event_command *cmd_ops,
-                         struct trace_event_file *file,
-                         char *glob, char *cmd, char *param)
+int event_enable_trigger_func(struct event_command *cmd_ops,
+                             struct trace_event_file *file,
+                             char *glob, char *cmd, char *param)
 {
        struct trace_event_file *event_enable_file;
        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;
        const char *system;
        const char *event;
+       bool hist = false;
        char *trigger;
        char *number;
        bool enable;
@@ -1207,8 +1199,15 @@ event_enable_trigger_func(struct event_command *cmd_ops,
        if (!event_enable_file)
                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);
 
        ret = -ENOMEM;
@@ -1228,6 +1227,7 @@ event_enable_trigger_func(struct event_command *cmd_ops,
        INIT_LIST_HEAD(&trigger_data->list);
        RCU_INIT_POINTER(trigger_data->filter, NULL);
 
+       enable_data->hist = hist;
        enable_data->enable = enable;
        enable_data->file = event_enable_file;
        trigger_data->private_data = enable_data;
@@ -1305,10 +1305,10 @@ event_enable_trigger_func(struct event_command *cmd_ops,
        goto out;
 }
 
-static int event_enable_register_trigger(char *glob,
-                                        struct event_trigger_ops *ops,
-                                        struct event_trigger_data *data,
-                                        struct trace_event_file *file)
+int event_enable_register_trigger(char *glob,
+                                 struct event_trigger_ops *ops,
+                                 struct event_trigger_data *data,
+                                 struct trace_event_file *file)
 {
        struct enable_trigger_data *enable_data = data->private_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) {
                test_enable_data = test->private_data;
                if (test_enable_data &&
+                   (test->cmd_ops->trigger_type ==
+                    data->cmd_ops->trigger_type) &&
                    (test_enable_data->file == enable_data->file)) {
                        ret = -EEXIST;
                        goto out;
@@ -1343,10 +1345,10 @@ out:
        return ret;
 }
 
-static void event_enable_unregister_trigger(char *glob,
-                                           struct event_trigger_ops *ops,
-                                           struct event_trigger_data *test,
-                                           struct trace_event_file *file)
+void event_enable_unregister_trigger(char *glob,
+                                    struct event_trigger_ops *ops,
+                                    struct event_trigger_data *test,
+                                    struct trace_event_file *file)
 {
        struct enable_trigger_data *test_enable_data = test->private_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) {
                enable_data = data->private_data;
                if (enable_data &&
+                   (data->cmd_ops->trigger_type ==
+                    test->cmd_ops->trigger_type) &&
                    (enable_data->file == test_enable_data->file)) {
                        unregistered = true;
                        list_del_rcu(&data->list);
@@ -1375,8 +1379,12 @@ event_enable_get_trigger_ops(char *cmd, char *param)
        struct event_trigger_ops *ops;
        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;
-
+#endif
        if (enable)
                ops = param ? &event_enable_count_trigger_ops :
                        &event_enable_trigger_ops;
@@ -1447,6 +1455,7 @@ __init int register_trigger_cmds(void)
        register_trigger_snapshot_cmd();
        register_trigger_stacktrace_cmd();
        register_trigger_enable_disable_cmds();
+       register_trigger_hist_enable_disable_cmds();
        register_trigger_hist_cmd();
 
        return 0;