]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - kernel/trace/trace_events_filter.c
tracing/function-graph-tracer: fix functions call traces imbalance
[mirror_ubuntu-hirsute-kernel.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"
27
28static int filter_pred_64(struct filter_pred *pred, void *event)
29{
30 u64 *addr = (u64 *)(event + pred->offset);
31 u64 val = (u64)pred->val;
32 int match;
33
34 match = (val == *addr) ^ pred->not;
35
36 return match;
37}
38
39static int filter_pred_32(struct filter_pred *pred, void *event)
40{
41 u32 *addr = (u32 *)(event + pred->offset);
42 u32 val = (u32)pred->val;
43 int match;
44
45 match = (val == *addr) ^ pred->not;
46
47 return match;
48}
49
50static int filter_pred_16(struct filter_pred *pred, void *event)
51{
52 u16 *addr = (u16 *)(event + pred->offset);
53 u16 val = (u16)pred->val;
54 int match;
55
56 match = (val == *addr) ^ pred->not;
57
58 return match;
59}
60
61static int filter_pred_8(struct filter_pred *pred, void *event)
62{
63 u8 *addr = (u8 *)(event + pred->offset);
64 u8 val = (u8)pred->val;
65 int match;
66
67 match = (val == *addr) ^ pred->not;
68
69 return match;
70}
71
72static int filter_pred_string(struct filter_pred *pred, void *event)
73{
74 char *addr = (char *)(event + pred->offset);
75 int cmp, match;
76
77 cmp = strncmp(addr, pred->str_val, pred->str_len);
78
79 match = (!cmp) ^ pred->not;
80
81 return match;
82}
83
84/* return 1 if event matches, 0 otherwise (discard) */
85int filter_match_preds(struct ftrace_event_call *call, void *rec)
86{
87 int i, matched, and_failed = 0;
88 struct filter_pred *pred;
89
90 for (i = 0; i < MAX_FILTER_PRED; i++) {
91 if (call->preds[i]) {
92 pred = call->preds[i];
93 if (and_failed && !pred->or)
94 continue;
95 matched = pred->fn(pred, rec);
96 if (!matched && !pred->or) {
97 and_failed = 1;
98 continue;
99 } else if (matched && pred->or)
100 return 1;
101 } else
102 break;
103 }
104
105 if (and_failed)
106 return 0;
107
108 return 1;
109}
110
111int filter_print_preds(struct filter_pred **preds, char *buf)
112{
113 ssize_t this_len = 0;
114 char *field_name;
115 struct filter_pred *pred;
116 int i;
117
118 if (!preds) {
119 this_len += sprintf(buf + this_len, "none\n");
120 return this_len;
121 }
122
123 for (i = 0; i < MAX_FILTER_PRED; i++) {
124 if (preds[i]) {
125 pred = preds[i];
126 field_name = pred->field_name;
127 if (i)
128 this_len += sprintf(buf + this_len,
129 pred->or ? "|| " : "&& ");
130 this_len += sprintf(buf + this_len,
131 "%s ", field_name);
132 this_len += sprintf(buf + this_len,
133 pred->not ? "!= " : "== ");
134 if (pred->str_val)
135 this_len += sprintf(buf + this_len,
136 "%s\n", pred->str_val);
137 else
138 this_len += sprintf(buf + this_len,
139 "%llu\n", pred->val);
140 } else
141 break;
142 }
143
144 return this_len;
145}
146
147static struct ftrace_event_field *
148find_event_field(struct ftrace_event_call *call, char *name)
149{
75c8b417 150 struct ftrace_event_field *field, *next;
7ce7e424 151
75c8b417 152 list_for_each_entry_safe(field, next, &call->fields, link) {
7ce7e424
TZ
153 if (!strcmp(field->name, name))
154 return field;
155 }
156
157 return NULL;
158}
159
160void filter_free_pred(struct filter_pred *pred)
161{
162 if (!pred)
163 return;
164
165 kfree(pred->field_name);
166 kfree(pred->str_val);
167 kfree(pred);
168}
169
170void filter_free_preds(struct ftrace_event_call *call)
171{
172 int i;
173
174 if (call->preds) {
175 for (i = 0; i < MAX_FILTER_PRED; i++)
176 filter_free_pred(call->preds[i]);
177 kfree(call->preds);
178 call->preds = NULL;
179 }
180}
181
cfb180f3
TZ
182void filter_free_subsystem_preds(struct event_subsystem *system)
183{
184 struct ftrace_event_call *call = __start_ftrace_events;
185 int i;
186
187 if (system->preds) {
188 for (i = 0; i < MAX_FILTER_PRED; i++)
189 filter_free_pred(system->preds[i]);
190 kfree(system->preds);
191 system->preds = NULL;
192 }
193
194 events_for_each(call) {
195 if (!call->name || !call->regfunc)
196 continue;
197
198 if (!strcmp(call->system, system->name))
199 filter_free_preds(call);
200 }
201}
202
7ce7e424
TZ
203static int __filter_add_pred(struct ftrace_event_call *call,
204 struct filter_pred *pred)
205{
206 int i;
207
208 if (call->preds && !pred->compound)
209 filter_free_preds(call);
210
211 if (!call->preds) {
212 call->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred),
213 GFP_KERNEL);
214 if (!call->preds)
215 return -ENOMEM;
216 }
217
218 for (i = 0; i < MAX_FILTER_PRED; i++) {
219 if (!call->preds[i]) {
220 call->preds[i] = pred;
221 return 0;
222 }
223 }
224
225 return -ENOMEM;
226}
227
228static int is_string_field(const char *type)
229{
230 if (strchr(type, '[') && strstr(type, "char"))
231 return 1;
232
233 return 0;
234}
235
236int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred)
237{
238 struct ftrace_event_field *field;
239
240 field = find_event_field(call, pred->field_name);
241 if (!field)
242 return -EINVAL;
243
244 pred->offset = field->offset;
245
246 if (is_string_field(field->type)) {
247 pred->fn = filter_pred_string;
248 pred->str_len = field->size;
249 return __filter_add_pred(call, pred);
250 }
251
252 switch (field->size) {
253 case 8:
254 pred->fn = filter_pred_64;
255 break;
256 case 4:
257 pred->fn = filter_pred_32;
258 break;
259 case 2:
260 pred->fn = filter_pred_16;
261 break;
262 case 1:
263 pred->fn = filter_pred_8;
264 break;
265 default:
266 return -EINVAL;
267 }
268
269 return __filter_add_pred(call, pred);
270}
271
cfb180f3
TZ
272static struct filter_pred *copy_pred(struct filter_pred *pred)
273{
274 struct filter_pred *new_pred = kmalloc(sizeof(*pred), GFP_KERNEL);
275 if (!new_pred)
276 return NULL;
277
278 memcpy(new_pred, pred, sizeof(*pred));
ee6cdabc
TZ
279
280 if (pred->field_name) {
281 new_pred->field_name = kstrdup(pred->field_name, GFP_KERNEL);
282 if (!new_pred->field_name) {
283 kfree(new_pred);
284 return NULL;
285 }
286 }
287
cfb180f3
TZ
288 if (pred->str_val) {
289 new_pred->str_val = kstrdup(pred->str_val, GFP_KERNEL);
cfb180f3 290 if (!new_pred->str_val) {
ee6cdabc 291 filter_free_pred(new_pred);
cfb180f3
TZ
292 return NULL;
293 }
294 }
295
296 return new_pred;
297}
298
299int filter_add_subsystem_pred(struct event_subsystem *system,
300 struct filter_pred *pred)
301{
302 struct ftrace_event_call *call = __start_ftrace_events;
303 struct filter_pred *event_pred;
304 int i;
305
306 if (system->preds && !pred->compound)
307 filter_free_subsystem_preds(system);
308
309 if (!system->preds) {
310 system->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred),
311 GFP_KERNEL);
312 if (!system->preds)
313 return -ENOMEM;
314 }
315
316 for (i = 0; i < MAX_FILTER_PRED; i++) {
317 if (!system->preds[i]) {
318 system->preds[i] = pred;
319 break;
320 }
cfb180f3
TZ
321 }
322
c4cff064
TZ
323 if (i == MAX_FILTER_PRED)
324 return -EINVAL;
325
cfb180f3 326 events_for_each(call) {
c4cff064
TZ
327 int err;
328
cfb180f3
TZ
329 if (!call->name || !call->regfunc)
330 continue;
331
c4cff064
TZ
332 if (strcmp(call->system, system->name))
333 continue;
334
335 if (!find_event_field(call, pred->field_name))
336 continue;
337
338 event_pred = copy_pred(pred);
339 if (!event_pred)
340 goto oom;
341
342 err = filter_add_pred(call, event_pred);
343 if (err)
344 filter_free_pred(event_pred);
345 if (err == -ENOMEM)
346 goto oom;
cfb180f3
TZ
347 }
348
349 return 0;
c4cff064
TZ
350
351oom:
352 system->preds[i] = NULL;
353 return -ENOMEM;
cfb180f3
TZ
354}
355
7ce7e424
TZ
356int filter_parse(char **pbuf, struct filter_pred *pred)
357{
358 char *tmp, *tok, *val_str = NULL;
359 int tok_n = 0;
360
361 /* field ==/!= number, or/and field ==/!= number, number */
362 while ((tok = strsep(pbuf, " \n"))) {
363 if (tok_n == 0) {
364 if (!strcmp(tok, "0")) {
365 pred->clear = 1;
366 return 0;
367 } else if (!strcmp(tok, "&&")) {
368 pred->or = 0;
369 pred->compound = 1;
370 } else if (!strcmp(tok, "||")) {
371 pred->or = 1;
372 pred->compound = 1;
373 } else
374 pred->field_name = tok;
375 tok_n = 1;
376 continue;
377 }
378 if (tok_n == 1) {
379 if (!pred->field_name)
380 pred->field_name = tok;
381 else if (!strcmp(tok, "!="))
382 pred->not = 1;
383 else if (!strcmp(tok, "=="))
384 pred->not = 0;
385 else {
386 pred->field_name = NULL;
387 return -EINVAL;
388 }
389 tok_n = 2;
390 continue;
391 }
392 if (tok_n == 2) {
393 if (pred->compound) {
394 if (!strcmp(tok, "!="))
395 pred->not = 1;
396 else if (!strcmp(tok, "=="))
397 pred->not = 0;
398 else {
399 pred->field_name = NULL;
400 return -EINVAL;
401 }
402 } else {
403 val_str = tok;
404 break; /* done */
405 }
406 tok_n = 3;
407 continue;
408 }
409 if (tok_n == 3) {
410 val_str = tok;
411 break; /* done */
412 }
413 }
414
415 pred->field_name = kstrdup(pred->field_name, GFP_KERNEL);
416 if (!pred->field_name)
417 return -ENOMEM;
418
419 pred->val = simple_strtoull(val_str, &tmp, 10);
420 if (tmp == val_str) {
421 pred->str_val = kstrdup(val_str, GFP_KERNEL);
422 if (!pred->str_val)
423 return -ENOMEM;
424 }
425
426 return 0;
427}
428
429