diff options
-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; |