|
@@ -0,0 +1,326 @@
|
|
|
+/*
|
|
|
+ * trace_events_filter - generic event filtering
|
|
|
+ *
|
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
|
+ * (at your option) any later version.
|
|
|
+ *
|
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ * GNU General Public License for more details.
|
|
|
+ *
|
|
|
+ * You should have received a copy of the GNU General Public License
|
|
|
+ * along with this program; if not, write to the Free Software
|
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
+ *
|
|
|
+ * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
|
|
|
+ */
|
|
|
+
|
|
|
+#include <linux/debugfs.h>
|
|
|
+#include <linux/uaccess.h>
|
|
|
+#include <linux/module.h>
|
|
|
+#include <linux/ctype.h>
|
|
|
+
|
|
|
+#include "trace.h"
|
|
|
+
|
|
|
+static int filter_pred_64(struct filter_pred *pred, void *event)
|
|
|
+{
|
|
|
+ u64 *addr = (u64 *)(event + pred->offset);
|
|
|
+ u64 val = (u64)pred->val;
|
|
|
+ int match;
|
|
|
+
|
|
|
+ match = (val == *addr) ^ pred->not;
|
|
|
+
|
|
|
+ return match;
|
|
|
+}
|
|
|
+
|
|
|
+static int filter_pred_32(struct filter_pred *pred, void *event)
|
|
|
+{
|
|
|
+ u32 *addr = (u32 *)(event + pred->offset);
|
|
|
+ u32 val = (u32)pred->val;
|
|
|
+ int match;
|
|
|
+
|
|
|
+ match = (val == *addr) ^ pred->not;
|
|
|
+
|
|
|
+ return match;
|
|
|
+}
|
|
|
+
|
|
|
+static int filter_pred_16(struct filter_pred *pred, void *event)
|
|
|
+{
|
|
|
+ u16 *addr = (u16 *)(event + pred->offset);
|
|
|
+ u16 val = (u16)pred->val;
|
|
|
+ int match;
|
|
|
+
|
|
|
+ match = (val == *addr) ^ pred->not;
|
|
|
+
|
|
|
+ return match;
|
|
|
+}
|
|
|
+
|
|
|
+static int filter_pred_8(struct filter_pred *pred, void *event)
|
|
|
+{
|
|
|
+ u8 *addr = (u8 *)(event + pred->offset);
|
|
|
+ u8 val = (u8)pred->val;
|
|
|
+ int match;
|
|
|
+
|
|
|
+ match = (val == *addr) ^ pred->not;
|
|
|
+
|
|
|
+ return match;
|
|
|
+}
|
|
|
+
|
|
|
+static int filter_pred_string(struct filter_pred *pred, void *event)
|
|
|
+{
|
|
|
+ char *addr = (char *)(event + pred->offset);
|
|
|
+ int cmp, match;
|
|
|
+
|
|
|
+ cmp = strncmp(addr, pred->str_val, pred->str_len);
|
|
|
+
|
|
|
+ match = (!cmp) ^ pred->not;
|
|
|
+
|
|
|
+ return match;
|
|
|
+}
|
|
|
+
|
|
|
+/* return 1 if event matches, 0 otherwise (discard) */
|
|
|
+int filter_match_preds(struct ftrace_event_call *call, void *rec)
|
|
|
+{
|
|
|
+ int i, matched, and_failed = 0;
|
|
|
+ struct filter_pred *pred;
|
|
|
+
|
|
|
+ for (i = 0; i < MAX_FILTER_PRED; i++) {
|
|
|
+ if (call->preds[i]) {
|
|
|
+ pred = call->preds[i];
|
|
|
+ if (and_failed && !pred->or)
|
|
|
+ continue;
|
|
|
+ matched = pred->fn(pred, rec);
|
|
|
+ if (!matched && !pred->or) {
|
|
|
+ and_failed = 1;
|
|
|
+ continue;
|
|
|
+ } else if (matched && pred->or)
|
|
|
+ return 1;
|
|
|
+ } else
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (and_failed)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+int filter_print_preds(struct filter_pred **preds, char *buf)
|
|
|
+{
|
|
|
+ ssize_t this_len = 0;
|
|
|
+ char *field_name;
|
|
|
+ struct filter_pred *pred;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (!preds) {
|
|
|
+ this_len += sprintf(buf + this_len, "none\n");
|
|
|
+ return this_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < MAX_FILTER_PRED; i++) {
|
|
|
+ if (preds[i]) {
|
|
|
+ pred = preds[i];
|
|
|
+ field_name = pred->field_name;
|
|
|
+ if (i)
|
|
|
+ this_len += sprintf(buf + this_len,
|
|
|
+ pred->or ? "|| " : "&& ");
|
|
|
+ this_len += sprintf(buf + this_len,
|
|
|
+ "%s ", field_name);
|
|
|
+ this_len += sprintf(buf + this_len,
|
|
|
+ pred->not ? "!= " : "== ");
|
|
|
+ if (pred->str_val)
|
|
|
+ this_len += sprintf(buf + this_len,
|
|
|
+ "%s\n", pred->str_val);
|
|
|
+ else
|
|
|
+ this_len += sprintf(buf + this_len,
|
|
|
+ "%llu\n", pred->val);
|
|
|
+ } else
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return this_len;
|
|
|
+}
|
|
|
+
|
|
|
+static struct ftrace_event_field *
|
|
|
+find_event_field(struct ftrace_event_call *call, char *name)
|
|
|
+{
|
|
|
+ struct ftrace_event_field *field;
|
|
|
+ struct list_head *entry, *tmp;
|
|
|
+
|
|
|
+ list_for_each_safe(entry, tmp, &call->fields) {
|
|
|
+ field = list_entry(entry, struct ftrace_event_field, link);
|
|
|
+ if (!strcmp(field->name, name))
|
|
|
+ return field;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+void filter_free_pred(struct filter_pred *pred)
|
|
|
+{
|
|
|
+ if (!pred)
|
|
|
+ return;
|
|
|
+
|
|
|
+ kfree(pred->field_name);
|
|
|
+ kfree(pred->str_val);
|
|
|
+ kfree(pred);
|
|
|
+}
|
|
|
+
|
|
|
+void filter_free_preds(struct ftrace_event_call *call)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (call->preds) {
|
|
|
+ for (i = 0; i < MAX_FILTER_PRED; i++)
|
|
|
+ filter_free_pred(call->preds[i]);
|
|
|
+ kfree(call->preds);
|
|
|
+ call->preds = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int __filter_add_pred(struct ftrace_event_call *call,
|
|
|
+ struct filter_pred *pred)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (call->preds && !pred->compound)
|
|
|
+ filter_free_preds(call);
|
|
|
+
|
|
|
+ if (!call->preds) {
|
|
|
+ call->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!call->preds)
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < MAX_FILTER_PRED; i++) {
|
|
|
+ if (!call->preds[i]) {
|
|
|
+ call->preds[i] = pred;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return -ENOMEM;
|
|
|
+}
|
|
|
+
|
|
|
+static int is_string_field(const char *type)
|
|
|
+{
|
|
|
+ if (strchr(type, '[') && strstr(type, "char"))
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred)
|
|
|
+{
|
|
|
+ struct ftrace_event_field *field;
|
|
|
+
|
|
|
+ field = find_event_field(call, pred->field_name);
|
|
|
+ if (!field)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ pred->offset = field->offset;
|
|
|
+
|
|
|
+ if (is_string_field(field->type)) {
|
|
|
+ pred->fn = filter_pred_string;
|
|
|
+ pred->str_len = field->size;
|
|
|
+ return __filter_add_pred(call, pred);
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (field->size) {
|
|
|
+ case 8:
|
|
|
+ pred->fn = filter_pred_64;
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ pred->fn = filter_pred_32;
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ pred->fn = filter_pred_16;
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ pred->fn = filter_pred_8;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return __filter_add_pred(call, pred);
|
|
|
+}
|
|
|
+
|
|
|
+int filter_parse(char **pbuf, struct filter_pred *pred)
|
|
|
+{
|
|
|
+ char *tmp, *tok, *val_str = NULL;
|
|
|
+ int tok_n = 0;
|
|
|
+
|
|
|
+ /* field ==/!= number, or/and field ==/!= number, number */
|
|
|
+ while ((tok = strsep(pbuf, " \n"))) {
|
|
|
+ if (tok_n == 0) {
|
|
|
+ if (!strcmp(tok, "0")) {
|
|
|
+ pred->clear = 1;
|
|
|
+ return 0;
|
|
|
+ } else if (!strcmp(tok, "&&")) {
|
|
|
+ pred->or = 0;
|
|
|
+ pred->compound = 1;
|
|
|
+ } else if (!strcmp(tok, "||")) {
|
|
|
+ pred->or = 1;
|
|
|
+ pred->compound = 1;
|
|
|
+ } else
|
|
|
+ pred->field_name = tok;
|
|
|
+ tok_n = 1;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (tok_n == 1) {
|
|
|
+ if (!pred->field_name)
|
|
|
+ pred->field_name = tok;
|
|
|
+ else if (!strcmp(tok, "!="))
|
|
|
+ pred->not = 1;
|
|
|
+ else if (!strcmp(tok, "=="))
|
|
|
+ pred->not = 0;
|
|
|
+ else {
|
|
|
+ pred->field_name = NULL;
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ tok_n = 2;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (tok_n == 2) {
|
|
|
+ if (pred->compound) {
|
|
|
+ if (!strcmp(tok, "!="))
|
|
|
+ pred->not = 1;
|
|
|
+ else if (!strcmp(tok, "=="))
|
|
|
+ pred->not = 0;
|
|
|
+ else {
|
|
|
+ pred->field_name = NULL;
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ val_str = tok;
|
|
|
+ break; /* done */
|
|
|
+ }
|
|
|
+ tok_n = 3;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (tok_n == 3) {
|
|
|
+ val_str = tok;
|
|
|
+ break; /* done */
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pred->field_name = kstrdup(pred->field_name, GFP_KERNEL);
|
|
|
+ if (!pred->field_name)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ pred->val = simple_strtoull(val_str, &tmp, 10);
|
|
|
+ if (tmp == val_str) {
|
|
|
+ pred->str_val = kstrdup(val_str, GFP_KERNEL);
|
|
|
+ if (!pred->str_val)
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|