diff options
| author | Steven Rostedt <srostedt@redhat.com> | 2010-02-15 22:25:37 -0500 |
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2010-02-15 22:25:37 -0500 |
| commit | fa6f2462ec91a70974df0221a6f005de547ef73e (patch) | |
| tree | dcddffbfa586f41b685952c8d4a5c030f4cc09b7 | |
| parent | a419a4e2a34a24b9e7d3f91aa195beac1e8bced2 (diff) | |
parse-events: Add event filtering on event fields
Add the ability to filter on event fields. This patch implements
the filter on trace-cmd report.
Using the following:
trace-cmd report -F 'sched.sched_switch:next_prio < 100 ||
(prev_comm != "swapper" && next_comm != "swapper")'
Will return a report where prios are under 100 (Real-time)
and does not include any context switch with the swapper task.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | parse-events.c | 48 | ||||
| -rw-r--r-- | parse-events.h | 116 | ||||
| -rw-r--r-- | parse-filter.c | 847 | ||||
| -rw-r--r-- | trace-cmd.c | 3 | ||||
| -rw-r--r-- | trace-read.c | 69 |
6 files changed, 1068 insertions, 17 deletions
| @@ -149,7 +149,7 @@ TRACE_VIEW_MAIN_OBJS = trace-view-main.o $(TRACE_VIEW_OBJS) | |||
| 149 | TRACE_GRAPH_MAIN_OBJS = trace-graph-main.o $(TRACE_GRAPH_OBJS) | 149 | TRACE_GRAPH_MAIN_OBJS = trace-graph-main.o $(TRACE_GRAPH_OBJS) |
| 150 | KERNEL_SHARK_OBJS = $(TRACE_VIEW_OBJS) $(TRACE_GRAPH_OBJS) kernel-shark.o | 150 | KERNEL_SHARK_OBJS = $(TRACE_VIEW_OBJS) $(TRACE_GRAPH_OBJS) kernel-shark.o |
| 151 | 151 | ||
| 152 | PEVENT_LIB_OBJS = parse-events.o trace-seq.o | 152 | PEVENT_LIB_OBJS = parse-events.o trace-seq.o parse-filter.o |
| 153 | TCMD_LIB_OBJS = $(PEVENT_LIB_OBJS) trace-util.o trace-input.o trace-ftrace.o \ | 153 | TCMD_LIB_OBJS = $(PEVENT_LIB_OBJS) trace-util.o trace-input.o trace-ftrace.o \ |
| 154 | trace-output.o trace-record.o | 154 | trace-output.o trace-record.o |
| 155 | 155 | ||
diff --git a/parse-events.c b/parse-events.c index 1592fa3..b29a8d5 100644 --- a/parse-events.c +++ b/parse-events.c | |||
| @@ -44,6 +44,19 @@ static void init_input_buf(const char *buf, unsigned long long size) | |||
| 44 | input_buf_ptr = 0; | 44 | input_buf_ptr = 0; |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | /** | ||
| 48 | * pevent_buffer_init - init buffer for parsing | ||
| 49 | * @buf: buffer to parse | ||
| 50 | * @size: the size of the buffer | ||
| 51 | * | ||
| 52 | * For use with pevent_read_token(), this initializes the internal | ||
| 53 | * buffer that pevent_read_token() will parse. | ||
| 54 | */ | ||
| 55 | void pevent_buffer_init(const char *buf, unsigned long long size) | ||
| 56 | { | ||
| 57 | init_input_buf(buf, size); | ||
| 58 | } | ||
| 59 | |||
| 47 | void breakpoint(void) | 60 | void breakpoint(void) |
| 48 | { | 61 | { |
| 49 | static int x; | 62 | static int x; |
| @@ -551,18 +564,6 @@ static struct event_format *alloc_event(void) | |||
| 551 | return event; | 564 | return event; |
| 552 | } | 565 | } |
| 553 | 566 | ||
| 554 | enum event_type { | ||
| 555 | EVENT_ERROR, | ||
| 556 | EVENT_NONE, | ||
| 557 | EVENT_SPACE, | ||
| 558 | EVENT_NEWLINE, | ||
| 559 | EVENT_OP, | ||
| 560 | EVENT_DELIM, | ||
| 561 | EVENT_ITEM, | ||
| 562 | EVENT_DQUOTE, | ||
| 563 | EVENT_SQUOTE, | ||
| 564 | }; | ||
| 565 | |||
| 566 | static void add_event(struct pevent *pevent, struct event_format *event) | 567 | static void add_event(struct pevent *pevent, struct event_format *event) |
| 567 | { | 568 | { |
| 568 | int i; | 569 | int i; |
| @@ -914,6 +915,29 @@ static enum event_type read_token(char **tok) | |||
| 914 | return EVENT_NONE; | 915 | return EVENT_NONE; |
| 915 | } | 916 | } |
| 916 | 917 | ||
| 918 | /** | ||
| 919 | * pevent_read_token - access to utilites to use the pevent parser | ||
| 920 | * @tok: The token to return | ||
| 921 | * | ||
| 922 | * This will parse tokens from the string given by | ||
| 923 | * pevent_init_data(). | ||
| 924 | * | ||
| 925 | * Returns the token type. | ||
| 926 | */ | ||
| 927 | enum event_type pevent_read_token(char **tok) | ||
| 928 | { | ||
| 929 | return read_token(tok); | ||
| 930 | } | ||
| 931 | |||
| 932 | /** | ||
| 933 | * pevent_free_token - free a token returned by pevent_read_token | ||
| 934 | * @token: the token to free | ||
| 935 | */ | ||
| 936 | void pevent_free_token(char *token) | ||
| 937 | { | ||
| 938 | free_token(token); | ||
| 939 | } | ||
| 940 | |||
| 917 | /* no newline */ | 941 | /* no newline */ |
| 918 | static enum event_type read_token_item(char **tok) | 942 | static enum event_type read_token_item(char **tok) |
| 919 | { | 943 | { |
diff --git a/parse-events.h b/parse-events.h index 879da4c..cc5af58 100644 --- a/parse-events.h +++ b/parse-events.h | |||
| @@ -110,7 +110,7 @@ enum format_flags { | |||
| 110 | 110 | ||
| 111 | struct format_field { | 111 | struct format_field { |
| 112 | struct format_field *next; | 112 | struct format_field *next; |
| 113 | struct event_format *event; | 113 | struct event_format *event; |
| 114 | char *type; | 114 | char *type; |
| 115 | char *name; | 115 | char *name; |
| 116 | int offset; | 116 | int offset; |
| @@ -240,6 +240,18 @@ enum event_sort_type { | |||
| 240 | EVENT_SORT_SYSTEM, | 240 | EVENT_SORT_SYSTEM, |
| 241 | }; | 241 | }; |
| 242 | 242 | ||
| 243 | enum event_type { | ||
| 244 | EVENT_ERROR, | ||
| 245 | EVENT_NONE, | ||
| 246 | EVENT_SPACE, | ||
| 247 | EVENT_NEWLINE, | ||
| 248 | EVENT_OP, | ||
| 249 | EVENT_DELIM, | ||
| 250 | EVENT_ITEM, | ||
| 251 | EVENT_DQUOTE, | ||
| 252 | EVENT_SQUOTE, | ||
| 253 | }; | ||
| 254 | |||
| 243 | struct cmdline; | 255 | struct cmdline; |
| 244 | struct cmdline_list; | 256 | struct cmdline_list; |
| 245 | struct func_map; | 257 | struct func_map; |
| @@ -469,9 +481,111 @@ void pevent_free(struct pevent *pevent); | |||
| 469 | void pevent_ref(struct pevent *pevent); | 481 | void pevent_ref(struct pevent *pevent); |
| 470 | void pevent_unref(struct pevent *pevent); | 482 | void pevent_unref(struct pevent *pevent); |
| 471 | 483 | ||
| 484 | /* access to the internal parser */ | ||
| 485 | void pevent_buffer_init(const char *buf, unsigned long long size); | ||
| 486 | enum event_type pevent_read_token(char **tok); | ||
| 487 | void pevent_free_token(char *token); | ||
| 488 | |||
| 472 | /* for debugging */ | 489 | /* for debugging */ |
| 473 | void pevent_print_funcs(struct pevent *pevent); | 490 | void pevent_print_funcs(struct pevent *pevent); |
| 474 | void pevent_print_printk(struct pevent *pevent); | 491 | void pevent_print_printk(struct pevent *pevent); |
| 475 | 492 | ||
| 493 | /* ----------------------- filtering ----------------------- */ | ||
| 494 | |||
| 495 | enum filter_boolean_type { | ||
| 496 | FILTER_FALSE, | ||
| 497 | FILTER_TRUE, | ||
| 498 | }; | ||
| 499 | |||
| 500 | enum filter_op_type { | ||
| 501 | FILTER_OP_AND = 1, | ||
| 502 | FILTER_OP_OR, | ||
| 503 | FILTER_OP_NOT, | ||
| 504 | }; | ||
| 505 | |||
| 506 | enum filter_cmp_type { | ||
| 507 | FILTER_CMP_NONE, | ||
| 508 | FILTER_CMP_EQ, | ||
| 509 | FILTER_CMP_NE, | ||
| 510 | FILTER_CMP_GT, | ||
| 511 | FILTER_CMP_LT, | ||
| 512 | FILTER_CMP_GE, | ||
| 513 | FILTER_CMP_LE, | ||
| 514 | FILTER_CMP_MATCH, | ||
| 515 | FILTER_CMP_NOT_MATCH, | ||
| 516 | FILTER_CMP_CONTAIN, | ||
| 517 | FILTER_CMP_NOT_CONTAIN, | ||
| 518 | }; | ||
| 519 | |||
| 520 | enum filter_arg_type { | ||
| 521 | FILTER_ARG_NONE, | ||
| 522 | FILTER_ARG_BOOLEAN, | ||
| 523 | FILTER_ARG_OP, | ||
| 524 | FILTER_ARG_NUM, | ||
| 525 | FILTER_ARG_STR, | ||
| 526 | }; | ||
| 527 | |||
| 528 | struct fliter_arg; | ||
| 529 | |||
| 530 | struct filter_arg_boolean { | ||
| 531 | enum filter_boolean_type value; | ||
| 532 | }; | ||
| 533 | |||
| 534 | struct filter_arg_op { | ||
| 535 | enum filter_op_type type; | ||
| 536 | struct filter_arg *left; | ||
| 537 | struct filter_arg *right; | ||
| 538 | }; | ||
| 539 | |||
| 540 | struct filter_arg_num { | ||
| 541 | enum filter_cmp_type type; | ||
| 542 | struct format_field *field; | ||
| 543 | unsigned long long val; | ||
| 544 | }; | ||
| 545 | |||
| 546 | struct filter_arg_str { | ||
| 547 | enum filter_cmp_type type; | ||
| 548 | struct format_field *field; | ||
| 549 | char *val; | ||
| 550 | }; | ||
| 551 | |||
| 552 | struct filter_arg { | ||
| 553 | enum filter_arg_type type; | ||
| 554 | union { | ||
| 555 | struct filter_arg_boolean bool; | ||
| 556 | struct filter_arg_op op; | ||
| 557 | struct filter_arg_num num; | ||
| 558 | struct filter_arg_str str; | ||
| 559 | }; | ||
| 560 | }; | ||
| 561 | |||
| 562 | struct filter_type { | ||
| 563 | int event_id; | ||
| 564 | struct event_format *event; | ||
| 565 | struct filter_arg *filter; | ||
| 566 | }; | ||
| 567 | |||
| 568 | struct event_filter { | ||
| 569 | struct pevent *pevent; | ||
| 570 | int filters; | ||
| 571 | struct filter_type *event_filters; | ||
| 572 | }; | ||
| 573 | |||
| 574 | struct event_filter *pevent_filter_alloc(struct pevent *pevent); | ||
| 575 | |||
| 576 | #define FILTER_NONE -2 | ||
| 577 | #define FILTER_NOEXIST -1 | ||
| 578 | #define FILTER_MISS 0 | ||
| 579 | #define FILTER_MATCH 1 | ||
| 580 | |||
| 581 | int pevent_filter_add_filter_str(struct event_filter *filter, | ||
| 582 | const char *filter_str, | ||
| 583 | char **error_str); | ||
| 584 | |||
| 585 | |||
| 586 | int pevent_filter_match(struct event_filter *filter, | ||
| 587 | struct record *record); | ||
| 588 | |||
| 589 | void pevent_filter_free(struct event_filter *filter); | ||
| 476 | 590 | ||
| 477 | #endif /* _PARSE_EVENTS_H */ | 591 | #endif /* _PARSE_EVENTS_H */ |
diff --git a/parse-filter.c b/parse-filter.c new file mode 100644 index 0000000..156b7e9 --- /dev/null +++ b/parse-filter.c | |||
| @@ -0,0 +1,847 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> | ||
| 3 | * | ||
| 4 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 5 | * This program is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; | ||
| 8 | * version 2.1 of the License (not later!) | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this program; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 18 | * | ||
| 19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 20 | */ | ||
| 21 | #include <stdio.h> | ||
| 22 | #include <stdlib.h> | ||
| 23 | #include <string.h> | ||
| 24 | #include <stdarg.h> | ||
| 25 | #include <ctype.h> | ||
| 26 | #include <errno.h> | ||
| 27 | |||
| 28 | #include "parse-events.h" | ||
| 29 | |||
| 30 | struct event_list { | ||
| 31 | struct event_list *next; | ||
| 32 | struct event_format *event; | ||
| 33 | }; | ||
| 34 | |||
| 35 | #define MAX_ERR_STR_SIZE 256 | ||
| 36 | |||
| 37 | static void show_error(char **error_str, const char *fmt, ...) | ||
| 38 | { | ||
| 39 | va_list ap; | ||
| 40 | |||
| 41 | if (!error_str) | ||
| 42 | return; | ||
| 43 | |||
| 44 | *error_str = malloc_or_die(MAX_ERR_STR_SIZE); | ||
| 45 | |||
| 46 | va_start(ap, fmt); | ||
| 47 | vsnprintf(*error_str, MAX_ERR_STR_SIZE, fmt, ap); | ||
| 48 | va_end(ap); | ||
| 49 | } | ||
| 50 | |||
| 51 | static void free_token(char *token) | ||
| 52 | { | ||
| 53 | pevent_free_token(token); | ||
| 54 | } | ||
| 55 | |||
| 56 | static enum event_type read_token(char **tok) | ||
| 57 | { | ||
| 58 | enum event_type type; | ||
| 59 | char *token = NULL; | ||
| 60 | |||
| 61 | do { | ||
| 62 | free_token(token); | ||
| 63 | type = pevent_read_token(&token); | ||
| 64 | } while (type == EVENT_NEWLINE || type == EVENT_SPACE); | ||
| 65 | |||
| 66 | *tok = token; | ||
| 67 | return type; | ||
| 68 | } | ||
| 69 | |||
| 70 | static int filter_cmp(const void *a, const void *b) | ||
| 71 | { | ||
| 72 | const struct filter_type *ea = a; | ||
| 73 | const struct filter_type *eb = b; | ||
| 74 | |||
| 75 | if (ea->event_id < eb->event_id) | ||
| 76 | return -1; | ||
| 77 | |||
| 78 | if (ea->event_id > eb->event_id) | ||
| 79 | return 1; | ||
| 80 | |||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | static struct filter_type * | ||
| 85 | find_filter_type(struct event_filter *filter, int id) | ||
| 86 | { | ||
| 87 | struct filter_type *filter_type; | ||
| 88 | struct filter_type key; | ||
| 89 | |||
| 90 | key.event_id = id; | ||
| 91 | |||
| 92 | filter_type = bsearch(&key, filter->event_filters, | ||
| 93 | filter->filters, | ||
| 94 | sizeof(*filter->event_filters), | ||
| 95 | filter_cmp); | ||
| 96 | |||
| 97 | return filter_type; | ||
| 98 | } | ||
| 99 | |||
| 100 | static struct filter_type * | ||
| 101 | add_filter_type(struct event_filter *filter, int id) | ||
| 102 | { | ||
| 103 | struct filter_type *filter_type; | ||
| 104 | int i; | ||
| 105 | |||
| 106 | filter_type = find_filter_type(filter, id); | ||
| 107 | if (filter_type) | ||
| 108 | return filter_type; | ||
| 109 | |||
| 110 | if (!filter->filters) | ||
| 111 | filter->event_filters = | ||
| 112 | malloc_or_die(sizeof(*filter->event_filters)); | ||
| 113 | else { | ||
| 114 | filter->event_filters = | ||
| 115 | realloc(filter->event_filters, | ||
| 116 | sizeof(*filter->event_filters) * | ||
| 117 | (filter->filters + 1)); | ||
| 118 | if (!filter->event_filters) | ||
| 119 | die("Could not allocate filter"); | ||
| 120 | } | ||
| 121 | |||
| 122 | for (i = 0; i < filter->filters; i++) { | ||
| 123 | if (filter->event_filters[i].event_id > id) | ||
| 124 | break; | ||
| 125 | } | ||
| 126 | |||
| 127 | if (i < filter->filters) | ||
| 128 | memmove(&filter->event_filters[i+1], | ||
| 129 | &filter->event_filters[i], | ||
| 130 | sizeof(*filter->event_filters) * | ||
| 131 | (filter->filters - i)); | ||
| 132 | |||
| 133 | filter_type = &filter->event_filters[i]; | ||
| 134 | filter_type->event_id = id; | ||
| 135 | filter_type->event = pevent_find_event(filter->pevent, id); | ||
| 136 | filter_type->filter = NULL; | ||
| 137 | |||
| 138 | filter->filters++; | ||
| 139 | |||
| 140 | return filter_type; | ||
| 141 | } | ||
| 142 | |||
| 143 | /** | ||
| 144 | * pevent_filter_alloc - create a new event filter | ||
| 145 | * @pevent: The pevent that this filter is associated with | ||
| 146 | */ | ||
| 147 | struct event_filter *pevent_filter_alloc(struct pevent *pevent) | ||
| 148 | { | ||
| 149 | struct event_filter *filter; | ||
| 150 | |||
| 151 | filter = malloc_or_die(sizeof(*filter)); | ||
| 152 | memset(filter, 0, sizeof(*filter)); | ||
| 153 | filter->pevent = pevent; | ||
| 154 | pevent_ref(pevent); | ||
| 155 | |||
| 156 | return filter; | ||
| 157 | } | ||
| 158 | |||
| 159 | static struct filter_arg *allocate_arg(void) | ||
| 160 | { | ||
| 161 | struct filter_arg *arg; | ||
| 162 | |||
| 163 | arg = malloc_or_die(sizeof(*arg)); | ||
| 164 | memset(arg, 0, sizeof(*arg)); | ||
| 165 | |||
| 166 | return arg; | ||
| 167 | } | ||
| 168 | |||
| 169 | static void free_arg(struct filter_arg *arg) | ||
| 170 | { | ||
| 171 | if (!arg) | ||
| 172 | return; | ||
| 173 | |||
| 174 | switch (arg->type) { | ||
| 175 | case FILTER_ARG_NONE: | ||
| 176 | case FILTER_ARG_BOOLEAN: | ||
| 177 | case FILTER_ARG_NUM: | ||
| 178 | break; | ||
| 179 | |||
| 180 | case FILTER_ARG_STR: | ||
| 181 | free(arg->str.val); | ||
| 182 | break; | ||
| 183 | |||
| 184 | case FILTER_ARG_OP: | ||
| 185 | free_arg(arg->op.left); | ||
| 186 | free_arg(arg->op.right); | ||
| 187 | default: | ||
| 188 | break; | ||
| 189 | } | ||
| 190 | |||
| 191 | free(arg); | ||
| 192 | } | ||
| 193 | |||
| 194 | static int | ||
| 195 | find_event(struct pevent *pevent, struct event_list **events, | ||
| 196 | char *sys_name, char *event_name) | ||
| 197 | { | ||
| 198 | struct event_format *event; | ||
| 199 | struct event_list *list; | ||
| 200 | |||
| 201 | event = pevent_find_event_by_name(pevent, sys_name, event_name); | ||
| 202 | if (!event) | ||
| 203 | return -1; | ||
| 204 | |||
| 205 | list = malloc_or_die(sizeof(*list)); | ||
| 206 | list->next = *events; | ||
| 207 | *events = list; | ||
| 208 | list->event = event; | ||
| 209 | |||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | static void free_events(struct event_list *events) | ||
| 214 | { | ||
| 215 | struct event_list *event; | ||
| 216 | |||
| 217 | while (events) { | ||
| 218 | event = events; | ||
| 219 | events = events->next; | ||
| 220 | free(event); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | static int process_valid_field(struct filter_arg *arg, | ||
| 225 | struct format_field *field, | ||
| 226 | enum filter_cmp_type op_type, | ||
| 227 | enum event_type type, | ||
| 228 | char *val, | ||
| 229 | char **error_str) | ||
| 230 | { | ||
| 231 | switch (type) { | ||
| 232 | |||
| 233 | case EVENT_SQUOTE: | ||
| 234 | /* treat this as a character if string is of length 1? */ | ||
| 235 | if (strlen(val) == 1) | ||
| 236 | goto as_int; | ||
| 237 | /* fall through */ | ||
| 238 | |||
| 239 | case EVENT_DQUOTE: | ||
| 240 | /* right now only allow match */ | ||
| 241 | switch (op_type) { | ||
| 242 | case FILTER_CMP_EQ: | ||
| 243 | op_type = FILTER_CMP_MATCH; | ||
| 244 | break; | ||
| 245 | case FILTER_CMP_NE: | ||
| 246 | op_type = FILTER_CMP_NOT_MATCH; | ||
| 247 | break; | ||
| 248 | |||
| 249 | default: | ||
| 250 | show_error(error_str, | ||
| 251 | "Op not allowed with string"); | ||
| 252 | return -1; | ||
| 253 | } | ||
| 254 | arg->type = FILTER_ARG_STR; | ||
| 255 | arg->str.field = field; | ||
| 256 | arg->str.type = op_type; | ||
| 257 | arg->str.val = strdup(val); | ||
| 258 | if (!arg->str.val) | ||
| 259 | die("Can't allocate arg value"); | ||
| 260 | break; | ||
| 261 | case EVENT_ITEM: | ||
| 262 | as_int: | ||
| 263 | arg->type = FILTER_ARG_NUM; | ||
| 264 | arg->num.field = field; | ||
| 265 | arg->num.type = op_type; | ||
| 266 | arg->num.val = strtoll(val, NULL, 0); | ||
| 267 | break; | ||
| 268 | |||
| 269 | default: | ||
| 270 | /* Can't happen */ | ||
| 271 | return -1; | ||
| 272 | } | ||
| 273 | |||
| 274 | return 0; | ||
| 275 | } | ||
| 276 | |||
| 277 | static enum event_type | ||
| 278 | process_filter(struct event_format *event, struct filter_arg **parg, | ||
| 279 | char **tok, char **error_str, int cont); | ||
| 280 | |||
| 281 | static enum event_type | ||
| 282 | process_paren(struct event_format *event, struct filter_arg **parg, | ||
| 283 | char **tok, char **error_str, int cont); | ||
| 284 | |||
| 285 | static enum event_type | ||
| 286 | process_not(struct event_format *event, struct filter_arg **parg, | ||
| 287 | char **tok, char **error_str, int cont); | ||
| 288 | |||
| 289 | static enum event_type | ||
| 290 | process_token(struct event_format *event, struct filter_arg **parg, | ||
| 291 | char **tok, char **error_str, int cont) | ||
| 292 | { | ||
| 293 | enum event_type type; | ||
| 294 | char *token; | ||
| 295 | |||
| 296 | *tok = NULL; | ||
| 297 | *parg = NULL; | ||
| 298 | |||
| 299 | type = read_token(&token); | ||
| 300 | |||
| 301 | if (type == EVENT_ITEM) { | ||
| 302 | type = process_filter(event, parg, &token, error_str, cont); | ||
| 303 | |||
| 304 | } else if (type == EVENT_DELIM && strcmp(token, "(") == 0) { | ||
| 305 | free_token(token); | ||
| 306 | type = process_paren(event, parg, &token, error_str, cont); | ||
| 307 | |||
| 308 | } else if (type == EVENT_OP && strcmp(token, "!") == 0) { | ||
| 309 | type = process_not(event, parg, &token, error_str, cont); | ||
| 310 | } | ||
| 311 | |||
| 312 | if (type == EVENT_ERROR) { | ||
| 313 | free_token(token); | ||
| 314 | free_arg(*parg); | ||
| 315 | *parg = NULL; | ||
| 316 | return EVENT_ERROR; | ||
| 317 | } | ||
| 318 | |||
| 319 | *tok = token; | ||
| 320 | return type; | ||
| 321 | } | ||
| 322 | |||
| 323 | static enum event_type | ||
| 324 | process_op(struct event_format *event, struct filter_arg *larg, | ||
| 325 | struct filter_arg **parg, char **tok, char **error_str) | ||
| 326 | { | ||
| 327 | enum event_type type; | ||
| 328 | struct filter_arg *arg; | ||
| 329 | |||
| 330 | arg = allocate_arg(); | ||
| 331 | arg->type = FILTER_ARG_OP; | ||
| 332 | arg->op.left = larg; | ||
| 333 | |||
| 334 | /* Can only be called with '&&' or '||' */ | ||
| 335 | arg->op.type = strcmp(*tok, "&&") == 0 ? | ||
| 336 | FILTER_OP_AND : FILTER_OP_OR; | ||
| 337 | |||
| 338 | free_token(*tok); | ||
| 339 | |||
| 340 | type = process_token(event, &arg->op.right, tok, error_str, 1); | ||
| 341 | if (type == EVENT_ERROR) | ||
| 342 | free_arg(arg); | ||
| 343 | |||
| 344 | *parg = arg; | ||
| 345 | |||
| 346 | return type; | ||
| 347 | } | ||
| 348 | |||
| 349 | static enum event_type | ||
| 350 | process_filter(struct event_format *event, struct filter_arg **parg, | ||
| 351 | char **tok, char **error_str, int cont) | ||
| 352 | { | ||
| 353 | struct format_field *field; | ||
| 354 | enum filter_cmp_type etype; | ||
| 355 | struct filter_arg *arg; | ||
| 356 | enum event_type type; | ||
| 357 | char *field_name; | ||
| 358 | char *token; | ||
| 359 | char *op; | ||
| 360 | int ret; | ||
| 361 | |||
| 362 | *parg = NULL; | ||
| 363 | |||
| 364 | field_name = *tok; | ||
| 365 | *tok = NULL; | ||
| 366 | |||
| 367 | type = read_token(&token); | ||
| 368 | if (type != EVENT_OP) { | ||
| 369 | if (type == EVENT_NONE) | ||
| 370 | show_error(error_str, | ||
| 371 | "Expected OP but found end of filter after %s", | ||
| 372 | field_name); | ||
| 373 | else | ||
| 374 | show_error(error_str, | ||
| 375 | "Expected OP but found %s after %s", | ||
| 376 | token, field_name); | ||
| 377 | free_token(field_name); | ||
| 378 | free_token(token); | ||
| 379 | return EVENT_ERROR; | ||
| 380 | } | ||
| 381 | |||
| 382 | if (strcmp(token, "==") == 0) { | ||
| 383 | etype = FILTER_CMP_EQ; | ||
| 384 | } else if (strcmp(token, "!=") == 0) { | ||
| 385 | etype = FILTER_CMP_NE; | ||
| 386 | } else if (strcmp(token, "<") == 0) { | ||
| 387 | etype = FILTER_CMP_LT; | ||
| 388 | } else if (strcmp(token, ">") == 0) { | ||
| 389 | etype = FILTER_CMP_GT; | ||
| 390 | } else if (strcmp(token, "<=") == 0) { | ||
| 391 | etype = FILTER_CMP_LE; | ||
| 392 | } else if (strcmp(token, ">=") == 0) { | ||
| 393 | etype = FILTER_CMP_GE; | ||
| 394 | } else { | ||
| 395 | show_error(error_str, | ||
| 396 | "Unknown op '%s' after '%s'", | ||
| 397 | token, field_name); | ||
| 398 | free_token(field_name); | ||
| 399 | free_token(token); | ||
| 400 | return EVENT_ERROR; | ||
| 401 | } | ||
| 402 | op = token; | ||
| 403 | |||
| 404 | type = read_token(&token); | ||
| 405 | if (type != EVENT_ITEM && type != EVENT_SQUOTE && type != EVENT_DQUOTE) { | ||
| 406 | show_error(error_str, | ||
| 407 | "Expected an item after '%s %s' instead of %s", | ||
| 408 | field_name, op, token); | ||
| 409 | free_token(field_name); | ||
| 410 | free_token(op); | ||
| 411 | free_token(token); | ||
| 412 | return EVENT_ERROR; | ||
| 413 | } | ||
| 414 | free_token(op); | ||
| 415 | |||
| 416 | field = pevent_find_field(event, field_name); | ||
| 417 | free_token(field_name); | ||
| 418 | |||
| 419 | arg = allocate_arg(); | ||
| 420 | |||
| 421 | if (field) { | ||
| 422 | ret = process_valid_field(arg, field, etype, type, token, error_str); | ||
| 423 | if (ret < 0) { | ||
| 424 | free_arg(arg); | ||
| 425 | return EVENT_ERROR; | ||
| 426 | } | ||
| 427 | } else { | ||
| 428 | /* | ||
| 429 | * When an event does not contain a field in the | ||
| 430 | * filter, just make it false. | ||
| 431 | */ | ||
| 432 | arg->type = FILTER_ARG_BOOLEAN; | ||
| 433 | arg->bool.value = FILTER_FALSE; | ||
| 434 | } | ||
| 435 | |||
| 436 | free_token(token); | ||
| 437 | |||
| 438 | type = read_token(tok); | ||
| 439 | |||
| 440 | if (cont && type == EVENT_OP && | ||
| 441 | (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) { | ||
| 442 | /* continue */; | ||
| 443 | type = process_op(event, arg, parg, tok, error_str); | ||
| 444 | } else | ||
| 445 | *parg = arg; | ||
| 446 | |||
| 447 | return type; | ||
| 448 | } | ||
| 449 | |||
| 450 | static enum event_type | ||
| 451 | process_paren(struct event_format *event, struct filter_arg **parg, | ||
| 452 | char **tok, char **error_str, int cont) | ||
| 453 | { | ||
| 454 | struct filter_arg *arg; | ||
| 455 | enum event_type type; | ||
| 456 | |||
| 457 | *parg = NULL; | ||
| 458 | |||
| 459 | type = process_token(event, &arg, tok, error_str, 1); | ||
| 460 | if (type == EVENT_ERROR) { | ||
| 461 | free_arg(arg); | ||
| 462 | return type; | ||
| 463 | } | ||
| 464 | if (type != EVENT_DELIM || strcmp(*tok, ")") != 0) { | ||
| 465 | if (*tok) | ||
| 466 | show_error(error_str, | ||
| 467 | "Expected ')' but found %s", *tok); | ||
| 468 | else | ||
| 469 | show_error(error_str, | ||
| 470 | "Unexpected end of filter; Expected ')'"); | ||
| 471 | free_token(*tok); | ||
| 472 | *tok = NULL; | ||
| 473 | free_arg(arg); | ||
| 474 | return EVENT_ERROR; | ||
| 475 | } | ||
| 476 | free_token(*tok); | ||
| 477 | |||
| 478 | type = read_token(tok); | ||
| 479 | |||
| 480 | if (cont && type == EVENT_OP && | ||
| 481 | (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) { | ||
| 482 | /* continue */; | ||
| 483 | type = process_op(event, arg, parg, tok, error_str); | ||
| 484 | } else | ||
| 485 | *parg = arg; | ||
| 486 | |||
| 487 | return type; | ||
| 488 | } | ||
| 489 | |||
| 490 | static enum event_type | ||
| 491 | process_not(struct event_format *event, struct filter_arg **parg, | ||
| 492 | char **tok, char **error_str, int cont) | ||
| 493 | { | ||
| 494 | struct filter_arg *arg; | ||
| 495 | enum event_type type; | ||
| 496 | |||
| 497 | arg = allocate_arg(); | ||
| 498 | arg->type = FILTER_ARG_OP; | ||
| 499 | arg->op.type = FILTER_OP_NOT; | ||
| 500 | |||
| 501 | arg->op.left = NULL; | ||
| 502 | type = process_token(event, &arg->op.right, tok, error_str, 0); | ||
| 503 | if (type == EVENT_ERROR) { | ||
| 504 | free_arg(arg); | ||
| 505 | *parg = NULL; | ||
| 506 | free_token(*tok); | ||
| 507 | *tok = NULL; | ||
| 508 | return EVENT_ERROR; | ||
| 509 | } | ||
| 510 | |||
| 511 | if (cont && type == EVENT_OP && | ||
| 512 | (strcmp(*tok, "&&") == 0 || strcmp(*tok, "||") == 0)) { | ||
| 513 | /* continue */; | ||
| 514 | type = process_op(event, arg, parg, tok, error_str); | ||
| 515 | } else | ||
| 516 | *parg = arg; | ||
| 517 | |||
| 518 | return type; | ||
| 519 | } | ||
| 520 | |||
| 521 | static int filter_event(struct event_filter *filter, | ||
| 522 | struct event_format *event, | ||
| 523 | const char *filter_str, char **error_str) | ||
| 524 | { | ||
| 525 | struct filter_type *filter_type; | ||
| 526 | enum event_type type; | ||
| 527 | struct filter_arg *arg; | ||
| 528 | char *token; | ||
| 529 | |||
| 530 | pevent_buffer_init(filter_str, strlen(filter_str)); | ||
| 531 | |||
| 532 | type = process_token(event, &arg, &token, error_str, 1); | ||
| 533 | |||
| 534 | if (type == EVENT_ERROR) | ||
| 535 | return -1; | ||
| 536 | |||
| 537 | if (type != EVENT_NONE) { | ||
| 538 | show_error(error_str, | ||
| 539 | "Expected end where %s was found", | ||
| 540 | token); | ||
| 541 | free_token(token); | ||
| 542 | free_arg(arg); | ||
| 543 | return -1; | ||
| 544 | } | ||
| 545 | |||
| 546 | filter_type = add_filter_type(filter, event->id); | ||
| 547 | if (filter_type->filter) | ||
| 548 | free_arg(filter_type->filter); | ||
| 549 | filter_type->filter = arg; | ||
| 550 | |||
| 551 | return 0; | ||
| 552 | } | ||
| 553 | |||
| 554 | /** | ||
| 555 | * pevent_filter_add_filter_str - add a new filter | ||
| 556 | * @filter: the event filter to add to | ||
| 557 | * @filter_str: the filter string that contains the filter | ||
| 558 | * @error_str: string containing reason for failed filter | ||
| 559 | * | ||
| 560 | * Returns 0 if the filter was successfully added | ||
| 561 | * -1 if there was an error. | ||
| 562 | * | ||
| 563 | * On error, if @error_str points to a string pointer, | ||
| 564 | * it is set to the reason that the filter failed. | ||
| 565 | * This string must be freed with "free". | ||
| 566 | */ | ||
| 567 | int pevent_filter_add_filter_str(struct event_filter *filter, | ||
| 568 | const char *filter_str, | ||
| 569 | char **error_str) | ||
| 570 | { | ||
| 571 | struct pevent *pevent = filter->pevent; | ||
| 572 | struct event_list *event; | ||
| 573 | struct event_list *events = NULL; | ||
| 574 | enum event_type type; | ||
| 575 | const char *filter_start; | ||
| 576 | char *event_name = NULL; | ||
| 577 | char *sys_name = NULL; | ||
| 578 | char *token; | ||
| 579 | int rtn = 0; | ||
| 580 | int ret; | ||
| 581 | |||
| 582 | if (error_str) | ||
| 583 | *error_str = NULL; | ||
| 584 | |||
| 585 | filter_start = strchr(filter_str, ':'); | ||
| 586 | if (!filter_start) { | ||
| 587 | show_error(error_str, "No filter found"); | ||
| 588 | return -1; | ||
| 589 | } | ||
| 590 | |||
| 591 | pevent_buffer_init(filter_str, filter_start - filter_str); | ||
| 592 | |||
| 593 | again: | ||
| 594 | type = read_token(&token); | ||
| 595 | if (type != EVENT_ITEM) { | ||
| 596 | show_error(error_str, "Expected an event name but got %s", | ||
| 597 | token); | ||
| 598 | free_token(token); | ||
| 599 | return -1; | ||
| 600 | } | ||
| 601 | |||
| 602 | sys_name = token; | ||
| 603 | type = read_token(&token); | ||
| 604 | if (type == EVENT_OP && strcmp(token, ".") == 0) { | ||
| 605 | /* this is of "system.event" format */ | ||
| 606 | free_token(token); | ||
| 607 | type = read_token(&token); | ||
| 608 | if (type != EVENT_ITEM) { | ||
| 609 | if (token) | ||
| 610 | show_error(error_str, | ||
| 611 | "Expected an event after '%s.' and before %s", | ||
| 612 | sys_name, token); | ||
| 613 | else | ||
| 614 | show_error(error_str, | ||
| 615 | "Expected an event after '%s.'", | ||
| 616 | sys_name); | ||
| 617 | goto out_err; | ||
| 618 | } | ||
| 619 | event_name = token; | ||
| 620 | type = read_token(&token); | ||
| 621 | } else | ||
| 622 | event_name = NULL; | ||
| 623 | |||
| 624 | /* Find this event */ | ||
| 625 | ret = find_event(pevent, &events, sys_name, event_name); | ||
| 626 | if (ret < 0) { | ||
| 627 | if (event_name) | ||
| 628 | show_error(error_str, | ||
| 629 | "No event found under '%s.%s'", | ||
| 630 | sys_name, event_name); | ||
| 631 | else | ||
| 632 | show_error(error_str, | ||
| 633 | "No event found under '%s'", | ||
| 634 | sys_name); | ||
| 635 | goto out_err; | ||
| 636 | } | ||
| 637 | |||
| 638 | free_token(sys_name); | ||
| 639 | free_token(event_name); | ||
| 640 | sys_name = NULL; | ||
| 641 | event_name = NULL; | ||
| 642 | |||
| 643 | if (type == EVENT_DELIM && strcmp(token, ",")) { | ||
| 644 | /* This filter is for more than one event */ | ||
| 645 | free_token(token); | ||
| 646 | goto again; | ||
| 647 | } | ||
| 648 | |||
| 649 | /* this should be the end of parsing events */ | ||
| 650 | if (type != EVENT_NONE) { | ||
| 651 | free_events(events); | ||
| 652 | show_error(error_str, | ||
| 653 | "expected ':' after %s", token); | ||
| 654 | free_token(token); | ||
| 655 | return -1; | ||
| 656 | } | ||
| 657 | |||
| 658 | /* filter starts here */ | ||
| 659 | for (event = events; event; event = event->next) { | ||
| 660 | ret = filter_event(filter, event->event, filter_start + 1, | ||
| 661 | error_str); | ||
| 662 | /* Failures are returned if a parse error happened */ | ||
| 663 | if (ret < 0) | ||
| 664 | rtn = ret; | ||
| 665 | } | ||
| 666 | |||
| 667 | free_events(events); | ||
| 668 | |||
| 669 | return rtn; | ||
| 670 | |||
| 671 | out_err: | ||
| 672 | free_token(event_name); | ||
| 673 | free_token(sys_name); | ||
| 674 | free_token(token); | ||
| 675 | free_events(events); | ||
| 676 | return -1; | ||
| 677 | } | ||
| 678 | |||
| 679 | static void free_filter_type(struct filter_type *filter_type) | ||
| 680 | { | ||
| 681 | free_arg(filter_type->filter); | ||
| 682 | } | ||
| 683 | |||
| 684 | void pevent_filter_free(struct event_filter *filter) | ||
| 685 | { | ||
| 686 | int i; | ||
| 687 | |||
| 688 | pevent_unref(filter->pevent); | ||
| 689 | |||
| 690 | for (i = 0; i < filter->filters; i++) | ||
| 691 | free_filter_type(&filter->event_filters[i]); | ||
| 692 | |||
| 693 | free(filter); | ||
| 694 | } | ||
| 695 | |||
| 696 | static int test_filter(struct event_format *event, | ||
| 697 | struct filter_arg *arg, struct record *record); | ||
| 698 | |||
| 699 | static unsigned long long | ||
| 700 | get_value(struct format_field *field, struct record *record) | ||
| 701 | { | ||
| 702 | unsigned long long val; | ||
| 703 | |||
| 704 | pevent_read_number_field(field, record->data, &val); | ||
| 705 | |||
| 706 | if (!(field->flags & FIELD_IS_SIGNED)) | ||
| 707 | return val; | ||
| 708 | |||
| 709 | switch (field->size) { | ||
| 710 | case 1: | ||
| 711 | return (char)val; | ||
| 712 | case 2: | ||
| 713 | return (short)val; | ||
| 714 | case 4: | ||
| 715 | return (int)val; | ||
| 716 | case 8: | ||
| 717 | return (long long)val; | ||
| 718 | } | ||
| 719 | return val; | ||
| 720 | } | ||
| 721 | |||
| 722 | static int test_num(struct event_format *event, | ||
| 723 | struct filter_arg *arg, struct record *record) | ||
| 724 | { | ||
| 725 | unsigned long long val; | ||
| 726 | |||
| 727 | val = get_value(arg->num.field, record); | ||
| 728 | |||
| 729 | switch (arg->num.type) { | ||
| 730 | case FILTER_CMP_EQ: | ||
| 731 | return val == arg->num.val; | ||
| 732 | |||
| 733 | case FILTER_CMP_NE: | ||
| 734 | return val != arg->num.val; | ||
| 735 | |||
| 736 | case FILTER_CMP_GT: | ||
| 737 | return val > arg->num.val; | ||
| 738 | |||
| 739 | case FILTER_CMP_LT: | ||
| 740 | return val < arg->num.val; | ||
| 741 | |||
| 742 | case FILTER_CMP_GE: | ||
| 743 | return val >= arg->num.val; | ||
| 744 | |||
| 745 | case FILTER_CMP_LE: | ||
| 746 | return val <= arg->num.val; | ||
| 747 | |||
| 748 | default: | ||
| 749 | /* ?? */ | ||
| 750 | return 0; | ||
| 751 | } | ||
| 752 | } | ||
| 753 | |||
| 754 | static int test_str(struct event_format *event, | ||
| 755 | struct filter_arg *arg, struct record *record) | ||
| 756 | { | ||
| 757 | const char *val = record->data + arg->str.field->offset; | ||
| 758 | |||
| 759 | switch (arg->str.type) { | ||
| 760 | case FILTER_CMP_MATCH: | ||
| 761 | return strncmp(val, arg->str.val, arg->str.field->size) == 0; | ||
| 762 | |||
| 763 | case FILTER_CMP_NOT_MATCH: | ||
| 764 | return strncmp(val, arg->str.val, arg->str.field->size) != 0; | ||
| 765 | |||
| 766 | default: | ||
| 767 | /* ?? */ | ||
| 768 | return 0; | ||
| 769 | } | ||
| 770 | } | ||
| 771 | |||
| 772 | static int test_op(struct event_format *event, | ||
| 773 | struct filter_arg *arg, struct record *record) | ||
| 774 | { | ||
| 775 | switch (arg->op.type) { | ||
| 776 | case FILTER_OP_AND: | ||
| 777 | return test_filter(event, arg->op.left, record) && | ||
| 778 | test_filter(event, arg->op.right, record); | ||
| 779 | |||
| 780 | case FILTER_OP_OR: | ||
| 781 | return test_filter(event, arg->op.left, record) || | ||
| 782 | test_filter(event, arg->op.right, record); | ||
| 783 | |||
| 784 | case FILTER_OP_NOT: | ||
| 785 | return !test_filter(event, arg->op.right, record); | ||
| 786 | |||
| 787 | default: | ||
| 788 | /* ?? */ | ||
| 789 | return 0; | ||
| 790 | } | ||
| 791 | } | ||
| 792 | |||
| 793 | static int test_filter(struct event_format *event, | ||
| 794 | struct filter_arg *arg, struct record *record) | ||
| 795 | { | ||
| 796 | switch (arg->type) { | ||
| 797 | case FILTER_ARG_BOOLEAN: | ||
| 798 | /* easy case */ | ||
| 799 | return arg->bool.value; | ||
| 800 | |||
| 801 | case FILTER_ARG_OP: | ||
| 802 | return test_op(event, arg, record); | ||
| 803 | |||
| 804 | case FILTER_ARG_NUM: | ||
| 805 | return test_num(event, arg, record); | ||
| 806 | |||
| 807 | case FILTER_ARG_STR: | ||
| 808 | return test_str(event, arg, record); | ||
| 809 | |||
| 810 | default: | ||
| 811 | /* ?? */ | ||
| 812 | return 0; | ||
| 813 | } | ||
| 814 | } | ||
| 815 | |||
| 816 | /** | ||
| 817 | * pevent_filter_match - test if a record matches a filter | ||
| 818 | * @filter: filter struct with filter information | ||
| 819 | * @record: the record to test against the filter | ||
| 820 | * | ||
| 821 | * Returns: | ||
| 822 | * 1 - filter found for event and @record matches | ||
| 823 | * 0 - filter found for event and @record does not match | ||
| 824 | * -1 - no filter found for @record's event | ||
| 825 | * -2 - if no filters exist | ||
| 826 | */ | ||
| 827 | int pevent_filter_match(struct event_filter *filter, | ||
| 828 | struct record *record) | ||
| 829 | { | ||
| 830 | struct pevent *pevent = filter->pevent; | ||
| 831 | struct filter_type *filter_type; | ||
| 832 | int event_id; | ||
| 833 | |||
| 834 | if (!filter->filters) | ||
| 835 | return FILTER_NONE; | ||
| 836 | |||
| 837 | event_id = pevent_data_type(pevent, record); | ||
| 838 | |||
| 839 | filter_type = find_filter_type(filter, event_id); | ||
| 840 | |||
| 841 | if (!filter_type) | ||
| 842 | return FILTER_NOEXIST; | ||
| 843 | |||
| 844 | return test_filter(filter_type->event, filter_type->filter, record) ? | ||
| 845 | FILTER_MATCH : FILTER_MISS; | ||
| 846 | } | ||
| 847 | |||
diff --git a/trace-cmd.c b/trace-cmd.c index d866d8f..a50773e 100644 --- a/trace-cmd.c +++ b/trace-cmd.c | |||
| @@ -1035,12 +1035,13 @@ void usage(char **argv) | |||
| 1035 | " Disables the tracer (may reset trace file)\n" | 1035 | " Disables the tracer (may reset trace file)\n" |
| 1036 | " Used in conjunction with start\n" | 1036 | " Used in conjunction with start\n" |
| 1037 | "\n" | 1037 | "\n" |
| 1038 | " %s report [-i file] [--cpu cpu] [-e][-f][-l][-P][-E]\n" | 1038 | " %s report [-i file] [--cpu cpu] [-e][-f][-l][-P][-E][-F filter]\n" |
| 1039 | " -i input file [default trace.dat]\n" | 1039 | " -i input file [default trace.dat]\n" |
| 1040 | " -e show file endianess\n" | 1040 | " -e show file endianess\n" |
| 1041 | " -f show function list\n" | 1041 | " -f show function list\n" |
| 1042 | " -P show printk list\n" | 1042 | " -P show printk list\n" |
| 1043 | " -E show event files stored\n" | 1043 | " -E show event files stored\n" |
| 1044 | " -F filter to filter output on\n" | ||
| 1044 | " -l show latency format (default with latency tracers)\n" | 1045 | " -l show latency format (default with latency tracers)\n" |
| 1045 | "\n" | 1046 | "\n" |
| 1046 | " %s split [options] -o file [start [end]]\n" | 1047 | " %s split [options] -o file [start [end]]\n" |
diff --git a/trace-read.c b/trace-read.c index 8768f11..c05fc5d 100644 --- a/trace-read.c +++ b/trace-read.c | |||
| @@ -38,6 +38,14 @@ | |||
| 38 | 38 | ||
| 39 | #include "trace-local.h" | 39 | #include "trace-local.h" |
| 40 | 40 | ||
| 41 | static struct filter { | ||
| 42 | struct filter *next; | ||
| 43 | const char *filter; | ||
| 44 | } *filter_strings; | ||
| 45 | static struct filter **filter_next = &filter_strings; | ||
| 46 | |||
| 47 | static struct event_filter *event_filters; | ||
| 48 | |||
| 41 | static unsigned int page_size; | 49 | static unsigned int page_size; |
| 42 | static int input_fd; | 50 | static int input_fd; |
| 43 | const char *input_file = "trace.dat"; | 51 | const char *input_file = "trace.dat"; |
| @@ -189,12 +197,58 @@ static void test_save(struct record *record, int cpu) | |||
| 189 | } | 197 | } |
| 190 | #endif | 198 | #endif |
| 191 | 199 | ||
| 200 | static void add_filter(const char *filter) | ||
| 201 | { | ||
| 202 | struct filter *ftr; | ||
| 203 | |||
| 204 | ftr = malloc_or_die(sizeof(*ftr)); | ||
| 205 | ftr->filter = filter; | ||
| 206 | ftr->next = NULL; | ||
| 207 | |||
| 208 | /* must maintain order of command line */ | ||
| 209 | *filter_next = ftr; | ||
| 210 | filter_next = &ftr->next; | ||
| 211 | } | ||
| 212 | |||
| 213 | static void process_filters(struct tracecmd_input *handle) | ||
| 214 | { | ||
| 215 | struct pevent *pevent; | ||
| 216 | struct filter *filter; | ||
| 217 | char *errstr; | ||
| 218 | int ret; | ||
| 219 | |||
| 220 | pevent = tracecmd_get_pevent(handle); | ||
| 221 | event_filters = pevent_filter_alloc(pevent); | ||
| 222 | |||
| 223 | while (filter_strings) { | ||
| 224 | filter = filter_strings; | ||
| 225 | filter_strings = filter->next; | ||
| 226 | ret = pevent_filter_add_filter_str(event_filters, | ||
| 227 | filter->filter, | ||
| 228 | &errstr); | ||
| 229 | if (ret < 0) | ||
| 230 | die("Error filtering: %s\n %s", | ||
| 231 | filter->filter, errstr); | ||
| 232 | free(errstr); | ||
| 233 | free(filter); | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | static int filter_record(struct tracecmd_input *handle, | ||
| 238 | struct record *record) | ||
| 239 | { | ||
| 240 | return 0; | ||
| 241 | } | ||
| 242 | |||
| 192 | static void show_data(struct tracecmd_input *handle, | 243 | static void show_data(struct tracecmd_input *handle, |
| 193 | struct record *record, int cpu) | 244 | struct record *record, int cpu) |
| 194 | { | 245 | { |
| 195 | struct pevent *pevent; | 246 | struct pevent *pevent; |
| 196 | struct trace_seq s; | 247 | struct trace_seq s; |
| 197 | 248 | ||
| 249 | if (filter_record(handle, record)) | ||
| 250 | return; | ||
| 251 | |||
| 198 | pevent = tracecmd_get_pevent(handle); | 252 | pevent = tracecmd_get_pevent(handle); |
| 199 | 253 | ||
| 200 | test_save(record, cpu); | 254 | test_save(record, cpu); |
| @@ -241,6 +295,8 @@ static void read_data_info(struct tracecmd_input *handle) | |||
| 241 | return; | 295 | return; |
| 242 | } | 296 | } |
| 243 | 297 | ||
| 298 | process_filters(handle); | ||
| 299 | |||
| 244 | do { | 300 | do { |
| 245 | next = -1; | 301 | next = -1; |
| 246 | ts = 0; | 302 | ts = 0; |
| @@ -251,7 +307,13 @@ static void read_data_info(struct tracecmd_input *handle) | |||
| 251 | record = tracecmd_read_next_data(handle, &cpu); | 307 | record = tracecmd_read_next_data(handle, &cpu); |
| 252 | 308 | ||
| 253 | if (record) { | 309 | if (record) { |
| 254 | show_data(handle, record, next); | 310 | ret = pevent_filter_match(event_filters, record); |
| 311 | switch (ret) { | ||
| 312 | case FILTER_NONE: | ||
| 313 | case FILTER_MATCH: | ||
| 314 | show_data(handle, record, next); | ||
| 315 | break; | ||
| 316 | } | ||
| 255 | free_record(record); | 317 | free_record(record); |
| 256 | } | 318 | } |
| 257 | } while (record); | 319 | } while (record); |
| @@ -296,7 +358,7 @@ void trace_report (int argc, char **argv) | |||
| 296 | {NULL, 0, NULL, 0} | 358 | {NULL, 0, NULL, 0} |
| 297 | }; | 359 | }; |
| 298 | 360 | ||
| 299 | c = getopt_long (argc-1, argv+1, "+hi:fepPlE", | 361 | c = getopt_long (argc-1, argv+1, "+hi:fepPlEF:", |
| 300 | long_options, &option_index); | 362 | long_options, &option_index); |
| 301 | if (c == -1) | 363 | if (c == -1) |
| 302 | break; | 364 | break; |
| @@ -307,6 +369,9 @@ void trace_report (int argc, char **argv) | |||
| 307 | case 'i': | 369 | case 'i': |
| 308 | input_file = optarg; | 370 | input_file = optarg; |
| 309 | break; | 371 | break; |
| 372 | case 'F': | ||
| 373 | add_filter(optarg); | ||
| 374 | break; | ||
| 310 | case 'f': | 375 | case 'f': |
| 311 | show_funcs = 1; | 376 | show_funcs = 1; |
| 312 | break; | 377 | break; |
