diff options
author | Li Zefan <lizf@cn.fujitsu.com> | 2009-07-19 22:20:53 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2009-07-20 13:29:19 -0400 |
commit | 1f9963cbb0280e0cd554161e00f1a0eeddbf1ae1 (patch) | |
tree | 80e27aa2cf5401fc04f7dbb806ad7e0ec04b6e8c | |
parent | ff4e9da2330beb8d64498a513d3f9694e941b01a (diff) |
tracing/filters: improve subsystem filter
Currently a subsystem filter should be applicable to all events
under the subsystem, and if it failed, all the event filters
will be cleared. Those behaviors make subsys filter much less
useful:
# echo 'vec == 1' > irq/softirq_entry/filter
# echo 'irq == 5' > irq/filter
bash: echo: write error: Invalid argument
# cat irq/softirq_entry/filter
none
I'd expect it set the filter for irq_handler_entry/exit, and
not touch softirq_entry/exit.
The basic idea is, try to see if the filter can be applied
to which events, and then just apply to the those events:
# echo 'vec == 1' > softirq_entry/filter
# echo 'irq == 5' > filter
# cat irq_handler_entry/filter
irq == 5
# cat softirq_entry/filter
vec == 1
Changelog for v2:
- do some cleanups to address Frederic's comments.
Inspired-by: Steven Rostedt <srostedt@redhat.com>
Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
Acked-by: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <4A63D485.7030703@cn.fujitsu.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | include/linux/ftrace_event.h | 4 | ||||
-rw-r--r-- | kernel/trace/trace.h | 3 | ||||
-rw-r--r-- | kernel/trace/trace_events_filter.c | 124 |
3 files changed, 87 insertions, 44 deletions
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 5c093ffc655b..26d3673d5143 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h | |||
@@ -101,6 +101,8 @@ void trace_current_buffer_discard_commit(struct ring_buffer_event *event); | |||
101 | 101 | ||
102 | void tracing_record_cmdline(struct task_struct *tsk); | 102 | void tracing_record_cmdline(struct task_struct *tsk); |
103 | 103 | ||
104 | struct event_filter; | ||
105 | |||
104 | struct ftrace_event_call { | 106 | struct ftrace_event_call { |
105 | struct list_head list; | 107 | struct list_head list; |
106 | char *name; | 108 | char *name; |
@@ -116,7 +118,7 @@ struct ftrace_event_call { | |||
116 | int (*define_fields)(void); | 118 | int (*define_fields)(void); |
117 | struct list_head fields; | 119 | struct list_head fields; |
118 | int filter_active; | 120 | int filter_active; |
119 | void *filter; | 121 | struct event_filter *filter; |
120 | void *mod; | 122 | void *mod; |
121 | 123 | ||
122 | #ifdef CONFIG_EVENT_PROFILE | 124 | #ifdef CONFIG_EVENT_PROFILE |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 94305c7bc11c..758b0dbed552 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -750,13 +750,14 @@ struct event_filter { | |||
750 | int n_preds; | 750 | int n_preds; |
751 | struct filter_pred **preds; | 751 | struct filter_pred **preds; |
752 | char *filter_string; | 752 | char *filter_string; |
753 | bool no_reset; | ||
753 | }; | 754 | }; |
754 | 755 | ||
755 | struct event_subsystem { | 756 | struct event_subsystem { |
756 | struct list_head list; | 757 | struct list_head list; |
757 | const char *name; | 758 | const char *name; |
758 | struct dentry *entry; | 759 | struct dentry *entry; |
759 | void *filter; | 760 | struct event_filter *filter; |
760 | int nr_events; | 761 | int nr_events; |
761 | }; | 762 | }; |
762 | 763 | ||
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 1c80ef702b83..27c2dbea3710 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c | |||
@@ -420,7 +420,14 @@ oom: | |||
420 | } | 420 | } |
421 | EXPORT_SYMBOL_GPL(init_preds); | 421 | EXPORT_SYMBOL_GPL(init_preds); |
422 | 422 | ||
423 | static void filter_free_subsystem_preds(struct event_subsystem *system) | 423 | enum { |
424 | FILTER_DISABLE_ALL, | ||
425 | FILTER_INIT_NO_RESET, | ||
426 | FILTER_SKIP_NO_RESET, | ||
427 | }; | ||
428 | |||
429 | static void filter_free_subsystem_preds(struct event_subsystem *system, | ||
430 | int flag) | ||
424 | { | 431 | { |
425 | struct ftrace_event_call *call; | 432 | struct ftrace_event_call *call; |
426 | 433 | ||
@@ -428,6 +435,14 @@ static void filter_free_subsystem_preds(struct event_subsystem *system) | |||
428 | if (!call->define_fields) | 435 | if (!call->define_fields) |
429 | continue; | 436 | continue; |
430 | 437 | ||
438 | if (flag == FILTER_INIT_NO_RESET) { | ||
439 | call->filter->no_reset = false; | ||
440 | continue; | ||
441 | } | ||
442 | |||
443 | if (flag == FILTER_SKIP_NO_RESET && call->filter->no_reset) | ||
444 | continue; | ||
445 | |||
431 | if (!strcmp(call->system, system->name)) { | 446 | if (!strcmp(call->system, system->name)) { |
432 | filter_disable_preds(call); | 447 | filter_disable_preds(call); |
433 | remove_filter_string(call->filter); | 448 | remove_filter_string(call->filter); |
@@ -529,7 +544,8 @@ static filter_pred_fn_t select_comparison_fn(int op, int field_size, | |||
529 | 544 | ||
530 | static int filter_add_pred(struct filter_parse_state *ps, | 545 | static int filter_add_pred(struct filter_parse_state *ps, |
531 | struct ftrace_event_call *call, | 546 | struct ftrace_event_call *call, |
532 | struct filter_pred *pred) | 547 | struct filter_pred *pred, |
548 | bool dry_run) | ||
533 | { | 549 | { |
534 | struct ftrace_event_field *field; | 550 | struct ftrace_event_field *field; |
535 | filter_pred_fn_t fn; | 551 | filter_pred_fn_t fn; |
@@ -541,10 +557,12 @@ static int filter_add_pred(struct filter_parse_state *ps, | |||
541 | 557 | ||
542 | if (pred->op == OP_AND) { | 558 | if (pred->op == OP_AND) { |
543 | pred->pop_n = 2; | 559 | pred->pop_n = 2; |
544 | return filter_add_pred_fn(ps, call, pred, filter_pred_and); | 560 | fn = filter_pred_and; |
561 | goto add_pred_fn; | ||
545 | } else if (pred->op == OP_OR) { | 562 | } else if (pred->op == OP_OR) { |
546 | pred->pop_n = 2; | 563 | pred->pop_n = 2; |
547 | return filter_add_pred_fn(ps, call, pred, filter_pred_or); | 564 | fn = filter_pred_or; |
565 | goto add_pred_fn; | ||
548 | } | 566 | } |
549 | 567 | ||
550 | field = find_event_field(call, pred->field_name); | 568 | field = find_event_field(call, pred->field_name); |
@@ -567,9 +585,6 @@ static int filter_add_pred(struct filter_parse_state *ps, | |||
567 | else | 585 | else |
568 | fn = filter_pred_strloc; | 586 | fn = filter_pred_strloc; |
569 | pred->str_len = field->size; | 587 | pred->str_len = field->size; |
570 | if (pred->op == OP_NE) | ||
571 | pred->not = 1; | ||
572 | return filter_add_pred_fn(ps, call, pred, fn); | ||
573 | } else { | 588 | } else { |
574 | if (field->is_signed) | 589 | if (field->is_signed) |
575 | ret = strict_strtoll(pred->str_val, 0, &val); | 590 | ret = strict_strtoll(pred->str_val, 0, &val); |
@@ -580,27 +595,33 @@ static int filter_add_pred(struct filter_parse_state *ps, | |||
580 | return -EINVAL; | 595 | return -EINVAL; |
581 | } | 596 | } |
582 | pred->val = val; | 597 | pred->val = val; |
583 | } | ||
584 | 598 | ||
585 | fn = select_comparison_fn(pred->op, field->size, field->is_signed); | 599 | fn = select_comparison_fn(pred->op, field->size, |
586 | if (!fn) { | 600 | field->is_signed); |
587 | parse_error(ps, FILT_ERR_INVALID_OP, 0); | 601 | if (!fn) { |
588 | return -EINVAL; | 602 | parse_error(ps, FILT_ERR_INVALID_OP, 0); |
603 | return -EINVAL; | ||
604 | } | ||
589 | } | 605 | } |
590 | 606 | ||
591 | if (pred->op == OP_NE) | 607 | if (pred->op == OP_NE) |
592 | pred->not = 1; | 608 | pred->not = 1; |
593 | 609 | ||
594 | return filter_add_pred_fn(ps, call, pred, fn); | 610 | add_pred_fn: |
611 | if (!dry_run) | ||
612 | return filter_add_pred_fn(ps, call, pred, fn); | ||
613 | return 0; | ||
595 | } | 614 | } |
596 | 615 | ||
597 | static int filter_add_subsystem_pred(struct filter_parse_state *ps, | 616 | static int filter_add_subsystem_pred(struct filter_parse_state *ps, |
598 | struct event_subsystem *system, | 617 | struct event_subsystem *system, |
599 | struct filter_pred *pred, | 618 | struct filter_pred *pred, |
600 | char *filter_string) | 619 | char *filter_string, |
620 | bool dry_run) | ||
601 | { | 621 | { |
602 | struct ftrace_event_call *call; | 622 | struct ftrace_event_call *call; |
603 | int err = 0; | 623 | int err = 0; |
624 | bool fail = true; | ||
604 | 625 | ||
605 | list_for_each_entry(call, &ftrace_events, list) { | 626 | list_for_each_entry(call, &ftrace_events, list) { |
606 | 627 | ||
@@ -610,16 +631,24 @@ static int filter_add_subsystem_pred(struct filter_parse_state *ps, | |||
610 | if (strcmp(call->system, system->name)) | 631 | if (strcmp(call->system, system->name)) |
611 | continue; | 632 | continue; |
612 | 633 | ||
613 | err = filter_add_pred(ps, call, pred); | 634 | if (call->filter->no_reset) |
614 | if (err) { | 635 | continue; |
615 | filter_free_subsystem_preds(system); | 636 | |
616 | parse_error(ps, FILT_ERR_BAD_SUBSYS_FILTER, 0); | 637 | err = filter_add_pred(ps, call, pred, dry_run); |
617 | goto out; | 638 | if (err) |
618 | } | 639 | call->filter->no_reset = true; |
619 | replace_filter_string(call->filter, filter_string); | 640 | else |
641 | fail = false; | ||
642 | |||
643 | if (!dry_run) | ||
644 | replace_filter_string(call->filter, filter_string); | ||
620 | } | 645 | } |
621 | out: | 646 | |
622 | return err; | 647 | if (fail) { |
648 | parse_error(ps, FILT_ERR_BAD_SUBSYS_FILTER, 0); | ||
649 | return err; | ||
650 | } | ||
651 | return 0; | ||
623 | } | 652 | } |
624 | 653 | ||
625 | static void parse_init(struct filter_parse_state *ps, | 654 | static void parse_init(struct filter_parse_state *ps, |
@@ -978,12 +1007,14 @@ static int check_preds(struct filter_parse_state *ps) | |||
978 | static int replace_preds(struct event_subsystem *system, | 1007 | static int replace_preds(struct event_subsystem *system, |
979 | struct ftrace_event_call *call, | 1008 | struct ftrace_event_call *call, |
980 | struct filter_parse_state *ps, | 1009 | struct filter_parse_state *ps, |
981 | char *filter_string) | 1010 | char *filter_string, |
1011 | bool dry_run) | ||
982 | { | 1012 | { |
983 | char *operand1 = NULL, *operand2 = NULL; | 1013 | char *operand1 = NULL, *operand2 = NULL; |
984 | struct filter_pred *pred; | 1014 | struct filter_pred *pred; |
985 | struct postfix_elt *elt; | 1015 | struct postfix_elt *elt; |
986 | int err; | 1016 | int err; |
1017 | int n_preds = 0; | ||
987 | 1018 | ||
988 | err = check_preds(ps); | 1019 | err = check_preds(ps); |
989 | if (err) | 1020 | if (err) |
@@ -1002,19 +1033,14 @@ static int replace_preds(struct event_subsystem *system, | |||
1002 | continue; | 1033 | continue; |
1003 | } | 1034 | } |
1004 | 1035 | ||
1036 | if (n_preds++ == MAX_FILTER_PRED) { | ||
1037 | parse_error(ps, FILT_ERR_TOO_MANY_PREDS, 0); | ||
1038 | return -ENOSPC; | ||
1039 | } | ||
1040 | |||
1005 | if (elt->op == OP_AND || elt->op == OP_OR) { | 1041 | if (elt->op == OP_AND || elt->op == OP_OR) { |
1006 | pred = create_logical_pred(elt->op); | 1042 | pred = create_logical_pred(elt->op); |
1007 | if (call) | 1043 | goto add_pred; |
1008 | err = filter_add_pred(ps, call, pred); | ||
1009 | else | ||
1010 | err = filter_add_subsystem_pred(ps, system, | ||
1011 | pred, filter_string); | ||
1012 | filter_free_pred(pred); | ||
1013 | if (err) | ||
1014 | return err; | ||
1015 | |||
1016 | operand1 = operand2 = NULL; | ||
1017 | continue; | ||
1018 | } | 1044 | } |
1019 | 1045 | ||
1020 | if (!operand1 || !operand2) { | 1046 | if (!operand1 || !operand2) { |
@@ -1023,11 +1049,12 @@ static int replace_preds(struct event_subsystem *system, | |||
1023 | } | 1049 | } |
1024 | 1050 | ||
1025 | pred = create_pred(elt->op, operand1, operand2); | 1051 | pred = create_pred(elt->op, operand1, operand2); |
1052 | add_pred: | ||
1026 | if (call) | 1053 | if (call) |
1027 | err = filter_add_pred(ps, call, pred); | 1054 | err = filter_add_pred(ps, call, pred, false); |
1028 | else | 1055 | else |
1029 | err = filter_add_subsystem_pred(ps, system, pred, | 1056 | err = filter_add_subsystem_pred(ps, system, pred, |
1030 | filter_string); | 1057 | filter_string, dry_run); |
1031 | filter_free_pred(pred); | 1058 | filter_free_pred(pred); |
1032 | if (err) | 1059 | if (err) |
1033 | return err; | 1060 | return err; |
@@ -1068,7 +1095,7 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string) | |||
1068 | goto out; | 1095 | goto out; |
1069 | } | 1096 | } |
1070 | 1097 | ||
1071 | err = replace_preds(NULL, call, ps, filter_string); | 1098 | err = replace_preds(NULL, call, ps, filter_string, false); |
1072 | if (err) | 1099 | if (err) |
1073 | append_filter_err(ps, call->filter); | 1100 | append_filter_err(ps, call->filter); |
1074 | 1101 | ||
@@ -1092,7 +1119,7 @@ int apply_subsystem_event_filter(struct event_subsystem *system, | |||
1092 | mutex_lock(&event_mutex); | 1119 | mutex_lock(&event_mutex); |
1093 | 1120 | ||
1094 | if (!strcmp(strstrip(filter_string), "0")) { | 1121 | if (!strcmp(strstrip(filter_string), "0")) { |
1095 | filter_free_subsystem_preds(system); | 1122 | filter_free_subsystem_preds(system, FILTER_DISABLE_ALL); |
1096 | remove_filter_string(system->filter); | 1123 | remove_filter_string(system->filter); |
1097 | mutex_unlock(&event_mutex); | 1124 | mutex_unlock(&event_mutex); |
1098 | return 0; | 1125 | return 0; |
@@ -1103,7 +1130,6 @@ int apply_subsystem_event_filter(struct event_subsystem *system, | |||
1103 | if (!ps) | 1130 | if (!ps) |
1104 | goto out_unlock; | 1131 | goto out_unlock; |
1105 | 1132 | ||
1106 | filter_free_subsystem_preds(system); | ||
1107 | replace_filter_string(system->filter, filter_string); | 1133 | replace_filter_string(system->filter, filter_string); |
1108 | 1134 | ||
1109 | parse_init(ps, filter_ops, filter_string); | 1135 | parse_init(ps, filter_ops, filter_string); |
@@ -1113,9 +1139,23 @@ int apply_subsystem_event_filter(struct event_subsystem *system, | |||
1113 | goto out; | 1139 | goto out; |
1114 | } | 1140 | } |
1115 | 1141 | ||
1116 | err = replace_preds(system, NULL, ps, filter_string); | 1142 | filter_free_subsystem_preds(system, FILTER_INIT_NO_RESET); |
1117 | if (err) | 1143 | |
1144 | /* try to see the filter can be applied to which events */ | ||
1145 | err = replace_preds(system, NULL, ps, filter_string, true); | ||
1146 | if (err) { | ||
1147 | append_filter_err(ps, system->filter); | ||
1148 | goto out; | ||
1149 | } | ||
1150 | |||
1151 | filter_free_subsystem_preds(system, FILTER_SKIP_NO_RESET); | ||
1152 | |||
1153 | /* really apply the filter to the events */ | ||
1154 | err = replace_preds(system, NULL, ps, filter_string, false); | ||
1155 | if (err) { | ||
1118 | append_filter_err(ps, system->filter); | 1156 | append_filter_err(ps, system->filter); |
1157 | filter_free_subsystem_preds(system, 2); | ||
1158 | } | ||
1119 | 1159 | ||
1120 | out: | 1160 | out: |
1121 | filter_opstack_clear(ps); | 1161 | filter_opstack_clear(ps); |