tracing: Allow triggers to filter for CPU ids and process names
authorDaniel Wagner <daniel.wagner@bmw-carit.de>
Mon, 10 Aug 2015 12:35:46 +0000 (14:35 +0200)
committerSteven Rostedt <rostedt@goodmis.org>
Tue, 11 Aug 2015 22:01:06 +0000 (18:01 -0400)
By extending the filter rules by more generic fields
we can write triggers filters like

  echo 'stacktrace if cpu == 1' > \
/sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/trigger

or

  echo 'stacktrace if comm == sshd'  > \
/sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/trigger

CPU and COMM are not part of struct trace_entry. We could add the two
new fields to ftrace_common_field list and fix up all depending
sides. But that looks pretty ugly. Another thing I would like to
avoid that the 'format' file contents changes.

All this can be avoided by introducing another list which contains
non field members of struct trace_entry.

Link: http://lkml.kernel.org/r/1439210146-24707-1-git-send-email-daniel.wagner@bmw-carit.de
Signed-off-by: Daniel Wagner <daniel.wagner@bmw-carit.de>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
kernel/trace/trace_events.c
kernel/trace/trace_events_filter.c

index 404a372..7ca09cd 100644 (file)
@@ -30,6 +30,7 @@
 DEFINE_MUTEX(event_mutex);
 
 LIST_HEAD(ftrace_events);
+static LIST_HEAD(ftrace_generic_fields);
 static LIST_HEAD(ftrace_common_fields);
 
 #define GFP_TRACE (GFP_KERNEL | __GFP_ZERO)
@@ -94,6 +95,10 @@ trace_find_event_field(struct trace_event_call *call, char *name)
        struct ftrace_event_field *field;
        struct list_head *head;
 
+       field = __find_event_field(&ftrace_generic_fields, name);
+       if (field)
+               return field;
+
        field = __find_event_field(&ftrace_common_fields, name);
        if (field)
                return field;
@@ -144,6 +149,13 @@ int trace_define_field(struct trace_event_call *call, const char *type,
 }
 EXPORT_SYMBOL_GPL(trace_define_field);
 
+#define __generic_field(type, item, filter_type)                       \
+       ret = __trace_define_field(&ftrace_generic_fields, #type,       \
+                                  #item, 0, 0, is_signed_type(type),   \
+                                  filter_type);                        \
+       if (ret)                                                        \
+               return ret;
+
 #define __common_field(type, item)                                     \
        ret = __trace_define_field(&ftrace_common_fields, #type,        \
                                   "common_" #item,                     \
@@ -153,6 +165,16 @@ EXPORT_SYMBOL_GPL(trace_define_field);
        if (ret)                                                        \
                return ret;
 
+static int trace_define_generic_fields(void)
+{
+       int ret;
+
+       __generic_field(int, cpu, FILTER_OTHER);
+       __generic_field(char *, comm, FILTER_PTR_STRING);
+
+       return ret;
+}
+
 static int trace_define_common_fields(void)
 {
        int ret;
@@ -2671,6 +2693,9 @@ static __init int event_trace_init(void)
        if (!entry)
                pr_warn("Could not create tracefs 'available_events' entry\n");
 
+       if (trace_define_generic_fields())
+               pr_warn("tracing: Failed to allocated generic fields");
+
        if (trace_define_common_fields())
                pr_warn("tracing: Failed to allocate common fields");
 
index d81d6f3..bd1bf18 100644 (file)
@@ -252,6 +252,50 @@ static int filter_pred_strloc(struct filter_pred *pred, void *event)
        return match;
 }
 
+/* Filter predicate for CPUs. */
+static int filter_pred_cpu(struct filter_pred *pred, void *event)
+{
+       int cpu, cmp;
+       int match = 0;
+
+       cpu = raw_smp_processor_id();
+       cmp = pred->val;
+
+       switch (pred->op) {
+       case OP_EQ:
+               match = cpu == cmp;
+               break;
+       case OP_LT:
+               match = cpu < cmp;
+               break;
+       case OP_LE:
+               match = cpu <= cmp;
+               break;
+       case OP_GT:
+               match = cpu > cmp;
+               break;
+       case OP_GE:
+               match = cpu >= cmp;
+               break;
+       default:
+               break;
+       }
+
+       return !!match == !pred->not;
+}
+
+/* Filter predicate for COMM. */
+static int filter_pred_comm(struct filter_pred *pred, void *event)
+{
+       int cmp, match;
+
+       cmp = pred->regex.match(current->comm, &pred->regex,
+                               pred->regex.field_len);
+       match = cmp ^ pred->not;
+
+       return match;
+}
+
 static int filter_pred_none(struct filter_pred *pred, void *event)
 {
        return 0;
@@ -1002,7 +1046,10 @@ static int init_pred(struct filter_parse_state *ps,
        if (is_string_field(field)) {
                filter_build_regex(pred);
 
-               if (field->filter_type == FILTER_STATIC_STRING) {
+               if (!strcmp(field->name, "comm")) {
+                       fn = filter_pred_comm;
+                       pred->regex.field_len = TASK_COMM_LEN;
+               } else if (field->filter_type == FILTER_STATIC_STRING) {
                        fn = filter_pred_string;
                        pred->regex.field_len = field->size;
                } else if (field->filter_type == FILTER_DYN_STRING)
@@ -1025,7 +1072,10 @@ static int init_pred(struct filter_parse_state *ps,
                }
                pred->val = val;
 
-               fn = select_comparison_fn(pred->op, field->size,
+               if (!strcmp(field->name, "cpu"))
+                       fn = filter_pred_cpu;
+               else
+                       fn = select_comparison_fn(pred->op, field->size,
                                          field->is_signed);
                if (!fn) {
                        parse_error(ps, FILT_ERR_INVALID_OP, 0);