tracing/events: move the ftrace event tracing code to core
[deliverable/linux.git] / kernel / trace / trace_events_filter.c
CommitLineData
7ce7e424
TZ
1/*
2 * trace_events_filter - generic event filtering
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
19 */
20
21#include <linux/debugfs.h>
22#include <linux/uaccess.h>
23#include <linux/module.h>
24#include <linux/ctype.h>
25
26#include "trace.h"
4bda2d51 27#include "trace_output.h"
7ce7e424
TZ
28
29static int filter_pred_64(struct filter_pred *pred, void *event)
30{
31 u64 *addr = (u64 *)(event + pred->offset);
32 u64 val = (u64)pred->val;
33 int match;
34
35 match = (val == *addr) ^ pred->not;
36
37 return match;
38}
39
40static int filter_pred_32(struct filter_pred *pred, void *event)
41{
42 u32 *addr = (u32 *)(event + pred->offset);
43 u32 val = (u32)pred->val;
44 int match;
45
46 match = (val == *addr) ^ pred->not;
47
48 return match;
49}
50
51static int filter_pred_16(struct filter_pred *pred, void *event)
52{
53 u16 *addr = (u16 *)(event + pred->offset);
54 u16 val = (u16)pred->val;
55 int match;
56
57 match = (val == *addr) ^ pred->not;
58
59 return match;
60}
61
62static int filter_pred_8(struct filter_pred *pred, void *event)
63{
64 u8 *addr = (u8 *)(event + pred->offset);
65 u8 val = (u8)pred->val;
66 int match;
67
68 match = (val == *addr) ^ pred->not;
69
70 return match;
71}
72
73static int filter_pred_string(struct filter_pred *pred, void *event)
74{
75 char *addr = (char *)(event + pred->offset);
76 int cmp, match;
77
78 cmp = strncmp(addr, pred->str_val, pred->str_len);
79
80 match = (!cmp) ^ pred->not;
81
82 return match;
83}
84
0a19e53c
TZ
85static int filter_pred_none(struct filter_pred *pred, void *event)
86{
87 return 0;
88}
89
7ce7e424
TZ
90/* return 1 if event matches, 0 otherwise (discard) */
91int filter_match_preds(struct ftrace_event_call *call, void *rec)
92{
93 int i, matched, and_failed = 0;
94 struct filter_pred *pred;
95
0a19e53c
TZ
96 for (i = 0; i < call->n_preds; i++) {
97 pred = call->preds[i];
98 if (and_failed && !pred->or)
99 continue;
100 matched = pred->fn(pred, rec);
101 if (!matched && !pred->or) {
102 and_failed = 1;
103 continue;
104 } else if (matched && pred->or)
105 return 1;
7ce7e424
TZ
106 }
107
108 if (and_failed)
109 return 0;
110
111 return 1;
112}
113
0a19e53c
TZ
114void filter_print_preds(struct filter_pred **preds, int n_preds,
115 struct trace_seq *s)
7ce7e424 116{
7ce7e424
TZ
117 char *field_name;
118 struct filter_pred *pred;
119 int i;
120
0a19e53c 121 if (!n_preds) {
4bda2d51
TZ
122 trace_seq_printf(s, "none\n");
123 return;
7ce7e424
TZ
124 }
125
0a19e53c
TZ
126 for (i = 0; i < n_preds; i++) {
127 pred = preds[i];
128 field_name = pred->field_name;
129 if (i)
130 trace_seq_printf(s, pred->or ? "|| " : "&& ");
131 trace_seq_printf(s, "%s ", field_name);
132 trace_seq_printf(s, pred->not ? "!= " : "== ");
133 if (pred->str_len)
134 trace_seq_printf(s, "%s\n", pred->str_val);
135 else
136 trace_seq_printf(s, "%llu\n", pred->val);
7ce7e424 137 }
7ce7e424
TZ
138}
139
140static struct ftrace_event_field *
141find_event_field(struct ftrace_event_call *call, char *name)
142{
1fc2d5c1 143 struct ftrace_event_field *field;
7ce7e424 144
1fc2d5c1 145 list_for_each_entry(field, &call->fields, link) {
7ce7e424
TZ
146 if (!strcmp(field->name, name))
147 return field;
148 }
149
150 return NULL;
151}
152
153void filter_free_pred(struct filter_pred *pred)
154{
155 if (!pred)
156 return;
157
158 kfree(pred->field_name);
7ce7e424
TZ
159 kfree(pred);
160}
161
0a19e53c
TZ
162static void filter_clear_pred(struct filter_pred *pred)
163{
164 kfree(pred->field_name);
165 pred->field_name = NULL;
166 pred->str_len = 0;
167}
168
169static int filter_set_pred(struct filter_pred *dest,
170 struct filter_pred *src,
171 filter_pred_fn_t fn)
172{
173 *dest = *src;
174 dest->field_name = kstrdup(src->field_name, GFP_KERNEL);
175 if (!dest->field_name)
176 return -ENOMEM;
177 dest->fn = fn;
178
179 return 0;
180}
181
182void filter_disable_preds(struct ftrace_event_call *call)
7ce7e424
TZ
183{
184 int i;
185
0a19e53c
TZ
186 call->n_preds = 0;
187
188 for (i = 0; i < MAX_FILTER_PRED; i++)
189 call->preds[i]->fn = filter_pred_none;
190}
191
192int init_preds(struct ftrace_event_call *call)
193{
194 struct filter_pred *pred;
195 int i;
196
197 call->n_preds = 0;
198
199 call->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred), GFP_KERNEL);
200 if (!call->preds)
201 return -ENOMEM;
202
203 for (i = 0; i < MAX_FILTER_PRED; i++) {
204 pred = kzalloc(sizeof(*pred), GFP_KERNEL);
205 if (!pred)
206 goto oom;
207 pred->fn = filter_pred_none;
208 call->preds[i] = pred;
209 }
210
211 return 0;
212
213oom:
214 for (i = 0; i < MAX_FILTER_PRED; i++) {
215 if (call->preds[i])
7ce7e424 216 filter_free_pred(call->preds[i]);
7ce7e424 217 }
0a19e53c
TZ
218 kfree(call->preds);
219 call->preds = NULL;
220
221 return -ENOMEM;
7ce7e424
TZ
222}
223
cfb180f3
TZ
224void filter_free_subsystem_preds(struct event_subsystem *system)
225{
226 struct ftrace_event_call *call = __start_ftrace_events;
227 int i;
228
0a19e53c
TZ
229 if (system->n_preds) {
230 for (i = 0; i < system->n_preds; i++)
cfb180f3
TZ
231 filter_free_pred(system->preds[i]);
232 kfree(system->preds);
233 system->preds = NULL;
0a19e53c 234 system->n_preds = 0;
cfb180f3
TZ
235 }
236
237 events_for_each(call) {
e1112b4d 238 if (!call->define_fields)
cfb180f3
TZ
239 continue;
240
241 if (!strcmp(call->system, system->name))
0a19e53c 242 filter_disable_preds(call);
cfb180f3
TZ
243 }
244}
245
7ce7e424 246static int __filter_add_pred(struct ftrace_event_call *call,
0a19e53c
TZ
247 struct filter_pred *pred,
248 filter_pred_fn_t fn)
7ce7e424 249{
0a19e53c 250 int idx, err;
7ce7e424 251
0a19e53c
TZ
252 if (call->n_preds && !pred->compound)
253 filter_disable_preds(call);
7ce7e424 254
0a19e53c
TZ
255 if (call->n_preds == MAX_FILTER_PRED)
256 return -ENOSPC;
7ce7e424 257
0a19e53c
TZ
258 idx = call->n_preds;
259 filter_clear_pred(call->preds[idx]);
260 err = filter_set_pred(call->preds[idx], pred, fn);
261 if (err)
262 return err;
263
264 call->n_preds++;
7ce7e424 265
0a19e53c 266 return 0;
7ce7e424
TZ
267}
268
269static int is_string_field(const char *type)
270{
271 if (strchr(type, '[') && strstr(type, "char"))
272 return 1;
273
274 return 0;
275}
276
277int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred)
278{
279 struct ftrace_event_field *field;
0a19e53c 280 filter_pred_fn_t fn;
7ce7e424
TZ
281
282 field = find_event_field(call, pred->field_name);
283 if (!field)
284 return -EINVAL;
285
0a19e53c 286 pred->fn = filter_pred_none;
7ce7e424
TZ
287 pred->offset = field->offset;
288
289 if (is_string_field(field->type)) {
0a19e53c 290 if (!pred->str_len)
9f58a159 291 return -EINVAL;
0a19e53c 292 fn = filter_pred_string;
7ce7e424 293 pred->str_len = field->size;
0a19e53c 294 return __filter_add_pred(call, pred, fn);
9f58a159 295 } else {
0a19e53c 296 if (pred->str_len)
9f58a159 297 return -EINVAL;
7ce7e424
TZ
298 }
299
300 switch (field->size) {
301 case 8:
0a19e53c 302 fn = filter_pred_64;
7ce7e424
TZ
303 break;
304 case 4:
0a19e53c 305 fn = filter_pred_32;
7ce7e424
TZ
306 break;
307 case 2:
0a19e53c 308 fn = filter_pred_16;
7ce7e424
TZ
309 break;
310 case 1:
0a19e53c 311 fn = filter_pred_8;
7ce7e424
TZ
312 break;
313 default:
314 return -EINVAL;
315 }
316
0a19e53c 317 return __filter_add_pred(call, pred, fn);
cfb180f3
TZ
318}
319
320int filter_add_subsystem_pred(struct event_subsystem *system,
321 struct filter_pred *pred)
322{
323 struct ftrace_event_call *call = __start_ftrace_events;
cfb180f3 324
0a19e53c 325 if (system->n_preds && !pred->compound)
cfb180f3
TZ
326 filter_free_subsystem_preds(system);
327
0a19e53c 328 if (!system->n_preds) {
cfb180f3
TZ
329 system->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred),
330 GFP_KERNEL);
331 if (!system->preds)
332 return -ENOMEM;
333 }
334
0a19e53c 335 if (system->n_preds == MAX_FILTER_PRED)
44e9c8b7 336 return -ENOSPC;
c4cff064 337
0a19e53c
TZ
338 system->preds[system->n_preds] = pred;
339
cfb180f3 340 events_for_each(call) {
c4cff064
TZ
341 int err;
342
e1112b4d 343 if (!call->define_fields)
cfb180f3
TZ
344 continue;
345
c4cff064
TZ
346 if (strcmp(call->system, system->name))
347 continue;
348
349 if (!find_event_field(call, pred->field_name))
350 continue;
351
0a19e53c
TZ
352 err = filter_add_pred(call, pred);
353 if (err == -ENOMEM) {
354 system->preds[system->n_preds] = NULL;
355 return err;
356 }
cfb180f3
TZ
357 }
358
0a19e53c 359 system->n_preds++;
c4cff064 360
0a19e53c 361 return 0;
cfb180f3
TZ
362}
363
7ce7e424
TZ
364int filter_parse(char **pbuf, struct filter_pred *pred)
365{
366 char *tmp, *tok, *val_str = NULL;
367 int tok_n = 0;
368
369 /* field ==/!= number, or/and field ==/!= number, number */
370 while ((tok = strsep(pbuf, " \n"))) {
371 if (tok_n == 0) {
372 if (!strcmp(tok, "0")) {
373 pred->clear = 1;
374 return 0;
375 } else if (!strcmp(tok, "&&")) {
376 pred->or = 0;
377 pred->compound = 1;
378 } else if (!strcmp(tok, "||")) {
379 pred->or = 1;
380 pred->compound = 1;
381 } else
382 pred->field_name = tok;
383 tok_n = 1;
384 continue;
385 }
386 if (tok_n == 1) {
387 if (!pred->field_name)
388 pred->field_name = tok;
389 else if (!strcmp(tok, "!="))
390 pred->not = 1;
391 else if (!strcmp(tok, "=="))
392 pred->not = 0;
393 else {
394 pred->field_name = NULL;
395 return -EINVAL;
396 }
397 tok_n = 2;
398 continue;
399 }
400 if (tok_n == 2) {
401 if (pred->compound) {
402 if (!strcmp(tok, "!="))
403 pred->not = 1;
404 else if (!strcmp(tok, "=="))
405 pred->not = 0;
406 else {
407 pred->field_name = NULL;
408 return -EINVAL;
409 }
410 } else {
411 val_str = tok;
412 break; /* done */
413 }
414 tok_n = 3;
415 continue;
416 }
417 if (tok_n == 3) {
418 val_str = tok;
419 break; /* done */
420 }
421 }
422
0a19e53c
TZ
423 if (!val_str || !strlen(val_str)
424 || strlen(val_str) >= MAX_FILTER_STR_VAL) {
bcabd91c
LZ
425 pred->field_name = NULL;
426 return -EINVAL;
427 }
428
7ce7e424
TZ
429 pred->field_name = kstrdup(pred->field_name, GFP_KERNEL);
430 if (!pred->field_name)
431 return -ENOMEM;
432
0a19e53c 433 pred->str_len = 0;
a3e0ab05 434 pred->val = simple_strtoull(val_str, &tmp, 0);
7ce7e424 435 if (tmp == val_str) {
0a19e53c
TZ
436 strncpy(pred->str_val, val_str, MAX_FILTER_STR_VAL);
437 pred->str_len = strlen(val_str);
438 pred->str_val[pred->str_len] = '\0';
a3e0ab05
LZ
439 } else if (*tmp != '\0')
440 return -EINVAL;
7ce7e424
TZ
441
442 return 0;
443}
444
445
This page took 0.04859 seconds and 5 git commands to generate.