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>
This commit is contained in:
parent
c93bf928fe
commit
9f61668073
2 changed files with 77 additions and 2 deletions
|
@ -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");
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue