Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tracing: Allow triggers to filter for CPU ids and process names

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>

authored by

Daniel Wagner and committed by
Steven Rostedt
9f616680 c93bf928

+77 -2
+25
kernel/trace/trace_events.c
··· 30 30 DEFINE_MUTEX(event_mutex); 31 31 32 32 LIST_HEAD(ftrace_events); 33 + static LIST_HEAD(ftrace_generic_fields); 33 34 static LIST_HEAD(ftrace_common_fields); 34 35 35 36 #define GFP_TRACE (GFP_KERNEL | __GFP_ZERO) ··· 95 94 struct ftrace_event_field *field; 96 95 struct list_head *head; 97 96 97 + field = __find_event_field(&ftrace_generic_fields, name); 98 + if (field) 99 + return field; 100 + 98 101 field = __find_event_field(&ftrace_common_fields, name); 99 102 if (field) 100 103 return field; ··· 149 144 } 150 145 EXPORT_SYMBOL_GPL(trace_define_field); 151 146 147 + #define __generic_field(type, item, filter_type) \ 148 + ret = __trace_define_field(&ftrace_generic_fields, #type, \ 149 + #item, 0, 0, is_signed_type(type), \ 150 + filter_type); \ 151 + if (ret) \ 152 + return ret; 153 + 152 154 #define __common_field(type, item) \ 153 155 ret = __trace_define_field(&ftrace_common_fields, #type, \ 154 156 "common_" #item, \ ··· 164 152 is_signed_type(type), FILTER_OTHER); \ 165 153 if (ret) \ 166 154 return ret; 155 + 156 + static int trace_define_generic_fields(void) 157 + { 158 + int ret; 159 + 160 + __generic_field(int, cpu, FILTER_OTHER); 161 + __generic_field(char *, comm, FILTER_PTR_STRING); 162 + 163 + return ret; 164 + } 167 165 168 166 static int trace_define_common_fields(void) 169 167 { ··· 2692 2670 tr, &ftrace_avail_fops); 2693 2671 if (!entry) 2694 2672 pr_warn("Could not create tracefs 'available_events' entry\n"); 2673 + 2674 + if (trace_define_generic_fields()) 2675 + pr_warn("tracing: Failed to allocated generic fields"); 2695 2676 2696 2677 if (trace_define_common_fields()) 2697 2678 pr_warn("tracing: Failed to allocate common fields");
+52 -2
kernel/trace/trace_events_filter.c
··· 252 252 return match; 253 253 } 254 254 255 + /* Filter predicate for CPUs. */ 256 + static int filter_pred_cpu(struct filter_pred *pred, void *event) 257 + { 258 + int cpu, cmp; 259 + int match = 0; 260 + 261 + cpu = raw_smp_processor_id(); 262 + cmp = pred->val; 263 + 264 + switch (pred->op) { 265 + case OP_EQ: 266 + match = cpu == cmp; 267 + break; 268 + case OP_LT: 269 + match = cpu < cmp; 270 + break; 271 + case OP_LE: 272 + match = cpu <= cmp; 273 + break; 274 + case OP_GT: 275 + match = cpu > cmp; 276 + break; 277 + case OP_GE: 278 + match = cpu >= cmp; 279 + break; 280 + default: 281 + break; 282 + } 283 + 284 + return !!match == !pred->not; 285 + } 286 + 287 + /* Filter predicate for COMM. */ 288 + static int filter_pred_comm(struct filter_pred *pred, void *event) 289 + { 290 + int cmp, match; 291 + 292 + cmp = pred->regex.match(current->comm, &pred->regex, 293 + pred->regex.field_len); 294 + match = cmp ^ pred->not; 295 + 296 + return match; 297 + } 298 + 255 299 static int filter_pred_none(struct filter_pred *pred, void *event) 256 300 { 257 301 return 0; ··· 1046 1002 if (is_string_field(field)) { 1047 1003 filter_build_regex(pred); 1048 1004 1049 - if (field->filter_type == FILTER_STATIC_STRING) { 1005 + if (!strcmp(field->name, "comm")) { 1006 + fn = filter_pred_comm; 1007 + pred->regex.field_len = TASK_COMM_LEN; 1008 + } else if (field->filter_type == FILTER_STATIC_STRING) { 1050 1009 fn = filter_pred_string; 1051 1010 pred->regex.field_len = field->size; 1052 1011 } else if (field->filter_type == FILTER_DYN_STRING) ··· 1072 1025 } 1073 1026 pred->val = val; 1074 1027 1075 - fn = select_comparison_fn(pred->op, field->size, 1028 + if (!strcmp(field->name, "cpu")) 1029 + fn = filter_pred_cpu; 1030 + else 1031 + fn = select_comparison_fn(pred->op, field->size, 1076 1032 field->is_signed); 1077 1033 if (!fn) { 1078 1034 parse_error(ps, FILT_ERR_INVALID_OP, 0);