tracing: Add trace_trigger kernel command line option
authorSteven Rostedt (Google) <rostedt@goodmis.org>
Fri, 21 Oct 2022 01:00:56 +0000 (21:00 -0400)
committerSteven Rostedt (Google) <rostedt@goodmis.org>
Thu, 24 Nov 2022 00:08:30 +0000 (19:08 -0500)
Allow triggers to be enabled at kernel boot up. For example:

  trace_trigger="sched_switch.stacktrace if prev_state == 2"

The above will enable the stacktrace trigger on top of the sched_switch
event and only trigger if its prev_state is 2 (TASK_UNINTERRUPTIBLE). Then
at boot up, a stacktrace will trigger and be recorded in the tracing ring
buffer every time the sched_switch happens where the previous state is
TASK_INTERRUPTIBLE.

Another useful trigger would be "traceoff" which can stop tracing on an
event if a field of the event matches a certain value defined by the
filter ("if" statement).

Link: https://lore.kernel.org/linux-trace-kernel/20221020210056.0d8d0a5b@gandalf.local.home
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
Documentation/admin-guide/kernel-parameters.txt
kernel/trace/trace_events.c

index a465d52..ccf91a4 100644 (file)
                        See also Documentation/trace/ftrace.rst "trace options"
                        section.
 
+       trace_trigger=[trigger-list]
+                       [FTRACE] Add a event trigger on specific events.
+                       Set a trigger on top of a specific event, with an optional
+                       filter.
+
+                       The format is is "trace_trigger=<event>.<trigger>[ if <filter>],..."
+                       Where more than one trigger may be specified that are comma deliminated.
+
+                       For example:
+
+                         trace_trigger="sched_switch.stacktrace if prev_state == 2"
+
+                       The above will enable the "stacktrace" trigger on the "sched_switch"
+                       event but only trigger it if the "prev_state" of the "sched_switch"
+                       event is "2" (TASK_UNINTERUPTIBLE).
+
+                       See also "Event triggers" in Documentation/trace/events.rst
+
+
        traceoff_on_warning
                        [FTRACE] enable this option to disable tracing when a
                        warning is hit. This turns off "tracing_on". Tracing can
index f71ea6e..3bfaf56 100644 (file)
@@ -2796,6 +2796,44 @@ trace_create_new_event(struct trace_event_call *call,
        return file;
 }
 
+#ifdef CONFIG_HIST_TRIGGERS
+#define MAX_BOOT_TRIGGERS 32
+
+static struct boot_triggers {
+       const char              *event;
+       char                    *trigger;
+} bootup_triggers[MAX_BOOT_TRIGGERS];
+
+static char bootup_trigger_buf[COMMAND_LINE_SIZE];
+static int nr_boot_triggers;
+
+static __init int setup_trace_triggers(char *str)
+{
+       char *trigger;
+       char *buf;
+       int i;
+
+       strlcpy(bootup_trigger_buf, str, COMMAND_LINE_SIZE);
+       ring_buffer_expanded = true;
+       disable_tracing_selftest("running event triggers");
+
+       buf = bootup_trigger_buf;
+       for (i = 0; i < MAX_BOOT_TRIGGERS; i++) {
+               trigger = strsep(&buf, ",");
+               if (!trigger)
+                       break;
+               bootup_triggers[i].event = strsep(&trigger, ".");
+               bootup_triggers[i].trigger = strsep(&trigger, ".");
+               if (!bootup_triggers[i].trigger)
+                       break;
+       }
+
+       nr_boot_triggers = i;
+       return 1;
+}
+__setup("trace_trigger=", setup_trace_triggers);
+#endif
+
 /* Add an event to a trace directory */
 static int
 __trace_add_new_event(struct trace_event_call *call, struct trace_array *tr)
@@ -2812,6 +2850,28 @@ __trace_add_new_event(struct trace_event_call *call, struct trace_array *tr)
                return event_define_fields(call);
 }
 
+#ifdef CONFIG_HIST_TRIGGERS
+static void trace_early_triggers(struct trace_event_file *file, const char *name)
+{
+       int ret;
+       int i;
+
+       for (i = 0; i < nr_boot_triggers; i++) {
+               if (strcmp(name, bootup_triggers[i].event))
+                       continue;
+               mutex_lock(&event_mutex);
+               ret = trigger_process_regex(file, bootup_triggers[i].trigger);
+               mutex_unlock(&event_mutex);
+               if (ret)
+                       pr_err("Failed to register trigger '%s' on event %s\n",
+                              bootup_triggers[i].trigger,
+                              bootup_triggers[i].event);
+       }
+}
+#else
+static inline void trace_early_triggers(struct trace_event_file *file, const char *name) { }
+#endif
+
 /*
  * Just create a descriptor for early init. A descriptor is required
  * for enabling events at boot. We want to enable events before
@@ -2822,12 +2882,19 @@ __trace_early_add_new_event(struct trace_event_call *call,
                            struct trace_array *tr)
 {
        struct trace_event_file *file;
+       int ret;
 
        file = trace_create_new_event(call, tr);
        if (!file)
                return -ENOMEM;
 
-       return event_define_fields(call);
+       ret = event_define_fields(call);
+       if (ret)
+               return ret;
+
+       trace_early_triggers(file, trace_event_name(call));
+
+       return 0;
 }
 
 struct ftrace_module_file_ops;
@@ -3735,6 +3802,8 @@ static __init int event_trace_enable(void)
                        list_add(&call->list, &ftrace_events);
        }
 
+       register_trigger_cmds();
+
        /*
         * We need the top trace array to have a working set of trace
         * points at early init, before the debug files and directories
@@ -3749,7 +3818,6 @@ static __init int event_trace_enable(void)
 
        register_event_cmds();
 
-       register_trigger_cmds();
 
        return 0;
 }