[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <1235725256.8512.88.camel@charm-linux>
Date: Fri, 27 Feb 2009 03:00:56 -0600
From: Tom Zanussi <tzanussi@...il.com>
To: linux-kernel <linux-kernel@...r.kernel.org>
Subject: [PATCH 2/4] zedtrace generic kernel filtering
Add generic kernel filtering.
Signed-off-by: Tom Zanussi <tzanussi@...il.com>
---
kernel/trace/trace_binary/Makefile | 2 +-
kernel/trace/trace_binary/zed.c | 103 +++++++++--
kernel/trace/trace_binary/zed.h | 15 ++
kernel/trace/trace_binary/zed_filter.c | 301 ++++++++++++++++++++++++++++++++
kernel/trace/trace_binary/zed_filter.h | 45 +++++
kernel/trace/trace_binary/zed_sched.c | 4 +
6 files changed, 451 insertions(+), 19 deletions(-)
create mode 100644 kernel/trace/trace_binary/zed_filter.c
create mode 100644 kernel/trace/trace_binary/zed_filter.h
diff --git a/kernel/trace/trace_binary/Makefile b/kernel/trace/trace_binary/Makefile
index 558e17a..26b3c8e 100644
--- a/kernel/trace/trace_binary/Makefile
+++ b/kernel/trace/trace_binary/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_ZEDTRACE) += zedtrace.o
-zedtrace-objs := zed.o zed_sched.o
+zedtrace-objs := zed.o zed_filter.o zed_sched.o
diff --git a/kernel/trace/trace_binary/zed.c b/kernel/trace/trace_binary/zed.c
index 171d2e5..13d301e 100644
--- a/kernel/trace/trace_binary/zed.c
+++ b/kernel/trace/trace_binary/zed.c
@@ -25,6 +25,7 @@
#include <linux/debugfs.h>
#include <trace/sched.h>
#include "zed.h"
+#include "zed_filter.h"
/* globals */
@@ -118,6 +119,7 @@ static ssize_t zed_event_desc_read(struct file *filp, char __user *buffer,
field->name, field->type, field->offset,
field->size);
}
+ length += print_preds(event, page, length);
if (length >= 0)
length = simple_read_from_buffer(buffer, count, ppos, page,
@@ -133,8 +135,10 @@ static ssize_t zed_event_desc_write(struct file *filp,
size_t count, loff_t *ppos)
{
struct zed_event *event = filp->private_data;
- char buf[32], *tmp;
- int enable;
+ char buf[32], *pbuf = buf;
+ char *field_name = NULL;
+ int compound = 0, or = 0, not = 0, enable;
+ unsigned long long val;
if (count > sizeof(buf))
return -EINVAL;
@@ -144,14 +148,22 @@ static ssize_t zed_event_desc_write(struct file *filp,
if (copy_from_user(buf, buffer, count))
return -EFAULT;
- enable = simple_strtol(buf, &tmp, 10);
- if (tmp == buf)
+ enable = parse_filter_enable(&pbuf, ¬, &or, &compound,
+ &field_name, &val);
+ if (enable < 0)
return -EINVAL;
- if (enable)
- *event->trace_flags = 1;
- else
+ if (enable == 1) {
*event->trace_flags = 0;
+ zed_free_preds(event);
+ return count;
+ } else if (enable == 2) {
+ *event->trace_flags = 1;
+ return count;
+ }
+
+ if (zed_add_pred(event, field_name, val, not, or, !compound))
+ return -EINVAL;
return count;
}
@@ -192,8 +204,11 @@ static void subsys_enable_all(char *subsys, int enable)
event = zed_events[i];
if (!event)
break;
- if (!strcmp(event->subsys_name, subsys))
+ if (!strcmp(event->subsys_name, subsys)) {
*event->trace_flags = enable;
+ if (!enable)
+ zed_free_preds(event);
+ }
}
}
@@ -207,6 +222,40 @@ static void enable_all(int enable)
if (!event)
break;
*event->trace_flags = enable;
+ if (!enable)
+ zed_free_preds(event);
+ }
+}
+
+static void subsys_pred_all(char *subsys, char *field_name,
+ unsigned long long val, int not, int or,
+ int compound)
+{
+ struct zed_event *event;
+ int i;
+
+ for (i = 0; i < MAX_EVENTS; i++) {
+ event = zed_events[i];
+ if (!event)
+ break;
+ if (!strcmp(event->subsys_name, subsys))
+ zed_add_pred(event, field_name, val, not, or,
+ !compound);
+ }
+}
+
+static void pred_all(char *field_name,
+ unsigned long long val, int not, int or,
+ int compound)
+{
+ struct zed_event *event;
+ int i;
+
+ for (i = 0; i < MAX_EVENTS; i++) {
+ event = zed_events[i];
+ if (!event)
+ break;
+ zed_add_pred(event, field_name, val, not, or, !compound);
}
}
@@ -221,8 +270,10 @@ static ssize_t all_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
char *subsys = filp->private_data;
- char buf[32], *tmp;
- int enable;
+ char buf[32], *pbuf = buf;
+ char *field_name = NULL;
+ int compound = 0, or = 0, not = 0, enable;
+ unsigned long long val;
if (count > sizeof(buf))
return -EINVAL;
@@ -232,22 +283,31 @@ static ssize_t all_write(struct file *filp, const char __user *buffer,
if (copy_from_user(buf, buffer, count))
return -EFAULT;
- enable = simple_strtol(buf, &tmp, 10);
- if (tmp == buf)
+ enable = parse_filter_enable(&pbuf, ¬, &or, &compound,
+ &field_name, &val);
+
+ if (enable < 0)
return -EINVAL;
- if (enable) {
- if (subsys)
- subsys_enable_all(subsys, 1);
- else
- enable_all(1);
- } else {
+ if (enable == 1) {
if (subsys)
subsys_enable_all(subsys, 0);
else
enable_all(0);
+ return count;
+ } else if (enable == 2) {
+ if (subsys)
+ subsys_enable_all(subsys, 1);
+ else
+ enable_all(1);
+ return count;
}
+ if (subsys)
+ subsys_pred_all(subsys, field_name, val, not, or, compound);
+ else
+ pred_all(field_name, val, not, or, compound);
+
return count;
}
@@ -318,6 +378,11 @@ static int remove_subsys(char *name)
return 0;
}
+void zed_set_core_event(enum zed_event_id event_id)
+{
+ zed_events[event_id]->core = 1;
+}
+
/**
* register_zed_event() - create an event description file for an event
*/
@@ -348,6 +413,8 @@ static struct zed_event *register_zed_event(char *subsys_name,
event->size = event_size;
event->trace_flags = trace_flags;
event->subsys_name = subsys->name;
+ event->preds = NULL;
+ event->core = 0;
register_zed_field(event, "magic", "u32",
offsetof(struct zed_header, magic),
diff --git a/kernel/trace/trace_binary/zed.h b/kernel/trace/trace_binary/zed.h
index 3cbef4b..56a6295 100644
--- a/kernel/trace/trace_binary/zed.h
+++ b/kernel/trace/trace_binary/zed.h
@@ -100,6 +100,8 @@ struct zed_event {
int size;
int *trace_flags;
struct list_head field_list;
+ struct zed_pred **preds;
+ int core;
};
/*
@@ -118,6 +120,8 @@ extern struct zed_event *zed_events[MAX_EVENTS];
extern u32 *zed_trace_sequence;
extern unsigned int zed_tracing;
+#include "zed_filter.h"
+
/*
* event definition macros
*/
@@ -169,6 +173,8 @@ ID_EVENTS(EVENTS(EVENT_ID, COMMA));
EVENTS(DECLARE_EVENT, SEMICOLON);
+extern void zed_set_core_event(enum zed_event_id event_id);
+
/*
* Macros to generate boilerplate tracepoint entry/exit code.
*/
@@ -208,9 +214,18 @@ EVENTS(DECLARE_EVENT, SEMICOLON);
#define ZED_TRACEPOINT_EXIT(name) \
exit: \
+ if (zed_events[name##_trace]->preds) \
+ if (!zed_match_preds(zed_events[name##_trace], zed_event)) \
+ zed_unreserve(name##_trace, var_len); \
local_irq_restore(zed_flags); \
} \
+static inline void zed_unreserve(enum zed_event_id event_id, int var_len)
+{
+ struct rchan_buf *buf = zed_channel->buf[smp_processor_id()];
+ buf->offset -= zed_events[event_id]->size + var_len;
+}
+
/**
* zed_reserve() - reserve a slot large enough for event_id
* @event_id: auto-generated event id (see documentation)
diff --git a/kernel/trace/trace_binary/zed_filter.c b/kernel/trace/trace_binary/zed_filter.c
new file mode 100644
index 0000000..f467bf0
--- /dev/null
+++ b/kernel/trace/trace_binary/zed_filter.c
@@ -0,0 +1,301 @@
+/*
+ * zed_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@...il.com>
+ */
+
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/jhash.h>
+#include <linux/vmalloc.h>
+#include <linux/debugfs.h>
+#include <trace/sched.h>
+#include <trace/block.h>
+#include "zed.h"
+
+static int zed_pred_64(struct zed_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 zed_pred_32(struct zed_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 zed_pred_16(struct zed_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 zed_pred_8(struct zed_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 char *field_with_offset(struct zed_event *event, int offset)
+{
+ struct zed_field *field;
+ struct list_head *entry, *tmp;
+
+ list_for_each_safe(entry, tmp, &event->field_list) {
+ field = list_entry(entry, struct zed_field, link);
+ if (field->offset == offset)
+ return field->name;
+ }
+ return NULL;
+}
+
+/**
+ * zed_match_preds() - return 1 if event matches, 0 otherwise (discard)
+ */
+int zed_match_preds(struct zed_event *event, void *rec)
+{
+ struct zed_pred *pred;
+ int i, matched;
+
+ for (i = 0; i < MAX_PRED; i++) {
+ if (event->preds[i]) {
+ pred = event->preds[i];
+ matched = pred->fn(pred, rec);
+ if (!matched && !pred->or)
+ return 0;
+ if (matched && pred->or)
+ return 1;
+ } else
+ break;
+ }
+
+ return 1;
+}
+
+ssize_t print_preds(struct zed_event *event, char *page, ssize_t length)
+{
+ ssize_t this_len = 0;
+ char *field_name;
+ struct zed_pred *pred;
+ int i;
+
+ this_len += sprintf(page + length + this_len, "filters:\n\t");
+ if (!event->preds) {
+ this_len += sprintf(page + length + this_len, "none\n");
+ return this_len;
+ }
+
+ for (i = 0; i < MAX_PRED; i++) {
+ if (event->preds[i]) {
+ pred = event->preds[i];
+ field_name = field_with_offset(event, pred->offset);
+ if (i)
+ this_len += sprintf(page + length + this_len,
+ pred->or ? "\tor " : "\tand ");
+ this_len += sprintf(page + length + this_len,
+ "%s ", field_name);
+ this_len += sprintf(page + length + this_len,
+ pred->not ? "!= " : "== ");
+ this_len += sprintf(page + length + this_len,
+ "%llu\n", pred->val);
+ } else
+ break;
+ }
+
+ return this_len;
+}
+
+void zed_free_preds(struct zed_event *event)
+{
+ if (event->preds) {
+ int i;
+ for (i = 0; i < MAX_PRED; i++) {
+ kfree(event->preds[i]);
+ event->preds[i] = NULL;
+ }
+ kfree(event->preds);
+ event->preds = NULL;
+ }
+}
+
+static int __zed_add_pred(struct zed_event *event,
+ zed_pred_t fn, u64 val, int offset,
+ int not, int or, int clear)
+{
+ int i;
+ struct zed_pred *pred = kmalloc(sizeof(*pred), GFP_KERNEL);
+
+ if (!pred)
+ return -ENOMEM;
+
+ pred->fn = fn;
+ pred->val = val;
+ pred->offset = offset;
+ pred->not = not;
+ pred->or = or;
+
+ if (event->preds && clear)
+ zed_free_preds(event);
+
+ if (!event->preds) {
+ event->preds = kzalloc(MAX_PRED * sizeof(pred), GFP_KERNEL);
+ if (!event->preds)
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < MAX_PRED; i++) {
+ if (!event->preds[i]) {
+ event->preds[i] = pred;
+ return 0;
+ }
+ }
+ return -ENOMEM;
+}
+
+static struct zed_field *find_zed_field(struct zed_event *event,
+ char *field_name)
+{
+ struct zed_field *field;
+ struct list_head *entry, *tmp;
+
+ list_for_each_safe(entry, tmp, &event->field_list) {
+ field = list_entry(entry, struct zed_field, link);
+ if (!strcmp(field->name, field_name))
+ return field;
+ }
+ return NULL;
+}
+
+int zed_add_pred(struct zed_event *event, char *field_name,
+ u64 val, int not, int or, int clear)
+{
+ struct zed_field *field;
+ zed_pred_t fn;
+
+ if (event->core)
+ return -EINVAL;
+
+ field = find_zed_field(event, field_name);
+ if (!field)
+ return -EINVAL;
+
+ switch (field->size) {
+ case 8:
+ fn = zed_pred_64;
+ break;
+ case 4:
+ fn = zed_pred_32;
+ break;
+ case 2:
+ fn = zed_pred_16;
+ break;
+ case 1:
+ fn = zed_pred_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return __zed_add_pred(event, fn, val, field->offset, not, or, clear);
+}
+
+int parse_filter_enable(char **pbuf, int *not, int *or, int *compound,
+ char **field_name, unsigned long long *val)
+{
+ 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"))
+ return 1;
+ else if (!strcmp(tok, "1"))
+ return 2;
+ else if (!strcmp(tok, "and")) {
+ *or = 0;
+ *compound = 1;
+ } else if (!strcmp(tok, "or")) {
+ *or = 1;
+ *compound = 1;
+ } else
+ *field_name = tok;
+ tok_n = 1;
+ continue;
+ }
+ if (tok_n == 1) {
+ if (!*field_name)
+ *field_name = tok;
+ else if (!strcmp(tok, "!="))
+ *not = 1;
+ else if (!strcmp(tok, "=="))
+ *not = 0;
+ else
+ return -EINVAL;
+ tok_n = 2;
+ continue;
+ }
+ if (tok_n == 2) {
+ if (*compound) {
+ if (!strcmp(tok, "!="))
+ *not = 1;
+ else if (!strcmp(tok, "=="))
+ *not = 0;
+ else
+ return -EINVAL;
+ } else {
+ val_str = tok;
+ break; /* done */
+ }
+ tok_n = 3;
+ continue;
+ }
+ if (tok_n == 3) {
+ val_str = tok;
+ break; /* done */
+ }
+ }
+
+ *val = simple_strtoull(val_str, &tmp, 10);
+ if (tmp == val_str)
+ return -EINVAL;
+
+ return 0;
+}
+
+
diff --git a/kernel/trace/trace_binary/zed_filter.h b/kernel/trace/trace_binary/zed_filter.h
new file mode 100644
index 0000000..1942eb9
--- /dev/null
+++ b/kernel/trace/trace_binary/zed_filter.h
@@ -0,0 +1,45 @@
+/*
+ * zedtrace filter declarations
+ *
+ * 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@...il.com>
+ */
+
+#ifndef _ZED_FILTER_H
+#define _ZED_FILTER_H
+
+#define MAX_PRED 8
+
+typedef int (*zed_pred_t) (struct zed_pred *pred, void *event);
+
+struct zed_pred {
+ zed_pred_t fn;
+ u64 val;
+ int offset;
+ int not;
+ int or;
+};
+
+extern ssize_t print_preds(struct zed_event *event, char *page,
+ ssize_t length);
+extern int parse_filter_enable(char **pbuf, int *not, int *or, int *compound,
+ char **field_name, unsigned long long *val);
+extern int zed_add_pred(struct zed_event *event, char *field_name,
+ u64 val, int not, int or, int clear);
+extern void zed_free_preds(struct zed_event *event);
+extern int zed_match_preds(struct zed_event *event, void *rec);
+
+#endif /* _ZED_FILTER_H */
diff --git a/kernel/trace/trace_binary/zed_sched.c b/kernel/trace/trace_binary/zed_sched.c
index 8eba079..9d9bcc8 100644
--- a/kernel/trace/trace_binary/zed_sched.c
+++ b/kernel/trace/trace_binary/zed_sched.c
@@ -197,5 +197,9 @@ void zed_disable_sched_tracepoints(void)
void zed_sched_init(void)
{
+ zed_set_core_event(sched_switch_trace);
+ zed_set_core_event(sched_migrate_task_trace);
+ zed_set_core_event(sched_process_fork_trace);
+ zed_set_core_event(sched_process_exit_trace);
}
--
1.5.6.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists