Re: [Question] Hooks for scheduler tracing (CFS)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



 Hi Remy,

On Mon, 16 Jul 2007 21:46:46 +0200 "Remy Bohmer" <[email protected]> wrote:

> Hello,
> 
> I am looking for some tool/kernel machanism that enables me to track
> every schedule change on the CFS scheduler of the RT-kernel for some
> period of time.
> Thus a tool that gives me an overview after some time which task got
> "scheduled in/out" at "which timestamp" and at "which CPU". ( I need
> the raw data)
> 
> In a far past (on Montavista kernels) I used LTT for this to log for
> some time with only the SCHED_CHANGE filter and text output. But, I
> cannot find any LTT(ng) support for any RT-kernel, and support for the
> new CFS (like in 2.6.22.1-rt4) is even harder.
> 
> So I was wondering if anybody knows some tool/kernel mechanism which
> can do this?
> If not, I will build a kernel extension for it myself (new extension
> to 'latency_trace' ?)
> In that case can anybody with in depth CFS scheduler knowledge please
> point me which hooks are safe to use for this?
> 
> I need it to get a detailed insight in my RT-system with its RT and
> non-RT applications.
> Thus to know when a certain task gets scheduled (and to calculate its
> per thread min/max/avg latencies), which task preempts another task,
> and to get a complete overview of what the RT system (and scheduler)
> is doing during time etc.
> 
> 

  Have a look at Steven Rostedt's logdev, it's very lightweight
and does what you want. I'm currently using it doing the same
thing you want to achieve.

  http://rostedt.homelinux.com/logdev/

  I ported it over to 2.6.21.5-rt20 (patches attached). The way I set it up
is:

  - the patches apply in the following order:

	. relay-update.patch
	. logdev-0.5.4.patch
	. logdev-markers-0.5.4.patch
	. logdev-trace-ctx-switch.patch
	. logdev-trace-sys_clock_getres.patch

  - be sure to enable debugfs in the kernel

  - the userspace tool, you can find at http://rostedt.homelinux.com/logdev/
    I modified the dump_log.h and log_marker.c to suit my needs (attached) and
    match the logdev-trace-ctx-switch.patch and logdev-trace-sys_clock_getres.patch

  - start tracing with 'echo 1 > /mnt/debugfs/logdev/mark'
    stop tracing with 'echo 0 > /mnt/debugfs/logdev/mark'
    dump trace buffers with 'logdev/logread > dump.log'

  The clock_getres trace is for me to mark some transitions in my
test program so that I can discard non interesting traces.

  Hope this helps, feel free to ask for more details.

  Sébastien.









  Adds a relay_kernel_read() used by logdev_relay in logdev-0.5.4.patch.


---
 include/linux/relay.h |    4 +++
 kernel/relay.c        |   54 ++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 46 insertions(+), 12 deletions(-)

Index: linux-2.6.21.5-rt20/include/linux/relay.h
===================================================================
--- linux-2.6.21.5-rt20.orig/include/linux/relay.h	2007-07-11 10:09:49.000000000 +0200
+++ linux-2.6.21.5-rt20/include/linux/relay.h	2007-07-11 14:28:47.000000000 +0200
@@ -178,6 +178,10 @@ extern int relay_buf_full(struct rchan_b
 
 extern size_t relay_switch_subbuf(struct rchan_buf *buf,
 				  size_t length);
+extern ssize_t relay_kernel_read(char *buffer,
+				 size_t count,
+				 loff_t *ppos,
+				 struct rchan_buf *kbuf);
 
 /**
  *	relay_write - write data into the channel
Index: linux-2.6.21.5-rt20/kernel/relay.c
===================================================================
--- linux-2.6.21.5-rt20.orig/kernel/relay.c	2007-07-11 10:09:49.000000000 +0200
+++ linux-2.6.21.5-rt20/kernel/relay.c	2007-07-11 14:28:47.000000000 +0200
@@ -946,14 +946,21 @@ static int subbuf_read_actor(size_t read
 			     struct rchan_buf *buf,
 			     size_t avail,
 			     read_descriptor_t *desc,
-			     read_actor_t actor)
+			     read_actor_t actor,
+			     int kernel_reader)
 {
 	void *from;
 	int ret = 0;
+	int err = 0;
 
 	from = buf->start + read_start;
 	ret = avail;
-	if (copy_to_user(desc->arg.buf, from, avail)) {
+
+	if (kernel_reader)
+		memcpy(desc->arg.buf, from, avail);
+	else
+		err = copy_to_user(desc->arg.buf, from, avail);
+	if (err) {
 		desc->error = -EFAULT;
 		ret = 0;
 	}
@@ -971,7 +978,8 @@ static int subbuf_send_actor(size_t read
 			     struct rchan_buf *buf,
 			     size_t avail,
 			     read_descriptor_t *desc,
-			     read_actor_t actor)
+			     read_actor_t actor,
+			     int kernel_reader)
 {
 	unsigned long pidx, poff;
 	unsigned int subbuf_pages;
@@ -1005,7 +1013,8 @@ typedef int (*subbuf_actor_t) (size_t re
 			       struct rchan_buf *buf,
 			       size_t avail,
 			       read_descriptor_t *desc,
-			       read_actor_t actor);
+			       read_actor_t actor,
+			       int kernel_reader);
 
 /*
  *	relay_file_read_subbufs - read count bytes, bridging subbuf boundaries
@@ -1013,16 +1022,21 @@ typedef int (*subbuf_actor_t) (size_t re
 static ssize_t relay_file_read_subbufs(struct file *filp, loff_t *ppos,
 					subbuf_actor_t subbuf_actor,
 					read_actor_t actor,
-					read_descriptor_t *desc)
+					read_descriptor_t *desc,
+					struct rchan_buf *kbuf)
 {
-	struct rchan_buf *buf = filp->private_data;
+	struct rchan_buf *buf;
 	size_t read_start, avail;
-	int ret;
+	int ret, kernel_reader;
+
+	buf = kbuf ? kbuf : filp->private_data;
+	kernel_reader = filp ? 0 : 1;
 
 	if (!desc->count)
 		return 0;
 
-	mutex_lock(&filp->f_path.dentry->d_inode->i_mutex);
+	if (filp)
+		mutex_lock(&filp->f_path.dentry->d_inode->i_mutex);
 	do {
 		if (!relay_file_read_avail(buf, *ppos))
 			break;
@@ -1033,7 +1047,7 @@ static ssize_t relay_file_read_subbufs(s
 			break;
 
 		avail = min(desc->count, avail);
-		ret = subbuf_actor(read_start, buf, avail, desc, actor);
+		ret = subbuf_actor(read_start, buf, avail, desc, actor, kernel_reader);
 		if (desc->error < 0)
 			break;
 
@@ -1042,7 +1056,8 @@ static ssize_t relay_file_read_subbufs(s
 			*ppos = relay_file_read_end_pos(buf, read_start, ret);
 		}
 	} while (desc->count && ret);
-	mutex_unlock(&filp->f_path.dentry->d_inode->i_mutex);
+	if (filp)
+		mutex_unlock(&filp->f_path.dentry->d_inode->i_mutex);
 
 	return desc->written;
 }
@@ -1058,8 +1073,23 @@ static ssize_t relay_file_read(struct fi
 	desc.arg.buf = buffer;
 	desc.error = 0;
 	return relay_file_read_subbufs(filp, ppos, subbuf_read_actor,
-				       NULL, &desc);
+				       NULL, &desc, NULL);
+}
+
+ssize_t relay_kernel_read(char *buffer,
+			  size_t count,
+			  loff_t *ppos,
+			  struct rchan_buf *kbuf)
+{
+	read_descriptor_t desc;
+	desc.written = 0;
+	desc.count = count;
+	desc.arg.buf = buffer;
+	desc.error = 0;
+	return relay_file_read_subbufs(NULL, ppos, subbuf_read_actor,
+				       NULL, &desc, kbuf);
 }
+EXPORT_SYMBOL_GPL(relay_kernel_read);
 
 static ssize_t relay_file_sendfile(struct file *filp,
 				   loff_t *ppos,
@@ -1073,7 +1103,7 @@ static ssize_t relay_file_sendfile(struc
 	desc.arg.data = target;
 	desc.error = 0;
 	return relay_file_read_subbufs(filp, ppos, subbuf_send_actor,
-				       actor, &desc);
+				       actor, &desc, NULL);
 }
 
 const struct file_operations relay_file_operations = {
---
 arch/i386/kernel/traps.c       |    2 
 arch/i386/mm/fault.c           |    3 
 drivers/char/sysrq.c           |   40 +
 include/linux/logdev.h         |  193 ++++++
 kernel/Makefile                |    1 
 kernel/logdev/Makefile         |    7 
 kernel/logdev/logdev.c         | 1269 +++++++++++++++++++++++++++++++++++++++
 kernel/logdev/logdev_priv.h    |   52 +
 kernel/logdev/logdev_probe.c   | 1310 +++++++++++++++++++++++++++++++++++++++++
 kernel/logdev/logdev_relay.c   |  175 +++++
 kernel/logdev/logdev_ringbuf.c |  482 +++++++++++++++
 kernel/logdev/logdev_ringbuf.h |  158 ++++
 lib/Kconfig.debug              |   70 ++
 13 files changed, 3759 insertions(+), 3 deletions(-)

Index: linux-2.6.21.5-rt20/include/linux/logdev.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21.5-rt20/include/linux/logdev.h	2007-07-11 14:28:50.000000000 +0200
@@ -0,0 +1,193 @@
+/*
+ * Logdevice - A device used to record debuging information in the kernel.
+ *             It uses a large memory ring buffer consisting of individual pages
+ *             to keep down on hogging large sections. A user may then read the device
+ *             to get debugging information out of it.  Or if configured, this can
+ *             be dumped to the network on a system crash.
+ *
+ * Copyright - 2005 - Steven Rostedt, Kihon Technologies, (rostedt at kihontech dot com)
+ */
+#ifndef _LOG_DEV_H
+#define _LOG_DEV_H
+
+#include <linux/sched.h>
+
+struct logdev_switch_struct {
+	unsigned long long t;
+	short pid_prev;
+	short pid_next;
+	int prev_prio;
+	int prev_static_prio;
+	int prev_normal_prio;
+	int prev_state;
+	int next_prio;
+	int next_static_prio;
+	int next_normal_prio;
+	char prev_comm[TASK_COMM_LEN];
+	char next_comm[TASK_COMM_LEN];
+};
+
+struct logdev_print {
+	char str[0];
+};
+
+struct logdev_print_time {
+	unsigned long long t;
+	char str[0];
+};
+
+struct logdev_print_time_func {
+	const char *file;
+	int line;
+	/* need to be after line, since we use this with print_time. */
+	unsigned long long t;
+	char str[0];
+};
+
+struct logdev_custom {
+	int id;
+	char data[0];
+};
+
+struct logdev_header {
+	long counter;
+	int id;
+	int size;
+};
+
+struct logdev_item {
+	struct logdev_header hdr;
+	union {
+		struct logdev_switch_struct sw;
+		struct logdev_print print;
+		struct logdev_print_time print_time;
+		struct logdev_print_time_func print_time_func;
+		struct logdev_custom custom;
+		char data[0];
+	} u;
+};
+
+#ifdef CONFIG_LOGDEV
+
+extern int in_logdump;
+
+/*
+ * Right now we only have one switch, but this may change
+ * in the future.
+ */
+extern unsigned long logdev_switches;
+#define LOGDEV_SW_PRINT_ENABLED		0
+#define LOGDEV_SW_SWITCH_ENABLED	1
+#define LOGDEV_SW_MARKER_ENABLED	2
+#define LOGDEV_SW_BITS			3
+
+#define LOGDEV_SW_ISSET(sw) (test_bit(sw, &logdev_switches))
+#define LOGDEV_SW_SET(sw) (set_bit(sw, &logdev_switches))
+#define LOGDEV_SW_CLEAR(sw) (clear_bit(sw, &logdev_switches))
+
+#define logdev_print_ison() LOGDEV_SW_ISSET(LOGDEV_SW_PRINT_ENABLED)
+#define logdev_print_on() LOGDEV_SW_SET(LOGDEV_SW_PRINT_ENABLED);
+#define logdev_print_off() LOGDEV_SW_CLEAR(LOGDEV_SW_PRINT_ENABLED);
+
+#define logdev_switch_ison() LOGDEV_SW_ISSET(LOGDEV_SW_SWITCH_ENABLED)
+#define logdev_switch_on() LOGDEV_SW_SET(LOGDEV_SW_SWITCH_ENABLED);
+#define logdev_switch_off() LOGDEV_SW_CLEAR(LOGDEV_SW_SWITCH_ENABLED);
+
+#define logdev_mark_ison() LOGDEV_SW_ISSET(LOGDEV_SW_MARKER_ENABLED)
+#define logdev_mark_on() LOGDEV_SW_SET(LOGDEV_SW_MARKER_ENABLED);
+#define logdev_mark_off() LOGDEV_SW_CLEAR(LOGDEV_SW_MARKER_ENABLED);
+
+typedef void (*logdev_callback_func)(struct logdev_header *hdr,
+				     struct logdev_custom *custom,
+				     int cpu,
+				     void *rec);
+
+int logdev_print(const char *str, ...)
+	__attribute__ ((format (printf, 1, 2)));
+int logdev_vprint(const char *str, va_list va);
+int logdev_print_time(const char *str, ...)
+	__attribute__ ((format (printf, 1, 2)));
+int logdev_print_time_func(const char *file, int line, const char *str, ...)
+	__attribute__ ((format (printf, 3, 4)));
+
+/*
+ * logdev_record is used for custom writes (saves of sprintf)
+ * use id and register a callback so that the logdump knows
+ * what to do when it sees this record.
+ * You can pass in multiple data structures, just end the function
+ * parameters with a NULL.
+ */
+int logdev_record(int id, int total_size, const void *data, int size, ...);
+void logdev_dump(void);
+void logdev_record_switch(struct task_struct *prev, struct task_struct *next);
+
+#define LOGDEV(x,y...) logdev_##x(y)
+#define LOGPRINTS(func,x...) do { if (logdev_print_ison()) LOGDEV(func,x); } while(0)
+
+/*
+ * Using the UPPER case here ignores the logdev_print_enabled flag
+ */
+#define LOGPRINT(x...) LOGDEV(print,x)
+#define LOGTPRINT(x...) LOGDEV(print_time,x)
+#define LOGTFPRINT(x...) LOGDEV(print_time_func,__FUNCTION__,__LINE__,x)
+
+/*
+ * Using these functions, will only log if logdev_print_enabled flag is set.
+ */
+
+#define lprint(x...) LOGPRINTS(print,x)
+#define ltprint(x...) LOGPRINTS(print_time,x)
+#define lfprint(x...) LOGPRINTS(print_time_func,__FUNCTION__,__LINE__,x)
+
+/*
+ * lfnprint is identical to lfprint except that it adds a new line at the end.
+ */
+#define _lnprint(func,x,y...) func( x "%s\n", y)
+#define lnprint(x...) _lnprint(lprint,x,"")
+#define ltnprint(x...) _lnprint(ltprint,x,"")
+#define lfnprint(x...) _lnprint(lfprint,x,"")
+
+struct logdev_callback {
+	struct list_head list;
+	int id;
+	logdev_callback_func func;
+};
+
+int logdev_register_callback(int custom_id, logdev_callback_func func);
+int logdev_unregister_callback(int custom_id);
+
+int logdev_init(void); /* If we want to put this in main.c */
+
+#else /* !LOGDEV */
+#define logdev_dump() do {} while(0)
+#define logdev_record(id, size, data) do {} while(0)
+#define logdev_record_switch(prev, next) do {} while(0)
+
+#define LOGPRINT(x...) do {} while(0)
+#define LOGTPRINT(x...) do {} while(0)
+#define LOGTFPRINT(x...) do {} while(0)
+
+#define logdev_print_ison() ( 0 )
+#define logdev_print_on() do {} while(0)
+#define logdev_print_off() do {} while(0)
+
+#define logdev_switch_ison() ( 0 )
+#define logdev_switch_on() do {} while(0)
+#define logdev_switch_off() do {} while(0)
+
+#define logdev_print(x...) do {} while(0)
+
+#define lprint(x...) do {} while(0)
+#define ltprint(x...) do {} while(0)
+#define lfprint(x...) do {} while(0)
+
+#define lnprint(x...) do {} while(0)
+#define ltnprint(x...) do {} while(0)
+#define lfnprint(x...) do {} while(0)
+
+#define logdev_register_callback(i,f) do {} while(0)
+#define logdev_unregister_callback(i) do {} while(0)
+#define in_logdump 0
+#endif /* LOGDEV */
+
+#endif
Index: linux-2.6.21.5-rt20/lib/Kconfig.debug
===================================================================
--- linux-2.6.21.5-rt20.orig/lib/Kconfig.debug	2007-07-11 10:09:49.000000000 +0200
+++ linux-2.6.21.5-rt20/lib/Kconfig.debug	2007-07-11 14:28:50.000000000 +0200
@@ -608,6 +608,76 @@ config FORCED_INLINING
 	  become the default in the future, until then this option is there to
 	  test gcc for this.
 
+config LOGDEV
+	bool "Enable logdev device"
+	depends on DEBUG_KERNEL
+	help
+	  The logdev device stores data into the kernel that can be retrieved
+	  later through a misc device (major 10).  The minor number is
+	  dynamic and is posted through /proc/logdev/minor. Utilities
+	  to open and read the device can be found at
+	  http://rostedt.homelinux.com/logdev
+
+	  This device allows for tracing lots of information in the kernel
+	  when simply printk is too expensive.  When the logdev is initialized,
+	  it allocates a default of 1 meg of memory (in page size units). This
+	  allows for saving data in a ring buffer without the need to allocate.
+
+config LOGDEV_PROBE
+	bool
+	depends on LOGDEV && KPROBES
+	default y
+
+choice
+	prompt "Logdev Backend"
+	depends on LOGDEV
+	default LOGDEV_RINGBUF
+
+config LOGDEV_RINGBUF
+	bool "Logdev internal ring buffer"
+	---help---
+	  Logdev needs a backend to store the data as it is logged.
+	  this is done in memory, to record without much digression
+	  in performance of machine.  With this option logdev creates
+	  its own simple memory ring buffer.  This allows it to record
+	  things really early in the boot process.
+
+config LOGDEV_RELAY
+	bool "Logdev with relayfs (BROKEN)"
+	---help---
+	  Logdev needs a backend to store the data as it is logged.
+	  this is done in memory, to record without much digression
+	  in performance of machine.  With this option logdev uses
+	  relayfs as its back end.  This makes it more standard to
+	  other things in the kernel, and for userspace.
+
+endchoice
+
+config LOGDEV_PAGES
+	int "Number of pages to allocate for logdev device"
+	depends on LOGDEV
+	default 256
+	help
+	  The Logdev device allocates a number of pages for the sole
+	  purpose of logging data.  This is the number of pages that
+	  the Logdev device should allocate upon loading / initializing.
+
+config LOGDEV_PRINT_ENABLED
+	bool "Default Logdev prints should be enabled on startup"
+	depends on LOGDEV
+	help
+	  Enable this if you expect the LOGPRINT macros to be enabled
+	  as soon as the logdev device is loaded. Otherwise you must
+	  enable it with /proc/logdev/print
+
+config LOGDEV_SWITCH_ENABLED
+	bool "Default Logdev printing of context switches on startup"
+	depends on LOGDEV
+	help
+	  Enable this if you expect the LOGSWITCH macros to be enabled
+	  as soon as the logdev device is loaded. Otherwise you must
+	  enable it with /proc/logdev/switch
+
 config RCU_TORTURE_TEST
 	tristate "torture tests for RCU"
 	depends on DEBUG_KERNEL
Index: linux-2.6.21.5-rt20/arch/i386/kernel/traps.c
===================================================================
--- linux-2.6.21.5-rt20.orig/arch/i386/kernel/traps.c	2007-07-11 10:09:49.000000000 +0200
+++ linux-2.6.21.5-rt20/arch/i386/kernel/traps.c	2007-07-11 14:28:50.000000000 +0200
@@ -31,6 +31,7 @@
 #include <linux/uaccess.h>
 #include <linux/nmi.h>
 #include <linux/bug.h>
+#include <linux/logdev.h>
 
 #ifdef CONFIG_EISA
 #include <linux/ioport.h>
@@ -790,6 +791,7 @@ void __kprobes die_nmi(struct pt_regs *r
 	printk(" on CPU%d, eip %08lx, registers:\n",
 		smp_processor_id(), regs->eip);
 	show_registers(regs);
+	logdev_dump();
 	console_silent();
 	spin_unlock(&nmi_print_lock);
 	bust_spinlocks(0);
Index: linux-2.6.21.5-rt20/drivers/char/sysrq.c
===================================================================
--- linux-2.6.21.5-rt20.orig/drivers/char/sysrq.c	2007-07-11 10:09:49.000000000 +0200
+++ linux-2.6.21.5-rt20/drivers/char/sysrq.c	2007-07-11 14:28:50.000000000 +0200
@@ -30,6 +30,7 @@
 #include <linux/suspend.h>
 #include <linux/writeback.h>
 #include <linux/buffer_head.h>		/* for fsync_bdev() */
+#include <linux/logdev.h>
 #include <linux/swap.h>
 #include <linux/spinlock.h>
 #include <linux/vt_kern.h>
@@ -181,6 +182,39 @@ static struct sysrq_key_op sysrq_mountro
 	.enable_mask	= SYSRQ_ENABLE_REMOUNT,
 };
 
+static void sysrq_handle_dumplog(int key, struct tty_struct *tty)
+{
+	logdev_dump();
+}
+
+static struct sysrq_key_op sysrq_dumplog_op = {
+	.handler	= sysrq_handle_dumplog,
+	.help_msg	= "Dumplog",
+	.action_msg	= "Dump logdev to serial",
+};
+
+static void sysrq_handle_togglelogswitch(int key, struct tty_struct *tty)
+{
+        /*
+         * Not so atomic, but we really don't care!
+         */
+        if (logdev_switch_ison()) {
+                logdev_switch_off();
+                printk("logdev_switch now off\n");
+        } else {
+                logdev_switch_on();
+                printk("logdev_switch now on\n");
+        }
+}
+
+static struct sysrq_key_op sysrq_togglelogdevswitch_op = {
+	.handler	= sysrq_handle_togglelogswitch,
+	.help_msg	= "Togglelogswitch",
+	.action_msg	= "Toggling logdev_switch",
+};
+
+/* END SYNC SYSRQ HANDLERS BLOCK */
+
 #ifdef CONFIG_LOCKDEP
 static void sysrq_handle_showlocks(int key, struct tty_struct *tty)
 {
@@ -350,9 +384,9 @@ static struct sysrq_key_op *sysrq_key_ta
 	&sysrq_crashdump_op,		/* c & ibm_emac driver debug */
 	&sysrq_showlocks_op,		/* d */
 	&sysrq_term_op,			/* e */
-	&sysrq_moom_op,			/* f */
+        &sysrq_togglelogdevswitch_op,	/* f */
 	/* g: May be registered by ppc for kgdb */
-	NULL,				/* g */
+        &sysrq_dumplog_op,		/* g */
 	NULL,				/* h */
 	&sysrq_kill_op,			/* i */
 	NULL,				/* j */
@@ -373,7 +407,7 @@ static struct sysrq_key_op *sysrq_key_ta
 	&sysrq_showstate_blocked_op,	/* w */
 	/* x: May be registered on ppc/powerpc for xmon */
 	NULL,				/* x */
-	NULL,				/* y */
+	&sysrq_moom_op,			/* y */
 	NULL				/* z */
 };
 
Index: linux-2.6.21.5-rt20/arch/i386/mm/fault.c
===================================================================
--- linux-2.6.21.5-rt20.orig/arch/i386/mm/fault.c	2007-07-11 10:09:49.000000000 +0200
+++ linux-2.6.21.5-rt20/arch/i386/mm/fault.c	2007-07-11 14:28:50.000000000 +0200
@@ -29,6 +29,7 @@
 #include <asm/kdebug.h>
 #include <asm/segment.h>
 
+#include <linux/logdev.h>
 extern void die(const char *,struct pt_regs *,long);
 
 static ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
@@ -519,6 +520,8 @@ no_context:
 	bust_spinlocks(1);
 
 	if (oops_may_print()) {
+		lfnprint("BUG!");
+		logdev_print_off();
 	#ifdef CONFIG_X86_PAE
 		if (error_code & 16) {
 			pte_t *pte = lookup_address(address);
Index: linux-2.6.21.5-rt20/kernel/Makefile
===================================================================
--- linux-2.6.21.5-rt20.orig/kernel/Makefile	2007-07-11 10:09:49.000000000 +0200
+++ linux-2.6.21.5-rt20/kernel/Makefile	2007-07-11 14:28:50.000000000 +0200
@@ -64,6 +64,7 @@ obj-$(CONFIG_SYSCTL) += utsname_sysctl.o
 obj-$(CONFIG_UTS_NS) += utsname.o
 obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o
 obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o
+obj-$(CONFIG_LOGDEV) += logdev/
 
 ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
 # According to Alan Modra <[email protected]>, the -fno-omit-frame-pointer is
Index: linux-2.6.21.5-rt20/kernel/logdev/Makefile
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21.5-rt20/kernel/logdev/Makefile	2007-07-11 14:28:50.000000000 +0200
@@ -0,0 +1,7 @@
+
+obj-y := logdev.o
+
+obj-$(CONFIG_LOGDEV_PROBE)	+= logdev_probe.o
+obj-$(CONFIG_LOGDEV_RINGBUF)	+= logdev_ringbuf.o
+obj-$(CONFIG_LOGDEV_RELAY)	+= logdev_relay.o
+
Index: linux-2.6.21.5-rt20/kernel/logdev/logdev.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21.5-rt20/kernel/logdev/logdev.c	2007-07-11 14:28:50.000000000 +0200
@@ -0,0 +1,1269 @@
+/*
+ * logdev.c
+ *
+ * Copyright (C) 2004-2006 Steven Rostedt, Kihon Technologies, Inc.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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; version 2 of the License (not later!)
+ *
+ * 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
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#undef CONFIG_CRITICAL_IRQSOFF_TIMING /* ignore this for this whole file! */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/kprobes.h>
+#include <linux/cpumask.h>
+#include <linux/notifier.h>
+#include <linux/nmi.h>
+#include <linux/delay.h>
+#include <linux/bootmem.h>
+#include <linux/debugfs.h>
+#include <linux/logdev.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/kdebug.h>
+
+#include "logdev_priv.h"
+
+static char *logdev_version = "0.5.4";
+
+/* used for BUG(), since we can't add any headers in that file */
+int get_processor_id(void) { return smp_processor_id(); }
+EXPORT_SYMBOL(get_processor_id);
+
+void log_show_eip(const char *func)
+{
+	lfnprint("(%s) from %p",func,__builtin_return_address(0));
+}
+
+/* bit mask of logdev settings (only one bit used so far) */
+unsigned long logdev_switches;
+EXPORT_SYMBOL_GPL(logdev_switches);
+
+/* Compares good for a difference of ~2 billion. */
+static inline int compare_cnt(unsigned long a, unsigned long b)
+{
+	long x = (long)a - (long)b;
+	return x > 0 ? 1 : x < 0 ? -1 : 0;
+}
+
+static atomic_t logdev_counter = ATOMIC_INIT(0);
+#define logdev_counter_inc() atomic_inc_return(&logdev_counter);
+
+DEFINE_PER_CPU(struct logdev_dev, logdev_dev);
+
+/*
+ * We don't support hotplug CPUS
+ */
+#define check_cpu(cpu) ({ \
+			static int once = 1;				\
+			int x;						\
+			if (unlikely(x = (cpu >= LOGDEV_CPUS)) && once) { \
+				once = 0;				\
+				printk("BUG %s:%d: cpu %d doesn't fit logdev cpus\n", \
+				       __FILE__, __LINE__, cpu);	\
+			}						\
+			x;						\
+		})
+
+int logdev_copy_from_dev(struct logdev_dev *dev, void *buf,
+			 int size);
+int logdev_copy_to_dev(struct logdev_dev *dev, const void *dat,
+		       int size);
+
+static int option_logdev_print(char *opt)
+{
+	logdev_print_on();
+	return 0;
+}
+
+static int option_logdev_switch(char *opt)
+{
+	logdev_switch_on();
+	return 0;
+}
+
+__setup("logdevprint", option_logdev_print);
+__setup("logdevswitch", option_logdev_switch);
+
+/*
+ * The following is to register call back functions to print out
+ * a custom record.
+ */
+
+DEFINE_SPINLOCK(logdev_callbacks_lock);
+
+LIST_HEAD(logdev_callbacks);
+EXPORT_SYMBOL_GPL(logdev_callbacks);
+
+int __kprobes logdev_register_callback(int custom_id,
+				       logdev_callback_func func)
+{
+	struct list_head *p;
+	struct logdev_callback *cb;
+	unsigned long flags;
+	int ret = 0;
+
+	cb = kmalloc(sizeof(*cb),GFP_KERNEL);
+	if (!cb) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	spin_lock_irqsave(&logdev_callbacks_lock, flags);
+	list_for_each(p,&logdev_callbacks) {
+		struct logdev_callback *c =
+			list_entry(p, struct logdev_callback, list);
+		if (c->id == custom_id) {
+			spin_unlock_irqrestore(&logdev_callbacks_lock,flags);
+			kfree(cb);
+			ret = -EBUSY;
+			goto out;
+		}
+	}
+
+	cb->id = custom_id;
+	cb->func = func;
+	list_add(&cb->list, &logdev_callbacks);
+	spin_unlock_irqrestore(&logdev_callbacks_lock, flags);
+
+ out:
+	return ret;
+}
+
+int __kprobes logdev_unregister_callback(int custom_id)
+{
+	struct list_head *p;
+	struct logdev_callback *cb;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&logdev_callbacks_lock, flags);
+	list_for_each(p,&logdev_callbacks) {
+		cb = list_entry(p, struct logdev_callback, list);
+		if (cb->id == custom_id)
+			break;
+	}
+	if (p == &logdev_callbacks) {
+		ret = -ENODEV;
+		spin_unlock_irqrestore(&logdev_callbacks_lock,flags);
+		goto out;
+	}
+
+	list_del(&cb->list);
+	spin_unlock_irqrestore(&logdev_callbacks_lock, flags);
+
+	kfree(cb);
+
+ out:
+	return ret;
+}
+
+/*
+ * We have a separate kernel buffer for each CPU.
+ * This buffer is used to copy snprintf data into the ring buffer.
+ */
+static char kern_buffer[LOGDEV_CPUS][PAGE_SIZE];
+
+/*
+ * logdev_record_switch is used to track context switches.
+ *
+ * If the logdev_switch is not set, then this doesn't record. Thus allowing
+ * you to just record the context switches that are needed to record.
+ * Just use logdev_switch_on and logdev_switch_off to turn on this function.
+ *
+ * This is called from schedule, and interrupts should already be turned off.
+ */
+void __kprobes  logdev_record_switch(struct task_struct *prev,
+				     struct task_struct *next)
+{
+	struct logdev_dev *dev;
+	struct logdev_header hdr;
+	struct logdev_switch_struct rs;
+	int cpu = smp_processor_id();
+
+	if (!logdev_switch_ison())
+		return;
+
+	if (check_cpu(cpu))
+		return;
+
+	dev = get_logdev(cpu);
+	if (!dev_running(dev))
+		return;
+
+	WARN_ON(!irqs_disabled());
+
+	rs.t = sched_clock();
+	rs.pid_prev = prev->pid;
+	rs.prev_prio = prev->prio;
+	rs.prev_static_prio = prev->static_prio;
+	rs.prev_normal_prio = prev->normal_prio;
+	rs.prev_state = prev->state;
+	rs.pid_next = next->pid;
+	rs.next_prio = next->prio;
+	rs.next_static_prio = next->static_prio;
+	rs.next_normal_prio = next->normal_prio;
+	memcpy(rs.prev_comm, prev->comm, TASK_COMM_LEN);
+	memcpy(rs.next_comm, next->comm, TASK_COMM_LEN);
+
+	hdr.counter = logdev_counter_inc();
+	hdr.id = LOGDEV_SWITCH_ID;
+	hdr.size = sizeof(hdr) + sizeof(rs);
+
+	spin_lock(&dev->lock);
+	logdev_copy_to_dev(dev,&hdr,sizeof(hdr));
+	logdev_copy_to_dev(dev,&rs,sizeof(rs));
+	spin_unlock(&dev->lock);
+}
+
+/*
+ * logdev_print acts like printk but it writes to the logdev device instead
+ *  of a console.
+ */
+int __kprobes logdev_vprint(const char *str, va_list va)
+{
+	struct logdev_dev *dev;
+	char *buffer;
+	int len=0;
+	struct logdev_header hdr;
+	struct logdev_print rs;
+	unsigned long flags;
+	int cpu;
+
+	local_irq_save(flags);
+
+	cpu = smp_processor_id();
+
+	if (check_cpu(cpu))
+		goto out;
+
+	dev = get_logdev(cpu);
+	if (!dev_running(dev))
+		goto out;
+
+	buffer = kern_buffer[cpu];
+	len = vsnprintf(buffer, PAGE_SIZE, str, va);
+
+	if (len >= PAGE_SIZE) {
+		buffer[PAGE_SIZE-1] = 0;
+		len = PAGE_SIZE;
+	}
+
+	hdr.counter = logdev_counter_inc();
+	hdr.id = LOGDEV_PRINT;
+	hdr.size = sizeof(hdr) + sizeof(rs) + len;
+
+	spin_lock(&dev->lock);
+	logdev_copy_to_dev(dev, &hdr, sizeof(hdr));
+	if (sizeof(rs))
+		logdev_copy_to_dev(dev, &rs, sizeof(rs));
+	logdev_copy_to_dev(dev, buffer, len);
+	spin_unlock(&dev->lock);
+
+ out:
+	local_irq_restore(flags);
+
+	return len;
+}
+
+int  __kprobes logdev_print(const char *str, ...)
+{
+	va_list va;
+	struct logdev_dev *dev;
+	char *buffer;
+	int len=0;
+	struct logdev_header hdr;
+	struct logdev_print rs;
+	unsigned long flags;
+	int cpu;
+
+	local_irq_save(flags);
+
+	cpu = smp_processor_id();
+
+	if (check_cpu(cpu))
+		goto out;
+
+	dev = get_logdev(cpu);
+	if (!dev_running(dev))
+		goto out;
+
+	buffer = kern_buffer[cpu];
+
+	va_start(va,str);
+	len = vsnprintf(buffer, PAGE_SIZE, str, va);
+	va_end(va);
+
+	if (len >= PAGE_SIZE) {
+		buffer[PAGE_SIZE-1] = 0;
+		len = PAGE_SIZE;
+	}
+
+	hdr.counter = logdev_counter_inc();
+	hdr.id = LOGDEV_PRINT;
+	hdr.size = sizeof(hdr) + sizeof(rs) + len;
+
+	spin_lock(&dev->lock);
+	logdev_copy_to_dev(dev, &hdr, sizeof(hdr));
+	if (sizeof(rs))
+		logdev_copy_to_dev(dev, &rs, sizeof(rs));
+	logdev_copy_to_dev(dev, buffer, len);
+	spin_unlock(&dev->lock);
+
+ out:
+	local_irq_restore(flags);
+
+	return len;
+}
+
+/*
+ * logdev_print_time is the same as logdev_print but it attaches a timestamp
+ *  to it.  Saves on doing it yourself.
+ */
+int  __kprobes logdev_print_time(const char *str, ...)
+{
+	struct logdev_dev *dev;
+	char *buffer;
+	va_list va;
+	int len = 0;
+	struct logdev_header hdr;
+	struct logdev_print_time rs;
+	unsigned long flags;
+	int cpu;
+
+	local_irq_save(flags);
+
+	cpu = smp_processor_id();
+
+	if (check_cpu(cpu))
+		goto out;
+
+	dev = get_logdev(cpu);
+	if (!dev_running(dev))
+		goto out;
+
+	buffer = kern_buffer[cpu];
+
+	va_start(va,str);
+	len = vsnprintf(buffer, PAGE_SIZE, str, va);
+	va_end(va);
+
+	if (len >= PAGE_SIZE) {
+		buffer[PAGE_SIZE-1] = 0;
+		len = PAGE_SIZE;
+	}
+
+	rs.t = sched_clock();
+
+	hdr.counter = logdev_counter_inc();
+	hdr.id = LOGDEV_PRINT_TIME;
+	hdr.size = sizeof(hdr) + sizeof(rs) + len;
+
+	spin_lock(&dev->lock);
+	logdev_copy_to_dev(dev, &hdr, sizeof(hdr));
+	logdev_copy_to_dev(dev, &rs, sizeof(rs));
+	logdev_copy_to_dev(dev, buffer, len);
+	spin_unlock(&dev->lock);
+
+ out:
+	local_irq_restore(flags);
+
+	return len;
+}
+
+/*
+ * logdev_print_time_func quickly stores the time, function and line number.
+ * this is really only good for live runs since the function is just a pointer,
+ * so a user land process would need to have the System.map available.
+ */
+int  __kprobes logdev_print_time_func(const char *file, int line,
+				      const char *str, ...)
+{
+	struct logdev_dev *dev;
+	char *buffer;
+	va_list va;
+	int len = 0;
+	struct logdev_header hdr;
+	struct logdev_print_time_func rs;
+	unsigned long flags;
+	int cpu;
+
+	local_irq_save(flags);
+
+	cpu = smp_processor_id();
+
+	if (check_cpu(cpu))
+		goto out;
+
+	dev = get_logdev(cpu);
+	if (!dev_running(dev))
+		goto out;
+
+	buffer = kern_buffer[cpu];
+
+	va_start(va,str);
+	len = vsnprintf(buffer, PAGE_SIZE, str, va);
+	va_end(va);
+
+	if (len >= PAGE_SIZE) {
+		buffer[PAGE_SIZE-1] = 0;
+		len = PAGE_SIZE;
+	}
+
+	rs.t = sched_clock();
+	rs.file = file;
+	rs.line = line;
+
+	hdr.counter = logdev_counter_inc();
+	hdr.id = LOGDEV_PRINT_TIME_FUNC;
+	hdr.size = sizeof(hdr) + sizeof(rs) + len;
+
+	spin_lock(&dev->lock);
+	logdev_copy_to_dev(dev, &hdr, sizeof(hdr));
+	logdev_copy_to_dev(dev, &rs, sizeof(rs));
+	logdev_copy_to_dev(dev, buffer, len);
+	spin_unlock(&dev->lock);
+
+ out:
+	local_irq_restore(flags);
+
+	return len;
+}
+
+/*
+ * If you feel like recording your own data, you can use logdev_record.
+ *  just pass your own id, size and data.  The size is the size of
+ *  the data being passed and not the size actually being written to the
+ *  device.
+ *  That is already calculated.
+ *
+ * This record will be added as LOGDEV_CUSTOM and the given id will be the
+ *  custom id.
+ *
+ * We use total_size so that we can avoid multiple copies to get the data
+ * into the buffer.
+ */
+int  __kprobes logdev_record(int id, int total_size, const void *data,
+			     int size, ...)
+{
+	struct logdev_dev *dev;
+	struct logdev_header hdr;
+	struct logdev_custom rs;
+	unsigned long flags;
+	int tsize = size;
+	int cpu;
+	int ret = 0;
+	va_list ap;
+
+	if (!data)
+		return -EINVAL;
+
+	local_irq_save(flags);
+
+	cpu = smp_processor_id();
+
+	if (check_cpu(cpu))
+		goto out;
+
+	dev = get_logdev(cpu);
+	if (!dev_running(dev))
+		goto out;
+
+	hdr.counter = logdev_counter_inc();
+	hdr.id = LOGDEV_CUSTOM;
+	hdr.size = sizeof(hdr) + sizeof(rs) + total_size;
+
+	rs.id = id;
+
+	spin_lock(&dev->lock);
+	logdev_copy_to_dev(dev, &hdr, sizeof(hdr));
+	logdev_copy_to_dev(dev, &rs, sizeof(rs));
+	va_start(ap, size);
+	do {
+		ret = logdev_copy_to_dev(dev, data, size);
+		if (tsize > total_size)
+			break;
+		data = va_arg(ap, void *);
+		if (data) {
+			size = va_arg(ap, int);
+			tsize += size;
+			if (tsize > total_size)
+				size -= tsize - total_size;
+		}
+	} while (data);
+	va_end(ap);
+
+	/*
+	 * If total_size didn't equal all sizes, then write padding.
+	 */
+	while (tsize < total_size) {
+		int cnt = total_size - tsize;
+		if (cnt > PAGE_SIZE)
+			cnt = PAGE_SIZE;
+		logdev_copy_to_dev(dev, kern_buffer[cpu], cnt);
+		tsize += cnt;
+	}
+
+	spin_unlock(&dev->lock);
+
+out:
+	local_irq_restore(flags);
+
+	return ret;
+}
+
+/*
+ * If you just want to write into the buffer using your own methods, then this
+ * is perfectly fine. Just pass in your data and the size of the data being
+ * passed in. You can read it out later with logdev_record_read. But you wont
+ * have the benefits of keeping integrity when the buffer overflows.
+ */
+int __kprobes logdev_record_write(const char *data, int size)
+{
+	struct logdev_dev *dev;
+	int ret;
+	unsigned long flags;
+	int cpu;
+
+	local_irq_save(flags);
+
+	cpu = smp_processor_id();
+	dev = get_logdev(cpu);
+	if (!dev_running(dev)) {
+		local_irq_restore(flags);
+		return 0;
+	}
+
+	spin_lock(&dev->lock);
+	ret = logdev_copy_to_dev(dev, data, size);
+	spin_unlock(&dev->lock);
+	local_irq_restore(flags);
+
+	return ret;
+}
+
+/*
+ * logdev_record_read reads some data from the logdev device no matter what
+ * it was.
+ */
+int __kprobes logdev_record_read(void *data, int size)
+{
+	struct logdev_dev *dev;
+	int ret;
+	unsigned long flags;
+	int cpu;
+
+	local_irq_save(flags);
+
+	cpu = smp_processor_id();
+	dev = get_logdev(cpu);
+	if (!dev_running(dev)) {
+		local_irq_restore(flags);
+		return 0;
+	}
+
+	spin_lock(&dev->lock);
+	ret = logdev_copy_from_dev(dev, data, size);
+	spin_unlock(&dev->lock);
+	local_irq_restore(flags);
+
+	return ret;
+}
+
+/*
+ * sched_clock isn't exported, so we export it ourselves.
+ */
+
+int in_logdump;
+EXPORT_SYMBOL_GPL(in_logdump);
+
+static int __kprobes get_next_cpus(struct logdev_header *hdr,
+				   int *_this_cpu, int *_next_cpu)
+{
+	int cpu;
+	int f = 0;
+	struct logdev_dev *dev;
+	int this_cpu = -1;
+	int next_cpu = -1;
+	int last_cpu = 1;
+
+	for_each_present_cpu(cpu) {
+
+		if (cpu >= LOGDEV_CPUS)
+			break;
+
+		dev = get_logdev(cpu);
+		if (!dev_suspended(dev))
+			continue;
+
+		/* Skip empty buffers */
+		if (hdr[cpu].id == LOGDEV_HDR_DONE)
+			continue;
+
+		/*
+		 * If the header is corrupted, just pick it
+		 * as if this buffer was the last. The corruption
+		 * will break out of the loop in the flush.
+		 */
+		if  (!logdev_valid(hdr[cpu].id)) {
+			this_cpu = cpu;
+			next_cpu = -1;
+			last_cpu = 1;
+			break;
+		}
+
+		/*
+		 * If this is the first cpu, then use it, otherwise,
+		 * compare.
+		 */
+		if (!f ||
+		    compare_cnt(hdr[cpu].counter,
+				hdr[this_cpu].counter) < 0) {
+			if (!f)
+				f = 1;
+			else {
+				/* we already have the first count */
+				next_cpu = this_cpu;
+				last_cpu = 0;
+				f = 2;
+			}
+			this_cpu = cpu;
+		} else if (f == 1 ||
+			   compare_cnt(hdr[cpu].counter,
+				       hdr[next_cpu].counter) < 0) {
+			f = 2;
+			next_cpu = cpu;
+			last_cpu = 0;
+		}
+	}
+
+	*_this_cpu = this_cpu;
+	*_next_cpu = next_cpu;
+
+	return last_cpu;
+}
+
+static int __kprobes process_log(struct logdev_dev *dev,
+				 struct logdev_header *hdr,
+				 int cpu)
+{
+	int i;
+	int r;
+	int count;
+	int corrupt = 0;
+	int line = 0;
+	int newline = 1;
+	unsigned long long t;
+	unsigned long usec_rem;
+	unsigned long secs;
+	const char *file = NULL;
+
+	r = sizeof(hdr[0]);
+
+	switch (hdr[cpu].id) {
+
+	case LOGDEV_PRINT_TIME_FUNC:
+	{
+		struct logdev_print_time_func rs;
+		int cap = sizeof(rs) - sizeof(struct logdev_print_time);
+
+		logdev_copy_from_dev(dev, &rs, cap);
+		file = rs.file;
+		line = rs.line;
+
+		r += cap;
+
+		/* fall through */
+	}
+
+	case LOGDEV_PRINT_TIME:
+	{
+		struct logdev_print_time rs;
+
+		logdev_copy_from_dev(dev, &rs, sizeof(rs));
+
+		if (newline) {
+			t = rs.t;
+			usec_rem = do_div(t, 1000000000)/1000;
+			secs = (unsigned long)t;
+
+			printk("[%5lu.%06lu] ",
+			       secs, usec_rem);
+		}
+
+		r += sizeof(rs);
+
+		/* fall through */
+	}
+
+	case LOGDEV_PRINT:
+		if (newline) {
+			printk("cpu:%d ",cpu);
+
+			if (hdr[cpu].id == LOGDEV_PRINT_TIME_FUNC)
+				printk("%s:%d ",file, line);
+		}
+		for (i=r; i < hdr[cpu].size; i += r) {
+			count = hdr[cpu].size - i;
+			if (count > PAGE_SIZE-1)
+				count = PAGE_SIZE-1;
+			r = logdev_copy_from_dev(dev, kern_buffer[cpu], count);
+			if (r < 0)
+				break;
+			kern_buffer[cpu][count] = 0;
+			printk("%s", kern_buffer[cpu]);
+			newline = (count) &&
+				(kern_buffer[cpu][count - 1] != '\n') ? 0 : 1;
+		}
+		break;
+
+	case  LOGDEV_SWITCH_ID:
+	{
+		struct logdev_switch_struct rs;
+
+		printk(">>>> IN LOGDEV SWITCH <<<< cpu:%d \n", cpu);
+
+		logdev_copy_from_dev(dev,&rs,sizeof(rs));
+
+		t = rs.t;
+		usec_rem = do_div(t, 1000000000)/1000;
+		secs = (unsigned long)t;
+
+		printk("CPU=%d [%5lu.%06lu] ",
+		       cpu, secs,usec_rem);
+
+		printk("%s:%d(%d:%d:%d) -->> ",
+		       rs.prev_comm,
+		       rs.pid_prev,
+		       rs.prev_prio,
+		       rs.prev_static_prio,
+		       rs.prev_normal_prio);
+
+		printk("%s:%d(%d:%d:%d)\n",
+		       rs.next_comm,
+		       rs.pid_next,
+		       rs.next_prio,
+		       rs.next_static_prio,
+		       rs.next_normal_prio);
+
+		break;
+	}
+
+	case LOGDEV_CUSTOM:
+	{
+		struct list_head *p;
+		struct logdev_custom custom;
+		int len;
+
+		logdev_copy_from_dev(dev,&custom,sizeof(custom));
+
+		spin_lock(&logdev_callbacks_lock);
+		i = len = hdr[cpu].size - sizeof(hdr[0]) - sizeof(custom);
+		if (i > PAGE_SIZE)
+			i = PAGE_SIZE;
+
+		/* change size to be what the record size is. */
+		hdr[cpu].size = i;
+
+		logdev_copy_from_dev(dev,kern_buffer[cpu],i);
+
+		list_for_each(p, &logdev_callbacks) {
+			struct logdev_callback *cb =
+				list_entry(p, struct logdev_callback, list);
+			if (cb->id == custom.id) {
+				cb->func(&hdr[cpu], &custom, cpu,
+					 kern_buffer[cpu]);
+				break;
+			}
+		}
+
+		/* No record should be bigger than a page. Ignore all else */
+		while (i < len) {
+			int count = len - i;
+			if (count > PAGE_SIZE)
+				count = PAGE_SIZE;
+			logdev_copy_from_dev(dev,kern_buffer[cpu],count);
+			i += count;
+		}
+
+		/* check if we didn't find a call back */
+		if (p == &logdev_callbacks) {
+			printk("skipping! LOGDEV_CUSTOM id %d\n",custom.id);
+		}
+
+		spin_unlock(&logdev_callbacks_lock);
+		break;
+	}
+	default:
+		corrupt = 1;
+		if (!dev->corrupted) {
+			dev->corrupted = 1;
+			printk(">>>>> Unknown logdev header, cpu %d buffer "
+			       "may be corrupted from this point on\n", cpu);
+		} else
+			printk("CPU %d >>> corrupted header <<<\n", cpu);
+		break;
+	}
+
+	return corrupt;
+}
+
+static int __kprobes flush_buffer(struct logdev_dev *dev,
+				  struct logdev_header *hdr,
+				  int cpu, int next_cpu, int last_cpu)
+{
+	int corrupt = 0;
+	int more_work = 1;
+
+	while (last_cpu || compare_cnt(hdr[cpu].counter,
+				       hdr[next_cpu].counter) <= 0) {
+
+		BUG_ON(hdr[cpu].id == LOGDEV_HDR_DONE);
+
+		/*
+		 *  Lets not set off watchdogs.
+		 */
+		touch_nmi_watchdog();
+
+		corrupt = process_log(dev, hdr, cpu);
+
+		/* Read the next header for this */
+		if ((logdev_copy_from_dev(dev,&hdr[cpu],sizeof(hdr[0])))
+		    != sizeof(hdr[0])) {
+			hdr[cpu].id = LOGDEV_HDR_DONE;
+			if (last_cpu)
+				more_work = 0;
+			break;
+		}
+		/*
+		 * if we have a corrupted header, then stop this buffer flush.
+		 */
+		if (corrupt)
+			break;
+	}
+
+	return more_work;
+}
+
+void __kprobes logdev_dump(void)
+{
+	int save_switch;
+	int save_print;
+	struct logdev_dev *dev;
+	struct logdev_header hdr[LOGDEV_CPUS];
+	static int started  = 0;
+	int do_lock = 1;
+	extern int in_logdump;
+	unsigned long flags;
+	int more_work = 0;
+	int cpu;
+
+	/*
+	 * We don't care about race conditions with this started variable.
+	 * It only exists to keep dumps a little cleaner. If two dumps get
+	 * through at the same time, it doesn't hurt.
+	 */
+	if (started)
+		return;
+
+	started = 1;
+
+	/*
+	 * Because of the started race, we also use in_logdump just for
+	 * reference.
+	 */
+	in_logdump++;
+
+	if (oops_in_progress)
+		do_lock = 0;
+
+	local_irq_save(flags);
+
+	/*
+	 * This is for debugging, so we don't want to reintroduce more output.
+	 */
+	save_switch = logdev_switch_ison();
+	save_print = logdev_print_ison();
+	logdev_switch_off();
+	logdev_print_off();
+
+	printk("****** Starting Logdev Dump ********\n");
+
+	/*
+	 * Read all the available headers for each CPU.
+	 */
+	for_each_present_cpu(cpu) {
+
+		if (cpu >= LOGDEV_CPUS)
+			break;
+
+		dev = get_logdev(cpu);
+		if (!dev_running(dev))
+			continue;
+
+		if (dev->corrupted) {
+			printk("Warning buffer for CPU %d is corrupted\n",
+			       cpu);
+			/* will be set when corrupted part is read. */
+			dev->corrupted = 0;
+		}
+
+		/*
+		 * Long time to hold the spin locks, but hey it's just
+		 * debugging.
+		 */
+		spin_lock(&dev->lock);
+
+		/*
+		 * Try to limit the amount added while reading
+		 * this buffer, suspend the buffer. (redundant but also good
+		 * for accounting)
+		 */
+		dev->init = LOGDEV_DEV_SUSPENDED;
+
+		if ((logdev_copy_from_dev(dev, &hdr[cpu], sizeof(hdr[0])))
+		    == sizeof(hdr[0]))
+			/* record that we have a buffer to work with. */
+			more_work = 1;
+		else
+			/* record that the buffer is empty */
+			hdr[cpu].id = LOGDEV_HDR_DONE;
+	}
+
+	while (more_work) {
+		int this_cpu;
+		int next_cpu;
+		int last_cpu;
+
+		/*
+		 * Find the cpu to work with that has the earliest counter,
+		 * and also the cpu with the next counter.
+		 */
+		last_cpu = get_next_cpus(hdr, &this_cpu, &next_cpu);
+
+		cpu = this_cpu;
+		dev = get_logdev(cpu);
+
+		BUG_ON(this_cpu < 0);
+		BUG_ON(!last_cpu && next_cpu < 0);
+		BUG_ON(dev->init != LOGDEV_DEV_SUSPENDED);
+		BUG_ON(hdr[cpu].id == LOGDEV_HDR_DONE);
+
+		/*
+		 * Now print out all from this buffer until we reach
+		 * the next cpu. If this is the last buffer to write
+		 * then finish the buffer.
+		 */
+		more_work = flush_buffer(dev, hdr, cpu, next_cpu, last_cpu);
+	}
+
+	printk( ">>>>> done <<<<<\n");
+
+	for_each_present_cpu(cpu) {
+		if (cpu >= LOGDEV_CPUS)
+			break;
+		dev = get_logdev(cpu);
+		if (dev_suspended(dev)) {
+		    dev->init = LOGDEV_DEV_RUNNING;
+		    /* buffers should be emptied */
+		    dev->corrupted = 0;
+		    spin_unlock(&dev->lock);
+		}
+
+	}
+	if (save_print)
+		logdev_print_on();
+	if (save_switch)
+		logdev_switch_on();
+
+	local_irq_restore(flags);
+	started = 0;
+	in_logdump--;
+}
+
+static int __kprobes logdev_panic_handler(struct notifier_block *this,
+					  unsigned long event,
+					  void *unused)
+{
+	logdev_dump();
+	return NOTIFY_OK;
+}
+
+static struct notifier_block logdev_panic_notifier = {
+	.notifier_call	= logdev_panic_handler,
+	.next		= NULL,
+	.priority	= 150	/* priority: INT_MAX >= x >= 0 */
+};
+
+/*
+ * Unfortunately, the die handlers have no way to unregister, so
+ * we don't want to do this if we are a module. We would if we could
+ * find a way to clean ourselves up when unloaded.
+ */
+int __kprobes logdev_die_handler(struct notifier_block *self,
+				 unsigned long val,
+				 void *data)
+{
+	switch (val) {
+	case DIE_OOPS:
+		logdev_dump();
+		break;
+	default:
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block logdev_die_notifier = {
+	.notifier_call = logdev_die_handler,
+	.priority = 200
+};
+
+static int logdev_open_generic(struct inode *inode, struct file *filp)
+{
+	filp->private_data = inode->i_private;
+	return 0;
+}
+
+
+static ssize_t logdev_debug_sw_read(struct file *filp, char __user *ubuf,
+				size_t cnt, loff_t *ppos)
+{
+	long bit = (long)(filp->private_data);
+	char buf[16];
+	int var = !!(logdev_switches & (1<<bit));
+	int r;
+
+	r = sprintf(buf, "%d\n", var);
+	return simple_read_from_buffer(ubuf, cnt, ppos,
+				       buf, r);
+}
+
+static ssize_t logdev_debug_sw_write(struct file *filp,
+				     const char __user *ubuf,
+				     size_t cnt, loff_t *ppos)
+{
+	int val;
+	unsigned long bit = (long)(filp->private_data);
+	char buf[16];
+
+	/* We control what bit is, so it had better be right! */
+	if (bit >= LOGDEV_SW_BITS)
+		BUG();
+
+	bit = (1 << bit);
+
+	if (cnt > 15)
+		cnt = 15;
+
+	if(copy_from_user(&buf, ubuf, cnt))
+		return -EFAULT;
+
+	buf[cnt] = 0;
+
+	val = simple_strtoul(buf, NULL, 10) ? bit : 0;
+
+	logdev_switches = (logdev_switches & ~bit) | val;
+
+	filp->f_pos += cnt;
+
+	return cnt;
+}
+
+static struct file_operations logdev_debug_sw_fops = {
+	.open = logdev_open_generic,
+	.read = logdev_debug_sw_read,
+	.write = logdev_debug_sw_write,
+};
+
+static ssize_t logdev_debug_read(struct file *filp, char __user *ubuf,
+				size_t cnt, loff_t *ppos)
+{
+	unsigned long *data = filp->private_data;
+	char buf[16];
+	int r;
+
+	r = sprintf(buf, "%0lx\n", *data);
+	return simple_read_from_buffer(ubuf, cnt, ppos,
+				       buf, r);
+}
+
+static ssize_t logdev_debug_write(struct file *filp, char __user *ubuf,
+				  size_t cnt, loff_t *ppos)
+{
+	unsigned long *data = filp->private_data;
+	char buf[10];
+	unsigned long val;
+
+	if (cnt > 9)
+		cnt = 9;
+
+	if(copy_from_user(&buf, ubuf, cnt))
+		return -EFAULT;
+
+	buf[cnt] = 0;
+
+	val = simple_strtoul(buf, NULL, 10);
+
+	*data = val;
+
+	filp->f_pos += cnt;
+
+	return cnt;
+}
+
+static struct file_operations logdev_debug_rdonly_fops = {
+	.open = logdev_open_generic,
+	.read = logdev_debug_read,
+};
+
+static struct file_operations logdev_debug_rdwr_fops = {
+	.open = logdev_open_generic,
+	.read = logdev_debug_read,
+	.read = logdev_debug_write,
+};
+
+struct dentry *logdev_d;
+EXPORT_SYMBOL_GPL(logdev_d);
+
+static int logdev_debugfs_init(void)
+{
+	struct dentry *d_switches;
+	struct dentry *d_switch;
+	struct dentry *d_print;
+#ifndef CONFIG_LOGDEV_MARKER
+	struct dentry *d_mark;
+#endif
+
+	logdev_d = debugfs_create_dir("logdev", NULL);
+	if (!logdev_d) {
+		printk("can't create logdev debugfs\n");
+		return 0;
+	}
+	if (logdev_d == ERR_PTR(-ENODEV)) {
+		printk("debugfs not configured in. Can't access logdev "
+		       "from userspace\n");
+		logdev_d = NULL;
+		return 0;
+	}
+
+	d_switches = debugfs_create_file("switches", 0444, logdev_d,
+					 &logdev_switches,
+					 &logdev_debug_rdonly_fops);
+	d_switch = debugfs_create_file("switch", 0644, logdev_d,
+				       (void*)LOGDEV_SW_SWITCH_ENABLED,
+				       &logdev_debug_sw_fops);
+	d_print = debugfs_create_file("print", 0644, logdev_d,
+				      (void*)LOGDEV_SW_PRINT_ENABLED,
+				      &logdev_debug_sw_fops);
+#ifndef CONFIG_LOGDEV_MARKER
+	d_mark = debugfs_create_file("mark", 0644, logdev_d,
+				      (void*)LOGDEV_SW_MARKER_ENABLED,
+				      &logdev_debug_sw_fops);
+#endif
+	return 0;
+}
+
+extern void logdev_cleanup_priv(struct logdev_dev *dev);
+
+static void __exit logdev_cleanup(void)
+{
+	struct logdev_dev *dev;
+	int i;
+
+	atomic_notifier_chain_unregister(&panic_notifier_list,
+					 &logdev_panic_notifier);
+
+	for (i=0; i < LOGDEV_CPUS; i++) {
+		dev = get_logdev(i);
+		logdev_cleanup_priv(dev);
+	}
+}
+
+extern int initialize_logdev(void);
+
+int __init logdev_init(void)
+{
+	int res = 0;
+	int cpu;
+	struct logdev_dev *dev;
+	static int init = 0;
+
+	if (init)
+		return 0;
+
+	init = 1;
+
+	printk("Logdevice: copyright Steven Rostedt, Kihon Technologies Inc."
+	       " (Version %s)\n",
+	       logdev_version);
+
+	for_each_present_cpu(cpu) {
+
+		if (cpu >= LOGDEV_CPUS) {
+			printk(KERN_WARNING "More present cpus (%d) than "
+			       "NR_CPUS (%d)\n",
+			       cpu,LOGDEV_CPUS);
+			break;
+		}
+
+		printk("Initializing logdev for cpu: %d\n",cpu);
+
+		dev = get_logdev(cpu);
+		spin_lock_init(&dev->lock);
+		dev->init = LOGDEV_DEV_UP;
+	}
+
+	if (initialize_logdev() < 0)
+		goto fail;
+
+	atomic_notifier_chain_register(&panic_notifier_list,
+				       &logdev_panic_notifier);
+
+	register_die_notifier(&logdev_die_notifier);
+
+	res = 0;
+
+#ifdef CONFIG_LOGDEV_PRINT_ENABLED
+	logdev_print_on();
+#endif
+#ifdef CONFIG_LOGDEV_SWITCH_ENABLED
+	logdev_switch_on();
+#endif
+
+out:
+	return res;
+
+fail:
+	logdev_cleanup();
+	goto out;
+}
+
+EXPORT_SYMBOL_GPL(logdev_record_switch);
+EXPORT_SYMBOL_GPL(logdev_print);
+EXPORT_SYMBOL_GPL(logdev_print_time);
+EXPORT_SYMBOL_GPL(logdev_print_time_func);
+EXPORT_SYMBOL_GPL(logdev_record);
+EXPORT_SYMBOL_GPL(logdev_record_write);
+EXPORT_SYMBOL_GPL(logdev_record_read);
+EXPORT_SYMBOL_GPL(logdev_dump);
+
+core_initcall(logdev_init);
+postcore_initcall(logdev_debugfs_init);
Index: linux-2.6.21.5-rt20/kernel/logdev/logdev_priv.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21.5-rt20/kernel/logdev/logdev_priv.h	2007-07-11 14:28:50.000000000 +0200
@@ -0,0 +1,52 @@
+#ifndef _LOG_DEV_PRIV_H
+#define _LOG_DEV_PRIV_H
+
+/* Random numbers out of my head used for MAGIC */
+#define LOGDEV_CUSTOM 0x1afb
+#define LOGDEV_SWITCH_ID 0x2afc
+#define LOGDEV_PRINT 0x4adb
+#define LOGDEV_PRINT_TIME 0x4adc
+#define LOGDEV_PRINT_TIME_FUNC 0x4add
+#define LOGDEV_HDR_DONE 0xbbbb  /* internal use only */
+
+static inline int logdev_valid(int id)
+{
+	switch (id) {
+	case LOGDEV_CUSTOM:
+	case LOGDEV_SWITCH_ID:
+	case LOGDEV_PRINT:
+	case LOGDEV_PRINT_TIME:
+	case LOGDEV_PRINT_TIME_FUNC:
+		return 1;
+	}
+	return 0;
+}
+
+struct logdev_dev {
+	int init;
+	int corrupted;
+	spinlock_t lock;
+	void *priv;
+};
+
+DECLARE_PER_CPU(struct logdev_dev, logdev_dev);
+#define get_logdev(cpu) &per_cpu(logdev_dev, cpu)
+
+#define LOGDEV_CPUS NR_CPUS
+
+#define LOGDEV_DEV_UNINITALIZED 0
+#define LOGDEV_DEV_RUNNING 1
+#define LOGDEV_DEV_SUSPENDED 2
+#define LOGDEV_DEV_UP 3
+
+#define dev_running(dev) ((dev)->init == LOGDEV_DEV_RUNNING)
+#define dev_suspended(dev) ((dev)->init == LOGDEV_DEV_SUSPENDED)
+#define dev_up(dev) ((dev)->init == LOGDEV_DEV_UP)
+
+extern struct dentry *logdev_d;
+
+#ifdef CONFIG_LOGDEV_RINGBUF
+#include "logdev_ringbuf.h"
+#endif
+
+#endif
Index: linux-2.6.21.5-rt20/kernel/logdev/logdev_probe.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21.5-rt20/kernel/logdev/logdev_probe.c	2007-07-11 14:28:50.000000000 +0200
@@ -0,0 +1,1310 @@
+/*
+ * logdev_probe.c
+ *
+ * Copyright (C) 2004-2006 Steven Rostedt <[email protected]>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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; version 2 of the License (not later!)
+ *
+ * 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
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/logdev.h>
+#include <linux/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/kprobes.h>
+#include <linux/ptrace.h>
+#include <linux/kallsyms.h>
+#include <linux/list.h>
+
+#include <asm/atomic.h>
+
+#include "logdev_priv.h"
+
+#undef DPRINTK
+#if 1
+#  define DPRINTK(x...) printk(x)
+#else
+#  define DPRINTK(x...) do { } while(0)
+#endif
+
+static atomic_t logdev_probe_next_id = ATOMIC_INIT(1);
+static DEFINE_MUTEX(probe_list_lock);
+enum logprobe_type {
+	LOGPROBE_BP,
+	LOGPROBE_FUNC,
+	LOGPROBE_CURR,
+	LOGPROBE_VAR,
+};
+
+static char *logdev_probe_buffer;
+static int logdev_probe_buffer_sz;
+static DEFINE_SPINLOCK(probe_buffer_lock);
+static DEFINE_PER_CPU(struct jprobe *, jprobe);
+
+/* ---------------- cut here for user space headers -------------------- */
+
+/* "s/user_//" for the below when bringing to user space */
+
+#define LOGPROBE_IOCTL_BASE 'l'
+
+#define PROBE_IOW(nr, type) _IOW(LOGPROBE_IOCTL_BASE, nr, type)
+
+#define LOGPROBE_DELPOINT  PROBE_IOW(0, unsigned long)
+#define LOGPROBE_ADDWATCH  PROBE_IOW(1, struct user_logdev_probe_watch)
+#define LOGPROBE_ADDFUNC   PROBE_IOW(2, struct user_logdev_probe_func)
+#define LOGPROBE_ADDCURR   PROBE_IOW(3, struct user_logdev_probe_curr)
+#define LOGPROBE_ADDVAR    PROBE_IOW(4, struct user_logdev_probe_var)
+
+#define LOGPROBE_ID_WATCH	0x56570001
+#define LOGPROBE_ID_FUNC	0x56570002
+#define LOGPROBE_ID_CURR	0x56570003
+#define LOGPROBE_ID_VAR		0x56570004
+
+#define LOGPROBE_TYPE_PREEMPT 0x1111
+
+struct logdev_probe_hdr_log {
+	unsigned long long t;
+	char comm[TASK_COMM_LEN];
+	int pid;
+	unsigned long addr;
+	unsigned long offset;
+	int func_symbol_size;
+};
+
+struct logdev_probe_watch_log {
+	struct logdev_probe_hdr_log hdr;
+	unsigned long var;
+	unsigned long value;
+	int var_symbol_size;
+	char symbols[];
+};
+
+struct logdev_probe_func_log {
+	struct logdev_probe_hdr_log hdr;
+	int str_size;
+	char symbols[];
+};
+
+struct logdev_probe_curr_log {
+	struct logdev_probe_hdr_log hdr;
+	int index;
+	unsigned long value;
+	int prefix_size;
+	char symbols[];
+};
+
+struct logdev_probe_var_log {
+	struct logdev_probe_hdr_log hdr;
+	int type;
+	unsigned long value;
+	int prefix_size;
+	char symbols[];
+};
+
+struct user_logdev_probe_watch {
+	unsigned long addr;
+	unsigned long watch;
+	const char *func;
+	const char *var;
+	int func_size;
+	int var_size;
+};
+
+struct user_logdev_probe_func {
+	const char *func;
+	const char *fmt1;
+	const char *fmt2;
+	int func_size;
+	int fmt1_size;
+	int fmt2_size;
+};
+
+struct user_logdev_probe_curr {
+	unsigned long addr;
+	const char *func;
+	const char *prefix;
+	int offset;
+	int func_size;
+	int prefix_size;
+};
+
+struct user_logdev_probe_var {
+	unsigned long addr;
+	const char *func;
+	const char *prefix;
+	int type;
+	int func_size;
+	int prefix_size;
+};
+
+/* ---------------- end of user space cut ---------------- */
+
+struct logdev_probe_hdr {
+	unsigned long addr;
+	unsigned long offset;
+	int func_symbol_size;
+	char func_symbol[KSYM_NAME_LEN+1];
+};
+
+struct logdev_probe_watch {
+	struct logdev_probe_hdr hdr;
+	struct kprobe kp;
+	unsigned long var;
+	int var_symbol_size;
+	char var_symbol[KSYM_NAME_LEN+1];
+};
+
+struct logdev_probe_func {
+	struct logdev_probe_hdr hdr;
+	struct jprobe jp;
+	const char *fmt1;
+	const char *fmt2;
+};
+
+struct logdev_probe_curr {
+	struct logdev_probe_hdr hdr;
+	struct kprobe kp;
+	int index;
+	char *prefix;
+	int prefix_size;
+};
+
+struct logdev_probe_var {
+	struct logdev_probe_hdr hdr;
+	struct kprobe kp;
+	int type;
+	char *prefix;
+	int prefix_size;
+};
+
+struct logdev_probe {
+	struct list_head list;
+	unsigned long id;
+	enum logprobe_type type;
+	union {
+		struct logdev_probe_watch watch;
+		struct logdev_probe_func func;
+		struct logdev_probe_curr curr;
+		struct logdev_probe_var var;
+	};
+};
+
+static LIST_HEAD(logdev_probes);
+
+static void logdev_print_time_cpu(unsigned long long t, int cpu)
+{
+	unsigned long usec_rem;
+	unsigned long secs;
+
+	usec_rem = do_div(t, 1000000000)/1000;
+	secs = (unsigned long)t;
+
+	printk("[%5lu.%06lu] cpu:%d ",
+	       secs, usec_rem, cpu);
+}
+
+/*
+ * This is called under a lock, so we can use a static buffer.
+ */
+static char probe_buf[PAGE_SIZE];
+
+/* ------------------- cut here for user space print -------------- */
+
+/* "s/printk/printf" */
+
+static const char *logdev_print_symbol(const char *sym, int size)
+{
+	/* we don't trust not having a null char */
+	while (size) {
+		int sz = size;
+		if (sz > PAGE_SIZE-1)
+			sz = PAGE_SIZE-1;
+		memcpy(probe_buf, sym, sz);
+		probe_buf[sz] = 0;
+		printk("%s", probe_buf);
+		sym += sz;
+		size -= sz;
+	}
+
+	return sym;
+}
+
+static const char * logdev_print_hdr(int cpu,
+				     const char *sym,
+				     struct logdev_probe_hdr_log *hdr)
+{
+	logdev_print_time_cpu(hdr->t, cpu);
+
+	memcpy(probe_buf, hdr->comm, TASK_COMM_LEN);
+	probe_buf[TASK_COMM_LEN] = 0;
+	printk("%s:%d ", probe_buf, hdr->pid);
+
+	printk("func: ");
+	sym = logdev_print_symbol(sym, hdr->func_symbol_size);
+	printk(" (%p) ", (void*)hdr->addr);
+
+	return sym;
+}
+
+static void logdev_probe_watch_print(int cpu, int size,
+				     struct logdev_probe_watch_log *lp)
+{
+	const char *sym;
+
+	if (size < sizeof(*lp) ||
+	    size != sizeof(*lp) + lp->var_symbol_size +
+	    lp->hdr.func_symbol_size) {
+		printk("CPU[%d] Bad probe var record!\n",
+		       cpu);
+		return;
+	}
+
+	sym = logdev_print_hdr(cpu, lp->symbols, &lp->hdr);
+
+	if (lp->var) {
+		printk("var: ");
+		sym = logdev_print_symbol(sym, lp->var_symbol_size);
+		printk(" (%p) = %08lx",
+		       (void*)lp->var,
+		       lp->value);
+	}
+	printk("\n");
+
+}
+
+static void logdev_probe_func_print(int cpu, int size,
+				    struct logdev_probe_func_log *lp)
+{
+	const char *sym;
+
+	if (size < sizeof(*lp) ||
+	    size != sizeof(*lp) + lp->hdr.func_symbol_size +
+	    lp->str_size) {
+		printk("CPU[%d] Bad probe func record!\n",
+		       cpu);
+		return;
+	}
+
+	sym = logdev_print_hdr(cpu, lp->symbols, &lp->hdr);
+
+	if (lp->str_size)
+		logdev_print_symbol(sym, lp->str_size);
+
+	printk("\n");
+
+}
+
+static void logdev_probe_curr_print(int cpu, int size,
+				    struct logdev_probe_curr_log *lp)
+{
+	const char *sym;
+
+	if (size < sizeof(*lp) ||
+	    size != sizeof(*lp) + lp->hdr.func_symbol_size +
+	    lp->prefix_size) {
+		printk("CPU[%d] Bad probe curr record!\n",
+		       cpu);
+		return;
+	}
+
+	sym = logdev_print_hdr(cpu, lp->symbols, &lp->hdr);
+
+	if (lp->prefix_size)
+		logdev_print_symbol(sym, lp->prefix_size);
+
+	printk(" index:%d = %p", lp->index, (void*)lp->value);
+
+	printk("\n");
+
+}
+
+static void logdev_probe_var_print(int cpu, int size,
+				   struct logdev_probe_var_log *lp)
+{
+	const char *sym;
+
+	if (size < sizeof(*lp) ||
+	    size != sizeof(*lp) + lp->hdr.func_symbol_size +
+	    lp->prefix_size) {
+		printk("CPU[%d] Bad probe curr record!\n",
+		       cpu);
+		return;
+	}
+
+	sym = logdev_print_hdr(cpu, lp->symbols, &lp->hdr);
+
+	switch (lp->type) {
+	case LOGPROBE_TYPE_PREEMPT:
+		printk(" preempt_count:");
+		break;
+	}
+
+	if (lp->prefix_size)
+		logdev_print_symbol(sym, lp->prefix_size);
+
+	printk("0x%lx", lp->value);
+
+	printk("\n");
+
+}
+
+static void logdev_probe_callback(struct logdev_header *hdr,
+				  struct logdev_custom *custom,
+				  int cpu,
+				  void *rec)
+{
+	switch (custom->id) {
+	case LOGPROBE_ID_WATCH:
+		logdev_probe_watch_print(cpu, hdr->size, rec);
+		break;
+	case LOGPROBE_ID_FUNC:
+		logdev_probe_func_print(cpu, hdr->size, rec);
+		break;
+	case LOGPROBE_ID_CURR:
+		logdev_probe_curr_print(cpu, hdr->size, rec);
+		break;
+	case LOGPROBE_ID_VAR:
+		logdev_probe_var_print(cpu, hdr->size, rec);
+		break;
+	default:
+		printk("Unknown probe callback id %x\n",
+		       custom->id);
+		break;
+	}
+}
+/* ------------------ end cut for user space printing ------------------- */
+
+
+/************************ Kprobes ******************************/
+
+static void __kprobes logprobe_hdr(struct logdev_probe_hdr_log *lp,
+				   struct logdev_probe_hdr *p)
+{
+	lp->t = sched_clock();
+	memcpy(lp->comm, current->comm, TASK_COMM_LEN);
+	lp->pid = current->pid;
+	lp->addr = p->addr;
+	lp->offset = p->offset;
+	lp->func_symbol_size = p->func_symbol_size;
+}
+
+static int __kprobes logprobe_watch(struct kprobe *kp, struct pt_regs *regs)
+{
+	struct logdev_probe_watch *p =
+		container_of(kp, struct logdev_probe_watch, kp);
+	struct logdev_probe_watch_log lp;
+	int total_sz = sizeof(lp) + p->var_symbol_size +
+		p->hdr.func_symbol_size;
+
+	rcu_read_lock();
+
+	logprobe_hdr(&lp.hdr, &p->hdr);
+
+	lp.var = p->var;
+	if (p->var)
+		lp.value = *(unsigned long*)(p->var);
+	lp.var_symbol_size = p->var_symbol_size;
+	logdev_record(LOGPROBE_ID_WATCH, total_sz,
+		      &lp, sizeof(lp),
+		      p->hdr.func_symbol, p->hdr.func_symbol_size,
+		      p->var_symbol, p->var_symbol_size,
+		      NULL);
+
+	rcu_read_unlock();
+
+	return 0;
+}
+
+
+static asmlinkage long __kprobes logprobe_func(unsigned long param, ...)
+{
+	struct jprobe *jp;
+	struct logdev_probe_func *p;
+	struct logdev_probe_func_log lp;
+	va_list ap;
+	int total_sz;
+	unsigned long flags;
+	int i = -1;
+
+	if (!logdev_probe_buffer)
+		jprobe_return();
+
+	jp = __get_cpu_var(jprobe);
+	if (!jp)
+		jprobe_return();
+
+	p = container_of(jp, struct logdev_probe_func, jp);
+
+	/* find probe */
+	rcu_read_lock();
+
+	logprobe_hdr(&lp.hdr, &p->hdr);
+
+	spin_lock_irqsave(&probe_buffer_lock, flags);
+	if (p->fmt1) {
+		i = snprintf(logdev_probe_buffer, logdev_probe_buffer_sz,
+			     p->fmt1, param);
+
+		if (p->fmt2 && i < logdev_probe_buffer_sz) {
+			va_start(ap, param);
+			i += snprintf(logdev_probe_buffer + i,
+				      logdev_probe_buffer_sz - i,
+				      p->fmt2, ap);
+			va_end(ap);
+		}
+		if (i > logdev_probe_buffer_sz)
+			i = logdev_probe_buffer_sz;
+
+		logdev_probe_buffer[i] = 0;
+	}
+
+	lp.str_size = i+1;
+
+	total_sz = sizeof(lp) + lp.hdr.func_symbol_size + lp.str_size;
+
+	logdev_record(LOGPROBE_ID_FUNC, total_sz,
+		      &lp, sizeof(lp),
+		      p->hdr.func_symbol, p->hdr.func_symbol_size,
+		      logdev_probe_buffer, lp.str_size,
+		      NULL);
+	spin_unlock_irqrestore(&probe_buffer_lock, flags);
+
+	rcu_read_unlock();
+
+	jprobe_return();
+	/* NOT REACHED */
+	return 0;
+}
+
+
+static int __kprobes logprobe_curr(struct kprobe *kp, struct pt_regs *regs)
+{
+	struct logdev_probe_curr *p =
+		container_of(kp, struct logdev_probe_curr, kp);
+	struct logdev_probe_curr_log lp;
+	int total_sz = sizeof(lp) + p->prefix_size +
+		p->hdr.func_symbol_size;
+	unsigned long *ptr;
+
+	rcu_read_lock();
+
+	logprobe_hdr(&lp.hdr, &p->hdr);
+
+	lp.index = p->index;
+
+	ptr = (unsigned long*)((char*)current + p->index);
+	lp.value = *ptr;
+
+	lp.prefix_size = p->prefix_size;
+
+	logdev_record(LOGPROBE_ID_CURR, total_sz,
+		      &lp, sizeof(lp),
+		      p->hdr.func_symbol, p->hdr.func_symbol_size,
+		      p->prefix, p->prefix_size,
+		      NULL);
+
+	rcu_read_unlock();
+
+	return 0;
+}
+
+static int __kprobes logprobe_var(struct kprobe *kp, struct pt_regs *regs)
+{
+	struct logdev_probe_var *p =
+		container_of(kp, struct logdev_probe_var, kp);
+	struct logdev_probe_var_log lp;
+	int total_sz = sizeof(lp) + p->prefix_size +
+		p->hdr.func_symbol_size;
+	unsigned long pc = preempt_count();
+
+	rcu_read_lock();
+
+	logprobe_hdr(&lp.hdr, &p->hdr);
+
+	lp.type = p->type;
+
+	switch(lp.type) {
+	case LOGPROBE_TYPE_PREEMPT:
+		lp.value = pc;
+	default:
+		lp.value = 0;
+	}
+
+	lp.prefix_size = p->prefix_size;
+
+	logdev_record(LOGPROBE_ID_VAR, total_sz,
+		      &lp, sizeof(lp),
+		      p->hdr.func_symbol, p->hdr.func_symbol_size,
+		      p->prefix, p->prefix_size,
+		      NULL);
+
+	rcu_read_unlock();
+
+	return 0;
+}
+
+/*********************** end Kprobes **************************/
+
+static int __kprobes logdev_probe_update_hdr(struct logdev_probe_hdr *p)
+{
+	const char *sym;
+	unsigned long size;
+	char *modname;
+
+	if (p->addr) {
+		sym = kallsyms_lookup(p->addr, &size, &p->offset, &modname,
+				      p->func_symbol);
+		if (sym)
+			p->func_symbol_size = strlen(sym);
+	} else {
+		size = strlen(p->func_symbol);
+		p->func_symbol_size = size;
+		p->addr = kallsyms_lookup_name(p->func_symbol);
+		if (!p->addr) {
+			DPRINTK("func name %s not found", p->func_symbol);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int __kprobes logdev_probe_add_watch(struct logdev_probe *lh)
+{
+	struct logdev_probe_watch *p = &lh->watch;
+	const char *sym;
+	char *modname;
+	unsigned long size;
+	unsigned long offset;
+	unsigned long watch = p->var;
+	int ret;
+
+	lh->type = LOGPROBE_BP;
+
+	DPRINTK("set up watch\n");
+	/* can only have one or the other */
+	if (p->hdr.func_symbol[0] && p->hdr.addr)
+		return -EINVAL;
+	if (p->var_symbol[0] && watch)
+		return -EINVAL;
+
+	DPRINTK("passed first test\n");
+	/* TBD: check upper limit ?? */
+	if (!p->hdr.func_symbol[0] && p->hdr.addr < PAGE_OFFSET)
+		return -EINVAL;
+
+	DPRINTK("passed second test\n");
+	/*
+	 * kallsyms requires no scheduling.
+	 */
+	preempt_disable();
+	ret = logdev_probe_update_hdr(&p->hdr);
+	if (ret) {
+		preempt_enable();
+		return ret;
+	}
+
+	if (watch) {
+		sym = kallsyms_lookup(watch, &size, &offset, &modname,
+				      p->var_symbol);
+		if (sym)
+			p->var_symbol_size = strlen(sym);
+	} else if (p->var_symbol) {
+		size = strlen(p->var_symbol);
+		p->var_symbol_size = size;
+		watch = kallsyms_lookup_name(p->var_symbol);
+		if (!watch) {
+			DPRINTK("var name %s not found", p->var_symbol);
+			preempt_enable();
+			return -EINVAL;
+		}
+	}
+	preempt_enable();
+
+	p->var = watch;
+	p->kp.pre_handler = logprobe_watch;
+	p->kp.addr = (kprobe_opcode_t *)p->hdr.addr;
+
+	ret = register_kprobe(&p->kp);
+	if (ret < 0)
+		return ret;
+
+	lh->id = atomic_inc_return(&logdev_probe_next_id);
+
+	return lh->id;
+}
+
+/* Hijack the jprobes registering! */
+
+static int __kprobes logdev_setjmp_pre_handler(struct kprobe *p,
+					       struct pt_regs *regs)
+{
+	__get_cpu_var(jprobe) = container_of(p, struct jprobe, kp);
+	return setjmp_pre_handler(p, regs);
+}
+
+static int __kprobes logdev_longjmp_break_handler(struct kprobe *p,
+						  struct pt_regs *regs)
+{
+	int ret;
+	ret = longjmp_break_handler(p, regs);
+	__get_cpu_var(jprobe) = NULL;
+	return ret;
+}
+
+static int logdev_register_jprobe(struct jprobe *jp)
+{
+	jp->kp.pre_handler = logdev_setjmp_pre_handler;
+	jp->kp.break_handler = logdev_longjmp_break_handler;
+
+	return register_kprobe(&jp->kp);
+}
+
+static int __kprobes logdev_probe_add_func(struct logdev_probe *lh,
+					   const char *fmt1,
+					   const char *fmt2)
+{
+	struct logdev_probe_func *p = &lh->func;
+	int ret;
+
+	lh->type = LOGPROBE_FUNC;
+
+	/*
+	 * kallsyms requires no scheduling.
+	 */
+	preempt_disable();
+
+	ret = logdev_probe_update_hdr(&p->hdr);
+	preempt_enable();
+
+	if (ret)
+		return ret;
+
+	p->fmt1 = fmt1;
+	p->fmt2 = fmt2;
+	p->jp.entry = (kprobe_opcode_t *)logprobe_func;
+	p->jp.kp.addr = (kprobe_opcode_t *)p->hdr.addr;
+
+	ret = logdev_register_jprobe(&p->jp);
+	if (ret < 0)
+		return ret;
+
+	lh->id = atomic_inc_return(&logdev_probe_next_id);
+
+	return lh->id;
+}
+
+static int __kprobes logdev_probe_add_curr(struct logdev_probe *lh)
+{
+	struct logdev_probe_curr *p = &lh->curr;
+	int ret;
+
+	lh->type = LOGPROBE_CURR;
+
+	/*
+	 * kallsyms requires no scheduling.
+	 */
+	preempt_disable();
+	ret = logdev_probe_update_hdr(&p->hdr);
+	preempt_enable();
+	if (ret)
+		return ret;
+
+	p->kp.pre_handler = logprobe_curr;
+	p->kp.addr = (kprobe_opcode_t *)p->hdr.addr;
+
+	ret = register_kprobe(&p->kp);
+	if (ret < 0)
+		return ret;
+
+	lh->id = atomic_inc_return(&logdev_probe_next_id);
+
+	return lh->id;
+}
+
+static int __kprobes logdev_probe_add_var(struct logdev_probe *lh)
+{
+	struct logdev_probe_var *p = &lh->var;
+	int ret;
+
+	lh->type = LOGPROBE_VAR;
+
+	/*
+	 * kallsyms requires no scheduling.
+	 */
+	preempt_disable();
+	ret = logdev_probe_update_hdr(&p->hdr);
+	preempt_enable();
+	if (ret)
+		return ret;
+
+	p->kp.pre_handler = logprobe_var;
+	p->kp.addr = (kprobe_opcode_t *)p->hdr.addr;
+
+	ret = register_kprobe(&p->kp);
+	if (ret < 0)
+		return ret;
+
+	lh->id = atomic_inc_return(&logdev_probe_next_id);
+
+	return lh->id;
+}
+
+/************************ User Land ******************************/
+
+static int logdev_setup_probe_watch(unsigned long __user *arg)
+{
+	struct user_logdev_probe_watch *uprobe;
+	struct logdev_probe *lh = NULL;
+	struct logdev_probe_watch *probe;
+	int ret;
+
+	uprobe = kmalloc(sizeof(*uprobe), GFP_KERNEL);
+
+	if (!uprobe)
+		return -ENOMEM;
+
+	ret = -EFAULT;
+	if(copy_from_user(uprobe, arg, sizeof(*uprobe)))
+		goto fail;
+
+	ret = -ENOMEM;
+	lh = kzalloc(sizeof(*lh), GFP_KERNEL);
+	if (!lh)
+		goto fail;
+
+	probe = &lh->watch;
+
+	ret = -EFAULT;
+	if (uprobe->func) {
+		if (uprobe->func_size > KSYM_NAME_LEN)
+			uprobe->func_size = KSYM_NAME_LEN;
+		if (copy_from_user(probe->hdr.func_symbol, uprobe->func,
+				   uprobe->func_size))
+			goto fail;
+	}
+	if (uprobe->var) {
+		if (uprobe->var_size > KSYM_NAME_LEN)
+			uprobe->var_size = KSYM_NAME_LEN;
+		if (copy_from_user(probe->var_symbol, uprobe->var,
+				   uprobe->var_size))
+			goto fail;
+	}
+	probe->hdr.addr = uprobe->addr;
+	probe->var = uprobe->watch;
+
+	ret = logdev_probe_add_watch(lh);
+	if (ret < 0)
+		goto fail;
+
+	mutex_lock(&probe_list_lock);
+	list_add_tail_rcu(&lh->list, &logdev_probes);
+	mutex_unlock(&probe_list_lock);
+
+	return ret;
+
+ fail:
+	if (uprobe)
+		kfree(uprobe);
+	if (lh)
+		kfree(lh);
+	return ret;
+}
+
+static int logdev_setup_probe_func(unsigned long __user *arg)
+{
+	struct user_logdev_probe_func *uprobe;
+	struct logdev_probe *lh = NULL;
+	struct logdev_probe_func *probe;
+	char *fmt1 = NULL;
+	char *fmt2 = NULL;
+	int ret;
+
+	uprobe = kmalloc(sizeof(*uprobe), GFP_KERNEL);
+
+	if (!uprobe)
+		return -ENOMEM;
+
+	ret = -EFAULT;
+	if(copy_from_user(uprobe, arg, sizeof(*uprobe)))
+		goto fail;
+
+	ret = -EINVAL;
+	if (!uprobe->func || !uprobe->func_size)
+		goto fail;
+
+	ret = -ENOMEM;
+	lh = kzalloc(sizeof(*lh), GFP_KERNEL);
+	if (!lh)
+		goto fail;
+
+	probe = &lh->func;
+
+	ret = -EFAULT;
+	if (uprobe->func_size > KSYM_NAME_LEN)
+		uprobe->func_size = KSYM_NAME_LEN;
+	if (copy_from_user(probe->hdr.func_symbol, uprobe->func,
+			   uprobe->func_size))
+		goto fail;
+
+	if (uprobe->fmt1 && uprobe->fmt1_size) {
+		ret = -ENOMEM;
+		fmt1 = kmalloc(uprobe->fmt1_size+1, GFP_KERNEL);
+		if (!fmt1)
+			goto fail;
+		ret = -EFAULT;
+		if (copy_from_user(fmt1, uprobe->fmt1,
+				   uprobe->fmt1_size))
+			goto fail;
+		fmt1[uprobe->fmt1_size] = 0;
+
+		if (uprobe->fmt2 && uprobe->fmt2_size) {
+			ret = -ENOMEM;
+			fmt2 = kmalloc(uprobe->fmt2_size+1, GFP_KERNEL);
+			if (!fmt2)
+				goto fail;
+			ret = -EFAULT;
+			if (copy_from_user(fmt2, uprobe->fmt2,
+					   uprobe->fmt2_size))
+				goto fail;
+			fmt2[uprobe->fmt2_size] = 0;
+		}
+	}
+
+	ret = logdev_probe_add_func(lh, fmt1, fmt2);
+	if (ret < 0)
+		goto fail;
+
+	mutex_lock(&probe_list_lock);
+	list_add_tail_rcu(&lh->list, &logdev_probes);
+	mutex_unlock(&probe_list_lock);
+
+	return ret;
+
+ fail:
+	DPRINTK("%s FAILED\n",__FUNCTION__);
+	if (fmt1)
+		kfree(fmt1);
+	if (fmt2)
+		kfree(fmt2);
+	if (uprobe)
+		kfree(uprobe);
+	if (lh)
+		kfree(lh);
+	return ret;
+}
+
+static int logdev_setup_probe_curr(unsigned long __user *arg)
+{
+	struct user_logdev_probe_curr *uprobe;
+	struct logdev_probe *lh = NULL;
+	struct logdev_probe_curr *probe;
+	int ret;
+
+	uprobe = kmalloc(sizeof(*uprobe), GFP_KERNEL);
+
+	if (!uprobe)
+		return -ENOMEM;
+
+	ret = -EFAULT;
+	if(copy_from_user(uprobe, arg, sizeof(*uprobe)))
+		goto fail;
+
+	ret = -ENOMEM;
+	lh = kzalloc(sizeof(*lh), GFP_KERNEL);
+	if (!lh)
+		goto fail;
+
+	probe = &lh->curr;
+
+	ret = -EFAULT;
+	if (uprobe->func) {
+		if (uprobe->func_size > KSYM_NAME_LEN)
+			uprobe->func_size = KSYM_NAME_LEN;
+		if (copy_from_user(probe->hdr.func_symbol, uprobe->func,
+				   uprobe->func_size))
+			goto fail;
+	}
+	/* yes this can be negative! */
+	probe->index = uprobe->offset;
+
+	if (uprobe->prefix && uprobe->prefix_size > 0) {
+		probe->prefix = kmalloc(uprobe->prefix_size + 1, GFP_KERNEL);
+		if (copy_from_user(probe->prefix, uprobe->prefix,
+				   uprobe->prefix_size))
+			goto fail;
+		probe->prefix[uprobe->prefix_size] = 0;
+		probe->prefix_size = uprobe->prefix_size + 1;
+	}
+	probe->hdr.addr = uprobe->addr;
+
+	ret = logdev_probe_add_curr(lh);
+	if (ret < 0)
+		goto fail;
+
+	mutex_lock(&probe_list_lock);
+	list_add_tail_rcu(&lh->list, &logdev_probes);
+	mutex_unlock(&probe_list_lock);
+
+	return ret;
+
+ fail:
+	if (uprobe)
+		kfree(uprobe);
+	if (lh)
+		kfree(lh);
+	return ret;
+}
+
+static int logdev_setup_probe_var(unsigned long __user *arg)
+{
+	struct user_logdev_probe_var *uprobe;
+	struct logdev_probe *lh = NULL;
+	struct logdev_probe_var *probe;
+	int ret;
+
+	uprobe = kmalloc(sizeof(*uprobe), GFP_KERNEL);
+
+	if (!uprobe)
+		return -ENOMEM;
+
+	ret = -EFAULT;
+	if(copy_from_user(uprobe, arg, sizeof(*uprobe)))
+		goto fail;
+
+	ret = -ENOMEM;
+	lh = kzalloc(sizeof(*lh), GFP_KERNEL);
+	if (!lh)
+		goto fail;
+
+	probe = &lh->var;
+
+	probe->type = uprobe->type;
+	switch(probe->type) {
+		case LOGPROBE_TYPE_PREEMPT:
+			break;
+	default:
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	ret = -EFAULT;
+	if (uprobe->func) {
+		if (uprobe->func_size > KSYM_NAME_LEN)
+			uprobe->func_size = KSYM_NAME_LEN;
+		if (copy_from_user(probe->hdr.func_symbol, uprobe->func,
+				   uprobe->func_size))
+			goto fail;
+	}
+
+	if (uprobe->prefix && uprobe->prefix_size > 0) {
+		probe->prefix = kmalloc(uprobe->prefix_size + 1, GFP_KERNEL);
+		if (copy_from_user(probe->prefix, uprobe->prefix,
+				   uprobe->prefix_size))
+			goto fail;
+		probe->prefix[uprobe->prefix_size] = 0;
+		probe->prefix_size = uprobe->prefix_size + 1;
+	}
+	probe->hdr.addr = uprobe->addr;
+
+	ret = logdev_probe_add_var(lh);
+	if (ret < 0)
+		goto fail;
+
+	mutex_lock(&probe_list_lock);
+	list_add_tail_rcu(&lh->list, &logdev_probes);
+	mutex_unlock(&probe_list_lock);
+
+	return ret;
+
+ fail:
+	if (uprobe)
+		kfree(uprobe);
+	if (lh)
+		kfree(lh);
+	return ret;
+}
+
+static int logdev_probe_ioctl(struct inode *inode, struct file *filp,
+			      unsigned int cmd, unsigned long arg)
+{
+	int ret = 0;
+
+	switch (cmd) {
+	case LOGPROBE_ADDWATCH:
+
+		ret = logdev_setup_probe_watch((unsigned long __user *)arg);
+
+		break;
+
+	case LOGPROBE_ADDFUNC:
+
+		ret = logdev_setup_probe_func((unsigned long __user *)arg);
+
+		break;
+
+	case LOGPROBE_ADDCURR:
+
+		ret = logdev_setup_probe_curr((unsigned long __user *)arg);
+
+		break;
+
+	case LOGPROBE_ADDVAR:
+
+		ret = logdev_setup_probe_var((unsigned long __user *)arg);
+
+		break;
+
+	case LOGPROBE_DELPOINT:
+	{
+		struct logdev_probe *lp;
+		unsigned long id = arg;
+
+		mutex_lock(&probe_list_lock);
+		list_for_each_entry_rcu(lp, &logdev_probes, list)
+			if (lp->id == id)
+				break;
+		if (&lp->list != &logdev_probes)
+			list_del_rcu(&lp->list);
+		else
+			lp = NULL;
+		mutex_unlock(&probe_list_lock);
+
+		if (!lp)
+			return -EINVAL;
+
+		switch (lp->type) {
+		case LOGPROBE_BP:
+			unregister_kprobe(&lp->watch.kp);
+			synchronize_rcu();
+			break;
+		case LOGPROBE_FUNC:
+			unregister_jprobe(&lp->func.jp);
+			synchronize_rcu();
+
+			if (lp->func.fmt1)
+				kfree(lp->func.fmt1);
+			if (lp->func.fmt2)
+				kfree(lp->func.fmt2);
+			break;
+		case LOGPROBE_CURR:
+			unregister_kprobe(&lp->curr.kp);
+			synchronize_rcu();
+			if (lp->curr.prefix)
+				kfree(lp->curr.prefix);
+			break;
+		case LOGPROBE_VAR:
+			unregister_kprobe(&lp->var.kp);
+			synchronize_rcu();
+			if (lp->var.prefix)
+				kfree(lp->var.prefix);
+			break;
+		default:
+			printk(KERN_WARNING "unregistering logdev probe of"
+			       "unknown type %d\n",
+			       lp->type);
+			/*
+			 * Don't even free this. We don't know where it
+			 * is registered, and this is most certainly a bug!
+			 */
+			return -EINVAL;
+		}
+
+		kfree(lp);
+
+		break;
+	}
+	default:
+		ret = -ENOTTY;
+	}
+
+	return ret;
+}
+
+
+/******************* List kprobe entries *****************/
+
+static void __kprobes *s_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	struct logdev_probe *p = NULL;
+	int l = 0;
+
+	list_for_each_entry(p, &logdev_probes, list) {
+		if (l++ >= *pos)
+			break;
+	}
+
+	(*pos)++;
+
+	if (&p->list == &logdev_probes)
+		return NULL;
+
+	return p;
+}
+
+static void __kprobes *s_start(struct seq_file *m, loff_t *pos)
+	__acquires(logdev_dev.lock)
+{
+	struct logdev_probe *p = NULL;
+	loff_t l = 0;
+
+	rcu_read_lock();
+
+	list_for_each_entry(p, &logdev_probes, list) {
+		if (l++ >= *pos)
+			break;
+	}
+
+	if (&p->list == &logdev_probes)
+		return NULL;
+
+	(*pos)++;
+
+	return p;
+}
+
+static void __kprobes s_stop(struct seq_file *m, void *p)
+	__releases(logdev_dev.lock)
+{
+	rcu_read_unlock();
+}
+
+static void hdr_show(struct seq_file *m, struct logdev_probe_hdr *p)
+{
+	seq_printf(m,"%s : %p\n",
+		   p->func_symbol,
+		   (void*)p->addr);
+}
+
+static int __kprobes s_show(struct seq_file *m, void *v)
+{
+	struct logdev_probe *lh = v;
+	struct logdev_probe_watch *watch = &lh->watch;
+	struct logdev_probe_func *func = &lh->func;
+	struct logdev_probe_curr *curr = &lh->curr;
+	struct logdev_probe_var *var = &lh->var;
+
+	seq_printf(m, "%ld:\t", lh->id);
+	switch (lh->type) {
+	case LOGPROBE_BP:
+		hdr_show(m, &watch->hdr);
+		if (watch->var)
+			seq_printf(m,"\t%s : %p\n",
+				   watch->var_symbol,
+				   (void*)watch->var);
+		break;
+	case LOGPROBE_FUNC:
+		hdr_show(m, &func->hdr);
+		if (func->fmt1)
+			seq_printf(m,"\tfmt1: %s\n",
+				   func->fmt1);
+		if (func->fmt2)
+			seq_printf(m,"\tfmt2: %s\n",
+				   func->fmt2);
+		break;
+
+	case LOGPROBE_CURR:
+		hdr_show(m, &curr->hdr);
+		seq_printf(m,"\tindex: %d (0x%x)\n",
+			   curr->index, curr->index);
+		if (curr->prefix)
+			seq_printf(m,"\tprefix: %s\n",
+				   curr->prefix);
+		break;
+
+	case LOGPROBE_VAR:
+		hdr_show(m, &var->hdr);
+		switch (var->type) {
+		case LOGPROBE_TYPE_PREEMPT:
+			seq_printf(m,"\tpreempt_count\n");
+			break;
+		default:
+			seq_printf(m,"\tunknown type\n");
+		}
+		if (curr->prefix)
+			seq_printf(m,"\tprefix: %s\n",
+				   curr->prefix);
+		break;
+
+	default:
+		seq_printf(m,"tunknown type %d\n",
+			   lh->type);
+	}
+	return 0;
+}
+
+static struct seq_operations logdev_seq_op = {
+	.start = s_start,
+	.next = s_next,
+	.stop = s_stop,
+	.show = s_show,
+};
+
+/******************* end list kprobes *****************/
+
+static int logdev_probe_open (struct inode *inode, struct file *filp)
+{
+	int ret;
+
+	ret = seq_open(filp, &logdev_seq_op);
+	if (!ret) {
+		struct seq_file *m = filp->private_data;
+		m->private = inode->i_private;
+	}
+
+	return ret;
+}
+
+
+static struct file_operations logdev_probe_fops = {
+	.read		= seq_read,
+	.ioctl		= logdev_probe_ioctl,
+	.open		= logdev_probe_open,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+/************************ End User Land ******************************/
+
+
+
+static int __init logdev_probe_init(void)
+{
+	debugfs_create_file("probe", 0600, logdev_d,
+			    NULL, &logdev_probe_fops);
+
+	logdev_register_callback(LOGPROBE_ID_WATCH, logdev_probe_callback);
+	logdev_register_callback(LOGPROBE_ID_FUNC, logdev_probe_callback);
+	logdev_register_callback(LOGPROBE_ID_CURR, logdev_probe_callback);
+	logdev_register_callback(LOGPROBE_ID_VAR, logdev_probe_callback);
+
+	/* just some decent number */
+	logdev_probe_buffer_sz = 256;
+	logdev_probe_buffer = kmalloc(logdev_probe_buffer_sz, GFP_KERNEL);
+
+	/* If we fail, really no harm done */
+	if (!logdev_probe_buffer) {
+		logdev_probe_buffer_sz = 0;
+		printk("logdev_probe: Warning, couldn't allocate func buffer\n");
+	}
+
+	return 0;
+}
+
+module_init(logdev_probe_init);
Index: linux-2.6.21.5-rt20/kernel/logdev/logdev_relay.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21.5-rt20/kernel/logdev/logdev_relay.c	2007-07-11 14:28:50.000000000 +0200
@@ -0,0 +1,175 @@
+/*
+ * logdev_relay.c
+ *
+ * Copyright (C) 2006 Tom Zanussi ([email protected]), IBM Corp
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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; version 2 of the License (not later!)
+ *
+ * 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
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/kthread.h>
+#include <linux/syscalls.h>
+#include <linux/cpumask.h>
+#include <linux/notifier.h>
+#include <linux/nmi.h>
+#include <linux/delay.h>
+#include <linux/relay.h>
+#include <linux/logdev.h>
+#include <linux/debugfs.h>
+
+#include "logdev_priv.h"
+
+static int pages = CONFIG_LOGDEV_PAGES;
+
+/*
+ * Relay structures
+ */
+struct logdev_dev_priv {
+	int cpu;
+	struct rchan_buf *buf;
+	loff_t read_pos;
+};
+DEFINE_PER_CPU(struct logdev_dev_priv, logdev_dev_priv);
+#define get_logdev_priv(cpu) &per_cpu(logdev_dev_priv, cpu)
+
+static struct rchan *logdev_chan;
+
+DEFINE_PER_CPU(struct logdev_dev_priv, logdev_dev_priv);
+#define get_logdev_priv(cpu) &per_cpu(logdev_dev_priv, cpu)
+
+int logdev_copy_from_dev(struct logdev_dev *ldev, void *buffer, int size)
+{
+	struct logdev_dev_priv *dev = ldev->priv;
+	struct rchan_buf *buf = dev->buf;
+
+	return relay_kernel_read(buffer, size, &dev->read_pos, buf);
+}
+
+int logdev_copy_to_dev(struct logdev_dev *ldev, const void *dat,
+			      int size)
+{
+	struct logdev_dev_priv *dev = ldev->priv;
+	struct rchan_buf *buf = dev->buf;
+
+	relay_write(buf->chan, dat, size);
+
+	return size;
+}
+
+static int logdev_subbuf_start(struct rchan_buf *buf,
+			       void *subbuf,
+			       void *prev_subbuf,
+			       unsigned int prev_padding)
+{
+	return 1;
+}
+
+
+/*
+ * file_create() callback.  Creates relay file in debugfs.
+ */
+static struct dentry *create_buf_file_handler(const char *filename,
+					      struct dentry *parent,
+					      int mode,
+					      struct rchan_buf *buf,
+					      int *is_global)
+{
+	struct dentry *buf_file;
+
+	buf_file = debugfs_create_file(filename, mode, parent, buf,
+				       &relay_file_operations);
+
+	return buf_file;
+}
+
+/*
+ * file_remove() default callback.  Removes relay file in debugfs.
+ */
+static int remove_buf_file_handler(struct dentry *dentry)
+{
+	debugfs_remove(dentry);
+
+	return 0;
+}
+
+/*
+ * relay callbacks
+ */
+static struct rchan_callbacks logdev_relay_callbacks =
+{
+	.subbuf_start = logdev_subbuf_start,
+	.create_buf_file = create_buf_file_handler,
+	.remove_buf_file = remove_buf_file_handler,
+};
+
+int initialize_logdev(void)
+{
+	/*
+	 * debugfs may not be ready when we initialize
+	 * logdev. So postpone this later.
+	 */
+	return 0;
+}
+
+void logdev_cleanup_priv(struct logdev_dev *ldev)
+{
+	if (logdev_chan)
+		relay_close(logdev_chan);
+}
+
+int __init logdev_misc_init(void)
+{
+	struct logdev_dev *ldev;
+	struct logdev_dev_priv *dev;
+	struct rchan *chan;
+	int subbufs;
+	int cpu;
+
+	/* round up */
+	subbufs = (pages + 1) >> 1;
+
+	chan = relay_open("dev", logdev_d,
+			  PAGE_SIZE * 2,
+			  subbufs,
+			  &logdev_relay_callbacks);
+	if (!chan)
+		return -1;
+
+	for_each_present_cpu(cpu) {
+
+		if (cpu >= LOGDEV_CPUS)
+			break;
+
+		ldev = get_logdev(cpu);
+		dev = get_logdev_priv(cpu);
+		dev->cpu = cpu;
+		ldev->priv = dev;
+
+		dev->cpu = cpu;
+		dev->buf = chan->buf[cpu];
+		ldev->init = LOGDEV_DEV_RUNNING;
+	}
+
+	return 0;
+}
+
+/* just needs to be after postcore init */
+arch_initcall(logdev_misc_init);
+
Index: linux-2.6.21.5-rt20/kernel/logdev/logdev_ringbuf.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21.5-rt20/kernel/logdev/logdev_ringbuf.c	2007-07-11 14:28:50.000000000 +0200
@@ -0,0 +1,482 @@
+/*
+ * logdev_ringbuf.c
+ *
+ * Copyright (C) 2004-2006 Steven Rostedt <[email protected]>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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; version 2 of the License (not later!)
+ *
+ * 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
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/proc_fs.h>
+#include <linux/time.h>
+#include <linux/netpoll.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/skbuff.h>
+#include <linux/kthread.h>
+#include <linux/syscalls.h>
+#include <linux/cpumask.h>
+#include <linux/notifier.h>
+#include <linux/nmi.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/bootmem.h>
+#include <linux/debugfs.h>
+#include <linux/kprobes.h>
+#include <linux/logdev.h>
+
+#include <asm/uaccess.h>
+
+#include "logdev_priv.h"
+
+/*
+ * If need be, we can be placed really early before memory
+ * is locate, so if a flag is set, use bootmem otherwise use kmalloc.
+ * The earliest we can call logdev_init is just after smp_prepare_boot_cpu.
+ */
+#undef LOGDEV_USE_BOOTMEM
+
+static int pages = CONFIG_LOGDEV_PAGES;
+
+static void *logdev_malloc(int size)
+{
+#ifndef LOGDEV_USE_BOOTMEM
+	return kmalloc(size, GFP_KERNEL);
+#else
+	return alloc_bootmem(sizeof(int));
+#endif
+}
+
+static void *logdev_get_free_pages(int order)
+{
+#ifndef LOGDEV_USE_BOOTMEM
+	return (void*)__get_free_pages(GFP_KERNEL,order);
+#else
+	return alloc_bootmem((1<<order)*PAGE_SIZE);
+#endif
+}
+
+static void logdev_kfree(const void *data)
+{
+#ifndef LOGDEV_USE_BOOTMEM
+	return kfree(data);
+#endif
+}
+
+static void logdev_free_page(unsigned long page)
+{
+#ifndef LOGDEV_USE_BOOTMEM
+	return free_page(page);
+#endif
+}
+
+DEFINE_PER_CPU(struct logdev_dev_priv, logdev_dev_priv);
+
+/* ---- User land interface ---- */
+
+static DEFINE_MUTEX(user_mutex);
+
+/*
+ * User buffer is used to get data from userland, and this can sleep when
+ * copying.
+ */
+static char user_buffer[PAGE_SIZE];
+
+
+/************************ User Land ******************************/
+
+ssize_t logdev_write(struct file *filp, const char *buf, size_t count,
+		     loff_t *f_pos)
+{
+	struct logdev_dev *ldev = filp->private_data;
+	struct logdev_dev_priv *dev;
+	unsigned long flags;
+
+	if (count > PAGE_SIZE)
+		count = PAGE_SIZE;
+
+	if (mutex_lock_interruptible(&user_mutex))
+		return -EINTR;
+
+	if (!dev_running(ldev))
+		goto out;
+	dev = ldev->priv;
+
+	if (copy_from_user(user_buffer,buf,count)) {
+		count = -EFAULT;
+		goto out;
+	}
+
+	spin_lock_irqsave(&ldev->lock,flags);
+	count = logdev_copy_to_dev(ldev,user_buffer,count);
+	spin_unlock_irqrestore(&ldev->lock,flags);
+
+	/* wake up those waiting for data */
+	if (waitqueue_active(&dev->wait))
+		wake_up_interruptible(&dev->wait);
+
+out:
+	mutex_unlock(&user_mutex);
+	return count;
+}
+
+ssize_t logdev_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
+{
+	struct logdev_dev *ldev = filp->private_data;
+	struct logdev_dev_priv *dev;
+	unsigned long flags;
+
+	if (mutex_lock_interruptible(&user_mutex))
+		return -EINTR;
+
+	if (!dev_running(ldev))
+		goto out_up;
+
+	dev = ldev->priv;
+
+	spin_lock_irqsave(&ldev->lock,flags);
+
+	if (!dev->size) {
+
+		/* TBD - FIXME */
+#if 1
+
+		count = 0;
+		goto out;
+#endif
+
+		if (filp->f_flags & O_NONBLOCK) {
+			count = -EAGAIN;
+			goto out;
+		}
+
+		do {
+			DECLARE_WAITQUEUE(wait,current);
+			current->state = TASK_INTERRUPTIBLE;
+			add_wait_queue(&dev->wait,&wait);
+			spin_unlock_irqrestore(&ldev->lock,flags);
+			schedule();
+			spin_lock_irqsave(&ldev->lock,flags);
+			remove_wait_queue(&dev->wait,&wait);
+			if (dev->size)
+				break;
+			if (signal_pending(current)) {
+				count = -ERESTARTSYS;
+				goto out;
+			}
+		} while(1);
+	}
+
+	if (count > PAGE_SIZE)
+		count = PAGE_SIZE;
+
+
+	count = logdev_copy_from_dev(ldev,user_buffer,count);
+
+ out:
+	/* We can't be corrupted if we have no data */
+	if (!dev->size)
+		dev->corrupted = 0;
+	spin_unlock_irqrestore(&ldev->lock,flags);
+
+	if (count > 0) {
+		/* Well if we fail here, we just lost the data read :-( */
+		if (copy_to_user(buf,user_buffer,count))
+			count = -EFAULT;
+	}
+ out_up:
+	mutex_unlock(&user_mutex);
+
+	return count;
+}
+
+static int logdev_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg)
+{
+	return -ENOTTY;
+}
+
+static int logdev_close(struct inode *inode, struct file *filp)
+{
+#if 0
+	int cpu = (int)filp->private_data;
+#endif
+	return 0;
+}
+
+
+static int logdev_open (struct inode *inode, struct file *filp)
+{
+	filp->private_data = inode->i_private;
+	return 0;
+}
+
+/**
+ *	logdev_poll - poll file op for logdev
+ *	@filp: the file
+ *	@wait: poll table
+ *
+ *	Poll implemention.
+ */
+static unsigned int
+logdev_poll(struct file *filp, poll_table *wait)
+{
+	struct logdev_dev *dev = (struct logdev_dev*)filp->private_data;
+	unsigned int mask = 0;
+
+	(void)dev;
+#if 0
+	if (filp->f_mode & FMODE_READ) {
+		poll_wait(filp, &app->read_wait, wait);
+		if (!empty_channel(dev))
+			mask |= POLLIN | POLLRDNORM;
+	}
+#endif
+
+	return mask;
+}
+
+static struct file_operations logdev_fops = {
+	.read		= logdev_read,
+	.write		= logdev_write,
+	.ioctl		= logdev_ioctl,
+	.open		= logdev_open,
+	.release	= logdev_close,
+	.poll		= logdev_poll,
+	.llseek		= no_llseek,
+};
+
+/************************ End User Land ******************************/
+
+
+/******************* entry debugging interface *****************/
+
+static void __kprobes *s_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	struct logdev_dev *ldev = m->private;
+	struct logdev_dev_priv *dev = ldev->priv;
+	int i = (int)*pos;
+
+	(*pos)++;
+
+	if (i >= dev->len)
+		return NULL;
+
+	return &dev->entry[i];
+}
+
+static void __kprobes *s_start(struct seq_file *m, loff_t *pos)
+	__acquires(logdev_dev.lock)
+{
+	struct logdev_dev *dev = m->private;
+	void *p = NULL;
+	loff_t l = 0;
+
+	/*
+	 * A little strong? Perhaps, but we know that this is bad right
+	 * from the start. Anyway this is for debugging purposes only,
+	 * so it's OK, as well as the big latency we get by turning off
+	 * intrerrupts. But we also never know who will be locking
+	 * this.
+	 */
+	if (irqs_disabled())
+		BUG();
+
+	local_irq_disable();
+	spin_lock(&dev->lock);
+	for (p = (void *)1; p && l < *pos; p = s_next(m,p,&l))
+		;
+
+	return p;
+}
+
+static void __kprobes s_stop(struct seq_file *m, void *p)
+	__releases(logdev_dev.lock)
+{
+	struct logdev_dev *dev = m->private;
+	spin_unlock(&dev->lock);
+	local_irq_enable();
+}
+
+static int __kprobes s_show(struct seq_file *m, void *v)
+{
+	int i = (long)(v);
+	struct logdev_entry *entry = v;
+	struct logdev_dev *ldev;
+	struct logdev_dev_priv *dev;
+
+	ldev = m->private;
+	dev = ldev->priv;
+
+	if (i == 1) {
+		seq_printf(m,"Logdev:\n");
+		seq_printf(m,"\tlen:\t%d\n",dev->len);
+		seq_printf(m,"\tsize:\t%d\n",dev->size);
+		seq_printf(m,"\tstart:\t%d\n",dev->start);
+		seq_printf(m,"\tend:\t%d\n",dev->end);
+		seq_printf(m,"\tcorrupted:%d\n",dev->corrupted);
+		seq_printf(m,"\n\tEntries:\n");
+
+	} else {
+		i = (int)((char*)entry - (char*)dev->entry) /
+			sizeof(struct logdev_entry);
+
+		seq_printf(m,"\t  %d:\t%8u : %8u\tsize: %lu\n", i,
+			   entry->head, entry->tail,
+			   LOGDEV_ENTRY_SIZE(entry));
+	}
+
+
+	return 0;
+}
+
+static struct seq_operations logdev_proc_op = {
+	.start = s_start,
+	.next = s_next,
+	.stop = s_stop,
+	.show = s_show,
+};
+
+static int logdev_entries_open (struct inode *inode, struct file *file)
+{
+	int ret;
+
+	ret = seq_open(file, &logdev_proc_op);
+	if (!ret) {
+		struct seq_file *m = file->private_data;
+		m->private = inode->i_private;
+	}
+
+	return ret;
+}
+
+static struct file_operations logdev_entries_fops = {
+	.open = logdev_entries_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
+/******************* end entry debugging interface *****************/
+
+int __init initialize_logdev(void)
+{
+	int cpu;
+
+	for_each_present_cpu(cpu) {
+		struct logdev_dev *ldev;
+		struct logdev_dev_priv *dev;
+		struct logdev_entry *entry;
+		int i;
+
+		ldev = get_logdev(cpu);
+		dev = get_logdev_priv(cpu);
+		dev->cpu = cpu;
+		ldev->priv = dev;
+
+		dev->len = pages;
+
+		dev->entry =
+			logdev_malloc(sizeof(struct logdev_entry)*dev->len);
+		if (!dev->entry)
+			return -ENOMEM;
+
+		memset(dev->entry,0,sizeof(*dev->entry)*dev->len);
+
+		for (i=0,entry=dev->entry; i<dev->len; i++,entry++) {
+			entry->dat = logdev_get_free_pages(0);
+			if (!entry->dat)
+				return -ENOMEM;
+		}
+		init_waitqueue_head(&dev->wait);
+
+		ldev->init = LOGDEV_DEV_RUNNING;
+	}
+
+	return 0;
+}
+
+void __init logdev_cleanup_priv(struct logdev_dev *ldev)
+{
+	struct logdev_dev_priv *dev = ldev->priv;
+
+	if (!ldev->init)
+		return;
+
+	if (dev->entry) {
+		struct logdev_entry *entry;
+		int i;
+
+		for (i=0, entry=dev->entry; i<dev->len; i++,entry++)
+			if (entry->dat) {
+				logdev_free_page((unsigned long)entry->dat);
+				entry->dat = NULL;
+			}
+		dev->len = 0;
+		logdev_kfree(dev->entry);
+		dev->entry = NULL;
+		ldev->init = 0;
+	}
+}
+
+int __init logdev_misc_init(void)
+{
+	struct logdev_dev *ldev;
+	struct logdev_dev_priv *dev;
+	struct dentry *entries;
+	int cpu;
+	char buf[16];
+
+	if (!logdev_d)
+		return 0;
+
+	entries = debugfs_create_dir("entry", logdev_d);
+
+	for_each_present_cpu(cpu) {
+
+		if (cpu >= LOGDEV_CPUS)
+			break;
+
+		ldev = get_logdev(cpu);
+		if (!ldev->init)
+			continue;
+
+		dev = ldev->priv;
+
+		/*
+		 * Setup the debugfs.
+		 */
+		sprintf(buf, "dev%d", cpu);
+		debugfs_create_file(buf, 0444, logdev_d,
+				    ldev, &logdev_fops);
+
+		if (entries) {
+			sprintf(buf,"%d",cpu);
+			debugfs_create_file(buf, 0444, entries,
+					    ldev, &logdev_entries_fops);
+		}
+	}
+
+	return 0;
+}
+
+/* just needs to be after postcore init */
+arch_initcall(logdev_misc_init);
Index: linux-2.6.21.5-rt20/kernel/logdev/logdev_ringbuf.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21.5-rt20/kernel/logdev/logdev_ringbuf.h	2007-07-11 14:28:50.000000000 +0200
@@ -0,0 +1,158 @@
+#ifndef __LINUX_LOGDEV_RINGBUF_H
+#define __LINUX_LOGDEV_RINGBUF_H
+
+/*
+ * Ring buffer structures
+ */
+struct logdev_entry {
+	unsigned int head;
+	unsigned int tail;
+	char *dat;
+};
+
+struct logdev_dev_priv {
+	struct logdev_entry *entry;
+	int cpu;
+	int size;
+	int len;
+	int start;
+	int end;
+	int corrupted;
+	wait_queue_head_t wait;
+};
+
+DECLARE_PER_CPU(struct logdev_dev_priv, logdev_dev_priv);
+#define get_logdev_priv(cpu) &per_cpu(logdev_dev_priv, cpu)
+
+#define LOGDEV_ENTRY_SIZE(e) ((((e)->tail - (e)->head)) & (PAGE_SIZE-1))
+#define LOGDEV_ENTRY_FREE(e) ((PAGE_SIZE-1) - LOGDEV_ENTRY_SIZE(e))
+#define LOGDEV_ENTRY_ADD(e,x) ((e) = ((e) + x) & (PAGE_SIZE-1))
+#define LOGDEV_ENTRY_INC(e) LOGDEV_ENTRY_ADD(e,1)
+#define LOGDEV_ENTRY_MAX (PAGE_SIZE-1)
+
+static inline int logdev_copy_from_dev(struct logdev_dev *ldev, void *buf,
+				       int size)
+{
+	struct logdev_dev_priv *dev = ldev->priv;
+	struct logdev_entry *entry = &dev->entry[dev->start];
+	int ret = 0;
+
+	if (size < 0) {
+		printk("logdev_copy_from_dev: size < 0 ???\n");
+		return -1;
+	}
+	if (size > dev->size)
+		size = dev->size;
+
+	while (size && dev->size) {
+		int copy = size;
+		int used;
+
+		if (!LOGDEV_ENTRY_SIZE(entry)) {
+			dev->start = (dev->start + 1) % dev->len;
+			entry = &dev->entry[dev->start];
+		}
+
+		if (copy > (used=LOGDEV_ENTRY_SIZE(entry)))
+			copy = used;
+		if (entry->head+copy > PAGE_SIZE)
+			copy = PAGE_SIZE - entry->head;
+		memcpy(buf,entry->dat+entry->head,copy);
+		LOGDEV_ENTRY_ADD(entry->head,copy);
+		buf += copy;
+		dev->size -= copy;
+		size -= copy;
+		ret += copy;
+
+	}
+
+	return ret;
+}
+
+static inline void move_start_to_next_entry(struct logdev_dev *ldev)
+{
+	struct logdev_dev_priv *dev = ldev->priv;
+	struct logdev_entry *entry = &dev->entry[dev->start];
+	int start = dev->start;
+	int size;
+	struct logdev_header hdr;
+
+	if (unlikely(dev->corrupted))
+		goto corrupted;
+
+	while (start == dev->start) {
+		logdev_copy_from_dev(ldev,(char*)&hdr,sizeof(hdr));
+		entry = &dev->entry[dev->start];
+
+		if (!logdev_valid(hdr.id)) {
+			dev->corrupted = 1;
+			if (start == dev->start)
+				goto corrupted;
+			return;
+		}
+
+		size = sizeof(hdr);
+		while (size < hdr.size) {
+			int count = hdr.size - size;
+			if (count > LOGDEV_ENTRY_SIZE(entry))
+				count = LOGDEV_ENTRY_SIZE(entry);
+			dev->size -= count;
+			size += count;
+			LOGDEV_ENTRY_ADD(entry->head,count);
+			if (LOGDEV_ENTRY_SIZE(entry) == 0) {
+				dev->start = (dev->start + 1) % dev->len;
+				entry->head = entry->tail = 0;
+				entry = &dev->entry[dev->start];
+			}
+		}
+	}
+	return;
+
+ corrupted:
+	/* Don't trust headers, just skip to the next entry */
+	dev->size -= LOGDEV_ENTRY_SIZE(entry);
+	entry->head = entry->tail = 0;
+	dev->start = (dev->start+1) % dev->len;
+	return;
+}
+
+static inline int logdev_copy_to_dev(struct logdev_dev *ldev, const void *dat,
+				     int size)
+{
+	struct logdev_dev_priv *dev = ldev->priv;
+	struct logdev_entry *entry = &dev->entry[dev->end];
+	const char *buf = dat;
+	int ret = 0;
+
+	while (size) {
+		int copy;
+		int free;
+		if (LOGDEV_ENTRY_SIZE(entry) == LOGDEV_ENTRY_MAX) {
+			dev->end = (dev->end+1) % dev->len;
+			entry = &dev->entry[dev->end];
+			/* if we wrapped, then clear out this entire
+			 * buffer.
+			 */
+			if (dev->end == dev->start) {
+				move_start_to_next_entry(ldev);
+			}
+		}
+		copy = size;
+		if (copy > (free=LOGDEV_ENTRY_FREE(entry)))
+			copy = free;
+		if (entry->tail+copy > PAGE_SIZE)
+			copy = PAGE_SIZE - entry->tail;
+		memcpy(entry->dat+entry->tail,buf,copy);
+		LOGDEV_ENTRY_ADD(entry->tail,copy);
+		buf += copy;
+		size -= copy;
+		ret += copy;
+		dev->size += copy;
+
+	}
+
+	return ret;
+
+}
+
+#endif /* __LINUX_LOGDEV_RINGBUF_H */
  Add kprobe based logdev markers.

---
 arch/i386/Kconfig.debug            |    5 
 arch/i386/kernel/vmlinux.lds.S     |   17 +
 arch/x86_64/Kconfig.debug          |    5 
 arch/x86_64/kernel/vmlinux.lds.S   |   15 +
 include/asm-i386/logdev_marker.h   |  182 +++++++++++++
 include/asm-x86_64/logdev_marker.h |  233 +++++++++++++++++
 include/linux/logdev_marker.h      |   60 ++++
 kernel/logdev/Makefile             |    2 
 kernel/logdev/logdev_marker.c      |  501 +++++++++++++++++++++++++++++++++++++
 kernel/logdev/logdev_tracers.c     |  102 +++++++
 lib/Kconfig.debug                  |   20 +
 11 files changed, 1142 insertions(+)

Index: linux-2.6.21.5-rt20/include/asm-i386/logdev_marker.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21.5-rt20/include/asm-i386/logdev_marker.h	2007-07-11 14:28:53.000000000 +0200
@@ -0,0 +1,182 @@
+/*
+ * logdev_marker.h
+ *
+ * Copyright - 2006 - Steven Rostedt, Red Hat Inc, (srostedt at redhat dot com)
+ */
+#ifndef _ASM_LOGDEV_MARKER_H
+#define _ASM_LOGDEV_MARKER_H
+
+
+/*
+ * eax = 0
+ * ebx = 3
+ * ecx = 1
+ * edx = 2
+ * edi = 7
+ * ebp = 5
+ * esp = 4
+ * esi = 6
+ */
+enum {
+	LD_REGA = 0,
+	LD_REGB = 3,
+	LD_REGC = 1,
+	LD_REGD = 2,
+	LD_REGDI = 7,
+	LD_REGBP = 5,
+	LD_REGSP = 4,
+	LD_REGSI = 6
+};
+
+static inline int logdev_mark_get_reg(unsigned long op)
+{
+	/*
+	 * Strip out the register:
+	 */
+	return (op >> 8) & 0x7;
+}
+
+static inline unsigned long
+logdev_mark_get_reg_content(int reg, struct pt_regs *regs)
+{
+	static int once;
+
+	switch (reg) {
+	case LD_REGA:
+		return regs->eax;
+	case LD_REGB:
+		return regs->ebx;
+	case LD_REGC:
+		return regs->ecx;
+	case LD_REGD:
+		return regs->edx;
+	case LD_REGDI:
+		return regs->edi;
+	case LD_REGBP:
+		return regs->ebp;
+	case LD_REGSP:
+		return regs->esp;
+	case LD_REGSI:
+		return regs->esi;
+	default:
+		if (!once) {
+			printk("unknown reg type %d\n", reg);
+			once = 1;
+		}
+	}
+	return 0;
+}
+
+
+static inline const char *logdev_reg_to_name(int reg)
+{
+	switch (reg) {
+	case LD_REGA:
+		return "eax";
+	case LD_REGB:
+		return "ebx";
+	case LD_REGC:
+		return "ecx";
+	case LD_REGD:
+		return "edx";
+	case LD_REGDI:
+		return "edi";
+	case LD_REGBP:
+		return "ebp";
+	case LD_REGSP:
+		return "esp";
+	case LD_REGSI:
+		return "esi";
+	}
+	return "unknown reg!";
+}
+
+#define LD_MARK_PROLOG(label)						\
+	"1:"								\
+	".section .__logdev_strings,\"a\"\n"				\
+	"__logdev_str_" #label ": .string \"" #label "\"\n"		\
+	".previous\n"							\
+	".section .__logdev_markers,\"a\"\n"				\
+	".long 1b, __logdev_caller__" #label "\n"			\
+	".long __logdev_str_" #label "\n"
+
+#define LD_MARK(label)						\
+	{							\
+		extern void __logdev_caller__ ## label(void);	\
+		asm(						\
+		    LD_MARK_PROLOG(label)			\
+		    ".long 0\n"					\
+		    ".previous"					\
+		    : :	);					\
+	}
+
+#define LD_MARK1(label, arg1)						\
+	{								\
+		extern void __logdev_caller__ ## label(typeof(arg1));	\
+		asm(							\
+		    LD_MARK_PROLOG(label)				\
+		    ".long 1\n"						\
+		    "xorl %0, %0\n"					\
+		    ".short 0\n"					\
+		    ".previous"						\
+		    : :							\
+		    "r"(arg1));						\
+	}
+
+#define LD_MARK2(label, arg1, arg2)					\
+	{								\
+		extern void __logdev_caller__ ## label(typeof(arg1),	\
+						       typeof(arg2));	\
+		asm(							\
+		    LD_MARK_PROLOG(label)				\
+		    ".long 2\n"						\
+		    "xorl %0, %0\n"					\
+		    ".short 0\n"					\
+		    "xorl %1, %1\n"					\
+		    ".short 0\n"					\
+		    ".previous"						\
+		    : :							\
+		    "r"(arg1), "r"(arg2));				\
+	}
+
+#define LD_MARK3(label, arg1, arg2, arg3)				\
+	{								\
+		extern void __logdev_caller__ ## label(typeof(arg1),	\
+						       typeof(arg2),	\
+						       typeof(arg3));	\
+		asm(							\
+		    LD_MARK_PROLOG(label)				\
+		    ".long 3\n"						\
+		    "xorl %0, %0\n"					\
+		    ".short 0\n"					\
+		    "xorl %1, %1\n"					\
+		    ".short 0\n"					\
+		    "xorl %2, %2\n"					\
+		    ".short 0\n"					\
+		    ".previous"						\
+		    : :							\
+		    "r"(arg1), "r"(arg2), "r"(arg3));			\
+	}
+
+#define LD_MARK4(label, arg1, arg2, arg3, arg4)				\
+	{								\
+		extern void __logdev_caller__ ## label(typeof(arg1),	\
+						       typeof(arg2),	\
+						       typeof(arg3),	\
+						       typeof(arg4));	\
+		asm(							\
+		    LD_MARK_PROLOG(label)				\
+		    "xorl %0, %0\n"					\
+		    ".short 0\n"					\
+		    "xorl %1, %1\n"					\
+		    ".short 0\n"					\
+		    "xorl %2, %2\n"					\
+		    ".short 0\n"					\
+		    "xorl %3, %3\n"					\
+		    ".short 0\n"					\
+		    ".previous"						\
+		    : :							\
+		    "r"(arg1), "r"(arg2), "r"(arg3), "r"(arg4));	\
+	}
+
+#endif /* _ASM_LOGDEV_MARKER_H */
Index: linux-2.6.21.5-rt20/include/linux/logdev_marker.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21.5-rt20/include/linux/logdev_marker.h	2007-07-11 14:28:53.000000000 +0200
@@ -0,0 +1,60 @@
+/*
+ * logdev_marker.h
+ *
+ * Copyright - 2006 - Steven Rostedt, Red Hat Inc, (srostedt at redhat dot com)
+ */
+#ifndef _LINUX_LOGDEV_MARKER_H
+#define _LINUX_LOGDEV_MARKER_H
+
+#ifdef CONFIG_LOGDEV_MARKER
+#include <linux/logdev.h>
+#include <asm/logdev_marker.h>
+
+#define LD_CALLER_NAME __logdev_caller__
+#define LDCALLER(caller) __logdev_caller__ ## caller
+
+#elif CONFIG_LOGDEV_MARKER_DIRECT
+
+#include <linux/logdev.h>
+
+/* just call the functions directly */
+
+#define LDCALLER(caller) __logdev_call_direct_ ## caller
+
+#define LD_DOCALL(label, x...)			\
+	do {					\
+		if (logdev_mark_ison())		\
+			LDCALLER(label)(x);	\
+	} while(0)
+#define LD_DOCALL0(label, x...)			\
+	do {					\
+		if (logdev_mark_ison())		\
+			LDCALLER(label)();	\
+	} while(0)
+
+#define LD_MARK(label)			LD_DOCALL0(label)
+#define LD_MARK1(label,arg1)		LD_DOCALL(label,arg1)
+#define LD_MARK2(label,arg1,arg2)	LD_DOCALL(label,arg1,arg2)
+#define LD_MARK3(label,arg1,arg2,arg3)	LD_DOCALL(label,arg1,arg2,arg3)
+#define LD_MARK4(label,arg1,arg2,arg3,arg4) \
+		LD_DOCALL(label,arg1,arg2,arg3,arg4)
+
+#else
+
+#define LD_MARK(label)				do { } while(0)
+#define LD_MARK1(label,arg1)			do { } while(0)
+#define LD_MARK2(label,arg1,arg2)		do { } while(0)
+#define LD_MARK3(label,arg1,arg2,arg3)		do { } while(0)
+#define LD_MARK4(label,arg1,arg2,arg3,arg4)	do { } while(0)
+
+#endif /* CONFIG_LOGDEV_MARKER */
+
+
+#ifdef CONFIG_LOGDEV_TRACING
+
+/* Place tracing prototypes here */
+
+
+#endif /* CONFIG_LOGDEV_TRACING */
+
+#endif /* _LINUX_LOGDEV_MARKER_H */
Index: linux-2.6.21.5-rt20/arch/i386/Kconfig.debug
===================================================================
--- linux-2.6.21.5-rt20.orig/arch/i386/Kconfig.debug	2007-07-11 10:09:48.000000000 +0200
+++ linux-2.6.21.5-rt20/arch/i386/Kconfig.debug	2007-07-11 14:28:53.000000000 +0200
@@ -78,6 +78,11 @@ config X86_MPPARSE
 	depends on X86_LOCAL_APIC && !X86_VISWS
 	default y
 
+config LOGDEV_MARKER
+	bool
+	depends on LOGDEV && KPROBES && !LOGDEV_MARKER_DIRECT
+	default y
+
 config DOUBLEFAULT
 	default y
 	bool "Enable doublefault exception handler" if EMBEDDED
Index: linux-2.6.21.5-rt20/arch/i386/kernel/vmlinux.lds.S
===================================================================
--- linux-2.6.21.5-rt20.orig/arch/i386/kernel/vmlinux.lds.S	2007-07-11 10:09:48.000000000 +0200
+++ linux-2.6.21.5-rt20/arch/i386/kernel/vmlinux.lds.S	2007-07-11 14:28:53.000000000 +0200
@@ -72,6 +72,13 @@ SECTIONS
   	__tracedata_end = .;
   }
 
+#ifdef CONFIG_LOGDEV_MARKER
+  . = ALIGN(4);
+  .__logdev_strings : AT(ADDR(.__logdev_strings) - LOAD_OFFSET) {
+	*(.__logdev_strings)
+  }
+#endif
+
   /* writeable */
   . = ALIGN(4096);
   .data : AT(ADDR(.data) - LOAD_OFFSET) {	/* Data */
@@ -200,6 +207,16 @@ SECTIONS
 	*(.data.percpu)
 	__per_cpu_end = .;
   }
+
+#ifdef CONFIG_LOGDEV_MARKER
+  . = ALIGN(4);
+  __logdev_marker_start = .;
+  .__logdev_markers : AT(ADDR(.__logdev_markers) - LOAD_OFFSET) {
+	*(.__logdev_markers)
+  }
+  __logdev_marker_end = .;
+#endif
+
   . = ALIGN(4096);
   /* freed after init ends here */
 	
Index: linux-2.6.21.5-rt20/kernel/logdev/logdev_marker.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21.5-rt20/kernel/logdev/logdev_marker.c	2007-07-11 14:28:53.000000000 +0200
@@ -0,0 +1,501 @@
+/*
+ * logdev_marker.c
+ *
+ * Copyright (C) 2006 Steven Rostedt, Red Hat Inc.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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; version 2 of the License (not later!)
+ *
+ * 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
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/logdev.h>
+#include <linux/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/kprobes.h>
+#include <linux/ptrace.h>
+#include <linux/kallsyms.h>
+#include <linux/list.h>
+#include <linux/logdev_marker.h>
+
+#include <asm/atomic.h>
+
+#include "logdev_priv.h"
+
+#define MAX_ARGS 4
+
+#undef DPRINTK
+#if 1
+#  define DPRINTK(x...) printk(x)
+#else
+#  define DPRINTK(x...) do { } while(0)
+#endif
+
+static LIST_HEAD(logdev_probes);
+static int logdev_marker_ids;
+
+typedef void (*logdev_func0_t)(void);
+typedef void (*logdev_func1_t)(unsigned long);
+typedef void (*logdev_func2_t)(unsigned long, unsigned long);
+typedef void (*logdev_func3_t)(unsigned long, unsigned long,
+			       unsigned long);
+typedef void (*logdev_func4_t)(unsigned long, unsigned long,
+			       unsigned long, unsigned long);
+
+union logdev_func {
+	logdev_func0_t func0;
+	logdev_func1_t func1;
+	logdev_func2_t func2;
+	logdev_func3_t func3;
+	logdev_func4_t func4;
+};
+
+struct logdev_mark_probe {
+	struct list_head list;
+	struct kprobe kp;
+	int id;
+	int enabled;
+	const char *label;
+	union logdev_func func;
+	unsigned long address;
+	int args;
+	unsigned long arg_regs[MAX_ARGS];
+};
+
+/* ---------------- cut here for user space headers -------------------- */
+
+#define LOGMARK_IOCTL_BASE 'm'
+
+#define MARK_IO(nr) _IO(LOGMARK_IOCTL_BASE, nr)
+#define MARK_IOW(nr, type) _IOW(LOGMARK_IOCTL_BASE, nr, type)
+
+#define LOGMARK_START	MARK_IO(0)
+#define LOGMARK_STOP	MARK_IO(1)
+#define LOGMARK_ENABLE	MARK_IOW(2, unsigned long)
+#define LOGMARK_DISABLE	MARK_IOW(3, unsigned long)
+
+/* ---------------- end of user space header cut ---------------- */
+
+
+/************************ Kprobes ******************************/
+
+static int __kprobes logmark_probe(struct kprobe *kp, struct pt_regs *regs)
+{
+	struct logdev_mark_probe *p =
+		container_of(kp, struct logdev_mark_probe, kp);
+	unsigned long args[MAX_ARGS];
+	int i;
+
+	for (i=0; i < p->args; i++)
+		args[i] = logdev_mark_get_reg_content(p->arg_regs[i], regs);
+
+	switch (p->args) {
+	case 0:
+		p->func.func0();
+		break;
+	case 1:
+		p->func.func1(args[0]);
+		break;
+	case 2:
+		p->func.func2(args[0], args[1]);
+		break;
+	case 3:
+		p->func.func3(args[0], args[1], args[2]);
+		break;
+	case 4:
+		p->func.func4(args[0], args[1], args[2], args[3]);
+		break;
+
+	}
+	return 0;
+}
+
+
+/************************ User Land ******************************/
+
+static DEFINE_MUTEX(logdev_marker_lock);
+
+static int mark_is_on;
+
+static int logdev_mark_enable(void)
+{
+	struct logdev_mark_probe *probe;
+	int ret = 0;
+
+	mutex_lock(&logdev_marker_lock);
+	if (!mark_is_on) {
+		list_for_each_entry(probe, &logdev_probes, list) {
+
+			if (!probe->enabled)
+				continue;
+
+			probe->kp.pre_handler = logmark_probe;
+			probe->kp.addr =
+				(kprobe_opcode_t *)probe->address;
+
+			ret = register_kprobe(&probe->kp);
+			if (ret < 0) {
+				ret = -EINVAL;
+				printk(KERN_WARNING
+				       "logdev_marker: can't register probe\n");
+				break;
+			}
+		}
+		mark_is_on = 1;
+	}
+	mutex_unlock(&logdev_marker_lock);
+
+	return ret;
+}
+
+static void logdev_mark_disable(void)
+{
+	struct logdev_mark_probe *probe;
+
+	mutex_lock(&logdev_marker_lock);
+	if (mark_is_on) {
+		list_for_each_entry(probe, &logdev_probes, list) {
+			if (!probe->enabled)
+				continue;
+			unregister_kprobe(&probe->kp);
+		}
+		mark_is_on = 0;
+	}
+	mutex_unlock(&logdev_marker_lock);
+}
+
+static int logdev_mark_ioctl(struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg)
+{
+	int ret = 0;
+	struct logdev_mark_probe *probe;
+	int id;
+
+	switch (cmd) {
+	case LOGMARK_START:
+
+		ret = logdev_mark_enable();
+
+		break;
+
+	case LOGMARK_STOP:
+
+		logdev_mark_disable();
+
+		break;
+
+	case LOGMARK_ENABLE:
+
+		id = (int)arg;
+
+		/* Assume not found */
+		ret = -EINVAL;
+
+		mutex_lock(&logdev_marker_lock);
+		list_for_each_entry(probe, &logdev_probes, list) {
+			if (probe->id == id) {
+				ret = 0;
+				if (probe->enabled)
+					break; /* nothing to do */
+				if (mark_is_on)
+					ret = register_kprobe(&probe->kp);
+				probe->enabled = 1;
+				break;
+			}
+		}
+		mutex_unlock(&logdev_marker_lock);
+
+		break;
+
+	case LOGMARK_DISABLE:
+
+		id = (int)arg;
+
+		/* Assume not found */
+		ret = -EINVAL;
+
+		mutex_lock(&logdev_marker_lock);
+		list_for_each_entry(probe, &logdev_probes, list) {
+			if (probe->id == id) {
+				ret = 0;
+				if (!probe->enabled)
+					break; /* nothing to do */
+				if (mark_is_on)
+					unregister_kprobe(&probe->kp);
+				probe->enabled = 0;
+				break;
+			}
+		}
+		mutex_unlock(&logdev_marker_lock);
+
+		break;
+
+	default:
+		ret = -ENOTTY;
+	}
+
+	return ret;
+}
+
+
+/******************* List mark entries *****************/
+
+static void __kprobes *s_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	struct logdev_mark_probe *p = NULL;
+	int l = 0;
+
+	list_for_each_entry(p, &logdev_probes, list) {
+		if (l++ >= *pos)
+			break;
+	}
+
+	(*pos)++;
+
+	if (&p->list == &logdev_probes)
+		return NULL;
+
+	return p;
+}
+
+static void __kprobes *s_start(struct seq_file *m, loff_t *pos)
+	__acquires(logdev_dev.lock)
+{
+	struct logdev_mark_probe *p = NULL;
+	loff_t l = 0;
+
+	list_for_each_entry(p, &logdev_probes, list) {
+		if (l++ >= *pos)
+			break;
+	}
+
+	if (&p->list == &logdev_probes)
+		return NULL;
+
+	(*pos)++;
+
+	return p;
+}
+
+static void __kprobes s_stop(struct seq_file *m, void *p)
+	__releases(logdev_dev.lock)
+{
+}
+
+#undef _STR
+#undef STR
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
+static int __kprobes s_show(struct seq_file *m, void *v)
+{
+	struct logdev_mark_probe *lm = v;
+	int i;
+	char namebuf[KSYM_NAME_LEN+1];
+	char *modname;
+	unsigned long offset, size;
+	const char *sym;
+
+	seq_printf(m, "%d: %s (%s)\n",
+		   lm->id, lm->label,
+		   lm->enabled ? "enabled" : "disabled");
+	seq_printf(m, "\tprobe address:\t%p",
+		   (void*)lm->address);
+
+	sym = kallsyms_lookup(lm->address, &size, &offset, &modname, namebuf);
+	if (sym) {
+		if (modname)
+			seq_printf(m, "  (%s+%#lx/%#lx [%s])", sym, offset,
+				size, modname);
+		else
+			seq_printf(m, "  (%s+%#lx/%#lx)", sym, offset, size);
+	}
+	seq_printf(m, "\n");
+
+
+	seq_printf(m, "\tfunc address:\t%p", (void*)lm->func.func0);
+
+	sym = kallsyms_lookup((unsigned long)lm->func.func0, &size, &offset,
+			      &modname, namebuf);
+	if (sym) {
+		int strsz = sizeof(STR(LD_CALLER_NAME))-1;
+		/* skip the add on name */
+		if (strncmp(STR(LD_CALLER_NAME), sym, strsz)==0)
+			sym += strsz;
+
+		if (modname)
+			seq_printf(m, "  (%s+%#lx/%#lx [%s])", sym, offset,
+				size, modname);
+		else
+			seq_printf(m, "  (%s+%#lx/%#lx)", sym, offset, size);
+	}
+	seq_printf(m, "\n");
+
+
+	seq_printf(m, "\targs:\t\t%d\n", lm->args);
+	for (i=0; i < lm->args; i++) {
+		seq_printf(m, "\targ %d reg:\t%s\n",
+			   i, logdev_reg_to_name(lm->arg_regs[i]));
+	}
+	seq_printf(m,"\n");
+	return 0;
+}
+
+static struct seq_operations logdev_seq_op = {
+	.start = s_start,
+	.next = s_next,
+	.stop = s_stop,
+	.show = s_show,
+};
+
+/******************* end list kprobes *****************/
+
+static int logdev_mark_open (struct inode *inode, struct file *filp)
+{
+	int ret;
+
+	ret = seq_open(filp, &logdev_seq_op);
+	if (!ret) {
+		struct seq_file *m = filp->private_data;
+		m->private = inode->i_private;
+	}
+
+	return ret;
+}
+
+
+static struct file_operations logdev_mark_fops = {
+	.read		= seq_read,
+	.ioctl		= logdev_mark_ioctl,
+	.open		= logdev_mark_open,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+
+static int logdev_open_generic(struct inode *inode, struct file *filp)
+{
+	filp->private_data = inode->i_private;
+	return 0;
+}
+
+
+static ssize_t logdev_debug_mark_read(struct file *filp, char __user *ubuf,
+				      size_t cnt, loff_t *ppos)
+{
+	char buf[16];
+	int r;
+
+	r = sprintf(buf, "%d\n", mark_is_on);
+	return simple_read_from_buffer(ubuf, cnt, ppos,
+				       buf, r);
+}
+
+static ssize_t logdev_debug_mark_write(struct file *filp,
+				       const char __user *ubuf,
+				       size_t cnt, loff_t *ppos)
+{
+	int val;
+	char buf[16];
+
+	if (cnt > 15)
+		cnt = 15;
+
+	if(copy_from_user(&buf, ubuf, cnt))
+		return -EFAULT;
+
+	buf[cnt] = 0;
+
+	val = simple_strtoul(buf, NULL, 10) ? 1 : 0;
+
+	if (val)
+		logdev_mark_enable();
+	else
+		logdev_mark_disable();
+
+	filp->f_pos += cnt;
+
+	return cnt;
+}
+
+static struct file_operations logdev_debug_mark_fops = {
+	.open = logdev_open_generic,
+	.read = logdev_debug_mark_read,
+	.write = logdev_debug_mark_write,
+};
+
+/************************ End User Land ******************************/
+
+
+extern unsigned long __logdev_marker_start;
+extern unsigned long __logdev_marker_end;
+
+static int __init logdev_marker_init(void)
+{
+	unsigned long *p;
+	struct logdev_mark_probe *probe;
+
+	debugfs_create_file("marker", 0600, logdev_d,
+			    NULL, &logdev_mark_fops);
+
+	/*
+	 * Arch must make sure that these are aligned by sizeof(long)
+	 *
+	 *  (p is incremented in the loop)
+	 */
+	for (p = &__logdev_marker_start; p < &__logdev_marker_end; ) {
+		int i;
+
+		probe = kzalloc(sizeof(*probe), GFP_KERNEL);
+		if (!probe) {
+			printk(KERN_WARNING "logdev_marker: ran out of memory!\n");
+			break;
+		}
+		probe->id = logdev_marker_ids++;
+		probe->enabled = 1;
+		probe->address = *p++;
+		probe->func.func0 = (logdev_func0_t)*p++;
+		probe->label = (char*)*p++;
+		probe->args = *p++;
+		if (probe->args > MAX_ARGS) {
+			printk(KERN_WARNING "logdev_marker: corrupted mark section\n");
+			kfree(probe);
+			break;
+		}
+		for (i=0; i < probe->args; i++) {
+			probe->arg_regs[i] = logdev_mark_get_reg(*p++);
+			if (probe->arg_regs[i] < 0)
+				break;
+		}
+		if (i < probe->args) {
+			printk(KERN_WARNING "logdev_marker: unknown reg\n");
+			kfree(probe);
+			break;
+		}
+
+		list_add_tail(&probe->list, &logdev_probes);
+	}
+
+	debugfs_create_file("mark", 0644, logdev_d,
+			    (void*)LOGDEV_SW_MARKER_ENABLED,
+			    &logdev_debug_mark_fops);
+
+	return 0;
+}
+
+module_init(logdev_marker_init);
Index: linux-2.6.21.5-rt20/kernel/logdev/Makefile
===================================================================
--- linux-2.6.21.5-rt20.orig/kernel/logdev/Makefile	2007-07-11 14:28:50.000000000 +0200
+++ linux-2.6.21.5-rt20/kernel/logdev/Makefile	2007-07-11 14:28:53.000000000 +0200
@@ -5,3 +5,5 @@ obj-$(CONFIG_LOGDEV_PROBE)	+= logdev_pro
 obj-$(CONFIG_LOGDEV_RINGBUF)	+= logdev_ringbuf.o
 obj-$(CONFIG_LOGDEV_RELAY)	+= logdev_relay.o
 
+obj-$(CONFIG_LOGDEV_MARKER)	+= logdev_marker.o
+obj-$(CONFIG_LOGDEV_TRACING)	+= logdev_tracers.o
Index: linux-2.6.21.5-rt20/lib/Kconfig.debug
===================================================================
--- linux-2.6.21.5-rt20.orig/lib/Kconfig.debug	2007-07-11 14:28:50.000000000 +0200
+++ linux-2.6.21.5-rt20/lib/Kconfig.debug	2007-07-11 14:28:53.000000000 +0200
@@ -623,6 +623,26 @@ config LOGDEV
 	  it allocates a default of 1 meg of memory (in page size units). This
 	  allows for saving data in a ring buffer without the need to allocate.
 
+config LOGDEV_MARKER_DIRECT
+	bool "Enable Logdev direct markers"
+	depends on LOGDEV
+	help
+	  There may be markers placed through out the kernel.  This
+	  option enables the markers to be function calls into the tracing
+	  routine.  The logging is not enabled on boot up and must be started
+	  by a user land tool.  This option may cause a slight performance overhead
+	  evern when disabled.  If you have kprobes, and the kprobe markers
+	  has been ported to your architecture, then it is recommended to use
+	  that instead, since it has a much less overhead when disabled,
+	  although it does have a higher overhead when logging is on.
+
+	  If in doubt, say N
+
+config LOGDEV_TRACING
+	bool
+	depends on LOGDEV_MARKER_DIRECT || LOGDEV_MARKER
+	default y
+
 config LOGDEV_PROBE
 	bool
 	depends on LOGDEV && KPROBES
Index: linux-2.6.21.5-rt20/kernel/logdev/logdev_tracers.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21.5-rt20/kernel/logdev/logdev_tracers.c	2007-07-11 14:28:53.000000000 +0200
@@ -0,0 +1,102 @@
+/*
+ * logdev_tracer.c
+ *
+ * Copyright (C) 2006 Steven Rostedt, Red Hat Inc.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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; version 2 of the License (not later!)
+ *
+ * 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
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/module.h>
+#include <linux/kprobes.h>
+#include <linux/logdev.h>
+#include <linux/logdev_marker.h>
+
+/* ---------------- cut here for user space headers -------------------- */
+
+#define LOGMARK_ID_MARK		0x56580000
+
+struct logdev_mark_hdr {
+	unsigned long long t;
+	int id;
+};
+/* ---------------- end here for user space headers -------------------- */
+
+/* ---------------- start here for user custom headers -------------------- */
+/* Add Tracing IDs and structures here */
+
+/* ---------------- end of user space custom headers ---------------- */
+
+/* TODO, put this in logdev.c so I don't keep copying it */
+static void logdev_print_time_cpu(unsigned long long t, int cpu)
+{
+	unsigned long usec_rem;
+	unsigned long secs;
+
+	usec_rem = do_div(t, 1000000000)/1000;
+	secs = (unsigned long)t;
+
+	printk("[%5lu.%06lu] cpu:%d ",
+	       secs, usec_rem, cpu);
+}
+
+/* ------------------- cut here for user space print -------------- */
+
+/* "s/printk/printf" */
+
+static void logdev_print_hdr(int cpu,
+			     struct logdev_mark_hdr *hdr)
+{
+	logdev_print_time_cpu(hdr->t, cpu);
+}
+
+/* Place printing of traces here */
+
+static void logdev_mark_callback(struct logdev_header *hdr,
+				 struct logdev_custom *custom,
+				 int cpu,
+				 void *rec)
+{
+	struct logdev_mark_hdr *lm = rec;
+
+	switch (lm->id) {
+	/* Add callbacks for traces here */
+	default:
+		printk("Unknown marker callback id %x\n",
+		       lm->id);
+		break;
+	}
+}
+/* ------------------ end cut for user space printing ------------------- */
+
+
+static void __kprobes logmark_hdr(struct logdev_mark_hdr *lm, int id)
+{
+	lm->t = sched_clock();
+	lm->id = id;
+}
+
+
+/* Add tracing callback functions here */
+
+
+static int __init logdev_tracer_init(void)
+{
+	logdev_register_callback(LOGMARK_ID_MARK, logdev_mark_callback);
+	return 0;
+}
+
+module_init(logdev_tracer_init);
Index: linux-2.6.21.5-rt20/include/asm-x86_64/logdev_marker.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21.5-rt20/include/asm-x86_64/logdev_marker.h	2007-07-11 14:28:53.000000000 +0200
@@ -0,0 +1,233 @@
+/*
+ * logdev_marker.h
+ *
+ * Copyright - 2006 - Steven Rostedt, Red Hat Inc, (srostedt at redhat dot com)
+ */
+#ifndef _ASM_LOGDEV_MARKER_H
+#define _ASM_LOGDEV_MARKER_H
+
+
+/*
+ * eax = 0
+ * ebx = 3
+ * ecx = 1
+ * edx = 2
+ * edi = 7
+ * ebp = 5
+ * esp = 4
+ * esi = 6
+ */
+enum {
+	LD_REGA = 0xc03148,
+	LD_REGB = 0xdb3148,
+	LD_REGC = 0xc93148,
+	LD_REGD = 0xd23148,
+	LD_REGDI = 0xff3148,
+	LD_REGBP = 0xed3148,
+	LD_REGSP = 0xe43148,
+	LD_REGSI = 0xf63148,
+	LD_REG8 = 0xc0314d,
+	LD_REG9 = 0xc9314d,
+	LD_REG10 = 0xd2314d,
+	LD_REG11 = 0xdb314d,
+	LD_REG12 = 0xe4314d,
+	LD_REG13 = 0xed314d,
+	LD_REG14 = 0xf6314d,
+	LD_REG15 = 0xff314d,
+};
+
+static inline int logdev_mark_get_reg(unsigned long op)
+{
+	/*
+	 * Use the same operand (xor r,r)
+	 */
+	return op;
+}
+
+static inline unsigned long
+logdev_mark_get_reg_content(int reg, struct pt_regs *regs)
+{
+	static int once;
+
+	switch (reg) {
+	case LD_REGA:
+		return regs->rax;
+	case LD_REGB:
+		return regs->rbx;
+	case LD_REGC:
+		return regs->rcx;
+	case LD_REGD:
+		return regs->rdx;
+	case LD_REGDI:
+		return regs->rdi;
+	case LD_REGBP:
+		return regs->rbp;
+	case LD_REGSP:
+		return regs->rsp;
+	case LD_REGSI:
+		return regs->rsi;
+	case LD_REG8:
+		return regs->r8;
+	case LD_REG9:
+		return regs->r9;
+	case LD_REG10:
+		return regs->r10;
+	case LD_REG11:
+		return regs->r11;
+	case LD_REG12:
+		return regs->r12;
+	case LD_REG13:
+		return regs->r13;
+	case LD_REG14:
+		return regs->r14;
+	case LD_REG15:
+		return regs->r15;
+	default:
+		if (!once) {
+			printk("unknown reg type %d\n", reg);
+			once = 1;
+		}
+	}
+	return 0;
+}
+
+
+static inline const char *logdev_reg_to_name(int reg)
+{
+	switch (reg) {
+	case LD_REGA:
+		return "rax";
+	case LD_REGB:
+		return "rbx";
+	case LD_REGC:
+		return "rcx";
+	case LD_REGD:
+		return "rdx";
+	case LD_REGDI:
+		return "rdi";
+	case LD_REGBP:
+		return "rbp";
+	case LD_REGSP:
+		return "rsp";
+	case LD_REGSI:
+		return "rsi";
+	case LD_REG8:
+		return "r8";
+	case LD_REG9:
+		return "r9";
+	case LD_REG10:
+		return "r10";
+	case LD_REG11:
+		return "r11";
+	case LD_REG12:
+		return "r12";
+	case LD_REG13:
+		return "r13";
+	case LD_REG14:
+		return "r14";
+	case LD_REG15:
+		return "r15";
+	}
+	return "unknown reg!";
+}
+
+#define LD_MARK_PROLOG(label)						\
+	"1:"								\
+	".section .__logdev_strings,\"a\"\n"				\
+	"__logdev_str_" #label ": .string \"" #label "\"\n"		\
+	".previous\n"							\
+	".section .__logdev_markers,\"a\"\n"				\
+	".quad 1b, __logdev_caller__" #label "\n"			\
+	".quad __logdev_str_" #label "\n"
+
+#define LD_SPACER				\
+	     ".byte 0\n"			\
+	     ".long 0\n"
+
+#define LD_MARK(label)						\
+	{							\
+		extern void __logdev_caller__ ## label(void);	\
+		asm(						\
+		    LD_MARK_PROLOG(label)			\
+		    ".quad 0\n"					\
+		    ".previous"					\
+		    : :	);					\
+	}
+
+#define LD_MARK1(label, arg1)						\
+	{								\
+		extern void __logdev_caller__ ## label(typeof(arg1));	\
+		asm(							\
+		    LD_MARK_PROLOG(label)				\
+		    ".quad 1\n"						\
+		    "xor %0, %0\n"					\
+		    LD_SPACER						\
+		    ".previous"						\
+		    : :							\
+		    "r"((unsigned long)arg1));				\
+	}
+
+#define LD_MARK2(label, arg1, arg2)					\
+	{								\
+		extern void __logdev_caller__ ## label(typeof(arg1),	\
+						       typeof(arg2));	\
+		asm(							\
+		    LD_MARK_PROLOG(label)				\
+		    ".quad 2\n"						\
+		    "xor %0, %0\n"					\
+		    LD_SPACER						\
+		    "xor %1, %1\n"					\
+		    LD_SPACER						\
+		    ".previous"						\
+		    : :							\
+		    "r"((unsigned long)arg1),				\
+		    "r"((unsigned long)arg2));				\
+	}
+
+#define LD_MARK3(label, arg1, arg2, arg3)				\
+	{								\
+		extern void __logdev_caller__ ## label(typeof(arg1),	\
+						       typeof(arg2),	\
+						       typeof(arg3));	\
+		asm(							\
+		    LD_MARK_PROLOG(label)				\
+		    ".quad 3\n"						\
+		    "xor %0, %0\n"					\
+		    LD_SPACER						\
+		    "xor %1, %1\n"					\
+		    LD_SPACER						\
+		    "xor %2, %2\n"					\
+		    LD_SPACER						\
+		    ".previous"						\
+		    : :							\
+		    "r"((unsigned long)arg1),				\
+		    "r"((unsigned long)arg2),				\
+		    "r"((unsigned long)arg3));				\
+	}
+
+#define LD_MARK4(label, arg1, arg2, arg3, arg4)				\
+	{								\
+		extern void __logdev_caller__ ## label(typeof(arg1),	\
+						       typeof(arg2),	\
+						       typeof(arg3),	\
+						       typeof(arg4));	\
+		asm(							\
+		    LD_MARK_PROLOG(label)				\
+		    ".quad 4\n"						\
+		    "xor %0, %0\n"					\
+		    LD_SPACER						\
+		    "xor %1, %1\n"					\
+		    LD_SPACER						\
+		    "xor %2, %2\n"					\
+		    LD_SPACER						\
+		    "xor %3, %3\n"					\
+		    LD_SPACER						\
+		    ".previous"						\
+		    : :							\
+		    "r"((unsigned long)arg1),				\
+		    "r"((unsigned long)arg2),				\
+		    "r"((unsigned long)arg3),				\
+		    "r"((unsigned long)arg4));				\
+	}
+
+#endif /* _ASM_LOGDEV_MARKER_H */
Index: linux-2.6.21.5-rt20/arch/x86_64/Kconfig.debug
===================================================================
--- linux-2.6.21.5-rt20.orig/arch/x86_64/Kconfig.debug	2007-07-11 10:09:48.000000000 +0200
+++ linux-2.6.21.5-rt20/arch/x86_64/Kconfig.debug	2007-07-11 14:28:53.000000000 +0200
@@ -55,6 +55,11 @@ config DEBUG_STACK_USAGE
 
 	  This option will slow down process creation somewhat.
 
+config LOGDEV_MARKER
+	bool
+	depends on LOGDEV && KPROBES && !LOGDEV_MARKER_DIRECT
+	default y
+
 #config X86_REMOTE_DEBUG
 #       bool "kgdb debugging stub"
 
Index: linux-2.6.21.5-rt20/arch/x86_64/kernel/vmlinux.lds.S
===================================================================
--- linux-2.6.21.5-rt20.orig/arch/x86_64/kernel/vmlinux.lds.S	2007-07-11 10:09:48.000000000 +0200
+++ linux-2.6.21.5-rt20/arch/x86_64/kernel/vmlinux.lds.S	2007-07-11 14:28:53.000000000 +0200
@@ -52,6 +52,13 @@ SECTIONS
 
   RODATA
 
+#ifdef CONFIG_LOGDEV_MARKER
+  . = ALIGN(8);
+  .__logdev_strings : AT(ADDR(.__logdev_strings) - LOAD_OFFSET) {
+	*(.__logdev_strings)
+  }
+#endif
+
   BUG_TABLE
 
   . = ALIGN(PAGE_SIZE);        /* Align data segment to page size boundary */
@@ -182,6 +189,14 @@ SECTIONS
   .altinstr_replacement : AT(ADDR(.altinstr_replacement) - LOAD_OFFSET) {
 	*(.altinstr_replacement)
   }
+#ifdef CONFIG_LOGDEV_MARKER
+  . = ALIGN(8);
+  __logdev_marker_start = .;
+  .__logdev_markers : AT(ADDR(.__logdev_markers) - LOAD_OFFSET) {
+	*(.__logdev_markers)
+  }
+  __logdev_marker_end = .;
+#endif
   /* .exit.text is discard at runtime, not link time, to deal with references
      from .altinstructions and .eh_frame */
   .exit.text : AT(ADDR(.exit.text) - LOAD_OFFSET) { *(.exit.text) }
---
 include/linux/logdev_marker.h  |    2 +
 kernel/logdev/logdev_tracers.c |   76 +++++++++++++++++++++++++++++++++++++++++
 kernel/sched.c                 |    3 +
 3 files changed, 81 insertions(+)

Index: linux-2.6.21.5-rt20/include/linux/logdev_marker.h
===================================================================
--- linux-2.6.21.5-rt20.orig/include/linux/logdev_marker.h	2007-07-11 14:28:53.000000000 +0200
+++ linux-2.6.21.5-rt20/include/linux/logdev_marker.h	2007-07-11 14:28:56.000000000 +0200
@@ -53,6 +53,8 @@
 #ifdef CONFIG_LOGDEV_TRACING
 
 /* Place tracing prototypes here */
+void LDCALLER(context_switch) (struct task_struct *prev,
+			       struct task_struct *next);
 
 
 #endif /* CONFIG_LOGDEV_TRACING */
Index: linux-2.6.21.5-rt20/kernel/logdev/logdev_tracers.c
===================================================================
--- linux-2.6.21.5-rt20.orig/kernel/logdev/logdev_tracers.c	2007-07-11 14:28:53.000000000 +0200
+++ linux-2.6.21.5-rt20/kernel/logdev/logdev_tracers.c	2007-07-11 14:28:56.000000000 +0200
@@ -37,6 +37,28 @@ struct logdev_mark_hdr {
 
 /* ---------------- start here for user custom headers -------------------- */
 /* Add Tracing IDs and structures here */
+#define LOGMARK_EXAMPLE_ID 0xffff
+#define LOGMARK_E_ID(x) ((LOGMARK_EXAMPLE_ID << 16)|(x))
+
+#define LOGMARK_CNTXTSW		LOGMARK_E_ID(1)
+
+struct logdev_mark_switch {
+	struct logdev_mark_hdr hdr;
+	short pid_prev;
+	short pid_next;
+	int prev_prio;
+	int prev_static_prio;
+	int prev_normal_prio;
+	int prev_rt_prio;
+	int prev_state;
+	int next_prio;
+	int next_static_prio;
+	int next_normal_prio;
+	int next_rt_prio;
+	char prev_comm[TASK_COMM_LEN];
+	char next_comm[TASK_COMM_LEN];
+};
+
 
 /* ---------------- end of user space custom headers ---------------- */
 
@@ -65,6 +87,26 @@ static void logdev_print_hdr(int cpu,
 
 /* Place printing of traces here */
 
+static void logdev_mark_switch_print(int cpu, int size,
+				     struct logdev_mark_switch *lm)
+{
+	logdev_print_hdr(cpu, &lm->hdr);
+
+	printk("%s:%d(%d:%d:%d) -->> ",
+	       lm->prev_comm,
+	       lm->pid_prev,
+	       lm->prev_prio,
+	       lm->prev_static_prio,
+	       lm->prev_normal_prio);
+
+	printk("%s:%d(%d:%d:%d)\n",
+	       lm->next_comm,
+	       lm->pid_next,
+	       lm->next_prio,
+	       lm->next_static_prio,
+	       lm->next_normal_prio);
+}
+
 static void logdev_mark_callback(struct logdev_header *hdr,
 				 struct logdev_custom *custom,
 				 int cpu,
@@ -74,6 +116,9 @@ static void logdev_mark_callback(struct 
 
 	switch (lm->id) {
 	/* Add callbacks for traces here */
+	case LOGMARK_CNTXTSW:
+		logdev_mark_switch_print(cpu, hdr->size, rec);
+		break;
 	default:
 		printk("Unknown marker callback id %x\n",
 		       lm->id);
@@ -92,6 +137,37 @@ static void __kprobes logmark_hdr(struct
 
 /* Add tracing callback functions here */
 
+void __kprobes LDCALLER(context_switch) (struct task_struct *prev,
+					 struct task_struct *next)
+{
+	struct logdev_mark_switch lm;
+
+	/* Filter on test task */
+	/*
+	if ((strncmp(prev->comm, "sched_football", 14) != 0) &&
+	    (strncmp(next->comm, "sched_football", 14) != 0))
+		return;
+	*/
+
+	logmark_hdr(&lm.hdr, LOGMARK_CNTXTSW);
+
+	lm.pid_prev = prev->pid;
+	lm.prev_prio = prev->prio;
+	lm.prev_static_prio = prev->static_prio;
+	lm.prev_normal_prio = prev->normal_prio;
+	lm.prev_rt_prio = prev->rt_priority;
+	lm.prev_state = prev->state;
+	lm.pid_next = next->pid;
+	lm.next_prio = next->prio;
+	lm.next_static_prio = next->static_prio;
+	lm.next_normal_prio = next->normal_prio;
+	lm.next_rt_prio = next->rt_priority;
+	memcpy(lm.prev_comm, prev->comm, TASK_COMM_LEN);
+	memcpy(lm.next_comm, next->comm, TASK_COMM_LEN);
+
+	logdev_record(LOGMARK_ID_MARK, sizeof(lm),
+		      &lm, sizeof(lm), NULL);
+}
 
 static int __init logdev_tracer_init(void)
 {
Index: linux-2.6.21.5-rt20/kernel/sched.c
===================================================================
--- linux-2.6.21.5-rt20.orig/kernel/sched.c	2007-07-11 10:09:48.000000000 +0200
+++ linux-2.6.21.5-rt20/kernel/sched.c	2007-07-11 14:28:56.000000000 +0200
@@ -61,6 +61,7 @@
 #include <linux/kprobes.h>
 #include <linux/delayacct.h>
 #include <linux/reciprocal_div.h>
+#include <linux/logdev_marker.h>
 
 #include <asm/tlb.h>
 #include <asm/unistd.h>
@@ -2143,6 +2144,8 @@ context_switch(struct rq *rq, struct tas
 {
 	struct mm_struct *mm, *oldmm;
 
+	LD_MARK2(context_switch, prev, next);
+
 	prepare_task_switch(rq, next);
 	mm = next->mm;
 	oldmm = prev->active_mm;
---
 include/linux/logdev_marker.h  |    2 ++
 kernel/logdev/logdev_tracers.c |   27 +++++++++++++++++++++++++++
 kernel/posix-timers.c          |    3 +++
 3 files changed, 32 insertions(+)

Index: linux-2.6.21.5-rt20/include/linux/logdev_marker.h
===================================================================
--- linux-2.6.21.5-rt20.orig/include/linux/logdev_marker.h	2007-07-13 16:44:28.000000000 +0200
+++ linux-2.6.21.5-rt20/include/linux/logdev_marker.h	2007-07-13 16:47:35.000000000 +0200
@@ -56,6 +56,8 @@
 void LDCALLER(context_switch) (struct task_struct *prev,
 			       struct task_struct *next);
 
+void LDCALLER(sys_clock_getres) (const clockid_t which_clock);
+
 
 #endif /* CONFIG_LOGDEV_TRACING */
 
Index: linux-2.6.21.5-rt20/kernel/logdev/logdev_tracers.c
===================================================================
--- linux-2.6.21.5-rt20.orig/kernel/logdev/logdev_tracers.c	2007-07-13 16:44:40.000000000 +0200
+++ linux-2.6.21.5-rt20/kernel/logdev/logdev_tracers.c	2007-07-13 17:16:23.000000000 +0200
@@ -41,6 +41,7 @@ struct logdev_mark_hdr {
 #define LOGMARK_E_ID(x) ((LOGMARK_EXAMPLE_ID << 16)|(x))
 
 #define LOGMARK_CNTXTSW		LOGMARK_E_ID(1)
+#define LOGMARK_GETRES		LOGMARK_E_ID(3)
 
 struct logdev_mark_switch {
 	struct logdev_mark_hdr hdr;
@@ -59,6 +60,12 @@ struct logdev_mark_switch {
 	char next_comm[TASK_COMM_LEN];
 };
 
+struct logdev_mark_getres {
+	struct logdev_mark_hdr hdr;
+	short pid;
+	clockid_t clock;
+	char comm[TASK_COMM_LEN];
+};
 
 /* ---------------- end of user space custom headers ---------------- */
 
@@ -169,6 +176,26 @@ void __kprobes LDCALLER(context_switch) 
 		      &lm, sizeof(lm), NULL);
 }
 
+void LDCALLER(sys_clock_getres) (const clockid_t which_clock)
+{
+	struct logdev_mark_getres lm;
+
+	/* Filter on task */
+	if (strncmp(current->comm, "sched_football", 14) != 0)
+		return;
+
+
+	logmark_hdr(&lm.hdr, LOGMARK_GETRES);
+	lm.pid = current->pid;
+	lm.clock = which_clock;
+	memcpy(lm.comm, current->comm, TASK_COMM_LEN);
+
+	logdev_record(LOGMARK_ID_MARK, sizeof(lm),
+		      &lm, sizeof(lm), NULL);
+}
+
+
+
 static int __init logdev_tracer_init(void)
 {
 	logdev_register_callback(LOGMARK_ID_MARK, logdev_mark_callback);
Index: linux-2.6.21.5-rt20/kernel/posix-timers.c
===================================================================
--- linux-2.6.21.5-rt20.orig/kernel/posix-timers.c	2007-07-13 16:44:12.000000000 +0200
+++ linux-2.6.21.5-rt20/kernel/posix-timers.c	2007-07-13 16:46:33.000000000 +0200
@@ -48,6 +48,7 @@
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 #include <linux/module.h>
+#include <linux/logdev_marker.h>
 
 /*
  * Management arrays for POSIX timers.	 Timers are kept in slab memory
@@ -964,6 +965,8 @@ sys_clock_getres(const clockid_t which_c
 	struct timespec rtn_tp;
 	int error;
 
+	LD_MARK1(sys_clock_getres, which_clock);
+
 	if (invalid_clockid(which_clock))
 		return -EINVAL;
 
#ifndef __DUMP_LOG_H
#define __DUMP_LOG_H

#include <sys/ioctl.h>

#define TASK_COMM_LEN 16

/* Random numbers out of my head used for MAGIC */
#define LOGDEV_CUSTOM 0x1afb
#define LOGDEV_SWITCH_ID 0x2afc
#define LOGDEV_PRINT 0x4adb
#define LOGDEV_PRINT_TIME 0x4adc
#define LOGDEV_PRINT_TIME_FUNC 0x4add
#define LOGDEV_HDR_DONE 0xbbbb  /* internal use only */

#define LOGDEV_VALID_ID(id) (((id) == LOGDEV_SWITCH_ID) || \
			     ((id) == LOGDEV_PRINT) || \
			     ((id) == LOGDEV_PRINT_TIME) || \
			     ((id) == LOGDEV_PRINT_TIME_FUNC) || \
			     ((id) == LOGDEV_CUSTOM))

struct logdev_switch_struct {
	unsigned long long t;
	short pid_prev;
	short pid_next;
	int prev_prio;
	int prev_static_prio;
	int prev_normal_prio;
	int prev_state;
	int next_prio;
	int next_static_prio;
	int next_normal_prio;
	char prev_comm[TASK_COMM_LEN];
	char next_comm[TASK_COMM_LEN];
};

struct logdev_print {
	char str[0];
};

struct logdev_print_time {
	unsigned long long t;
	char str[0];
};

struct logdev_print_time_func {
	const char *file;
	int line;
	/* need to be after line, since we use this with print_time. */
	unsigned long long t;
	char str[0];
};

struct logdev_custom {
	int id;
	char data[0];
};

struct logdev_header {
	long counter;
	int id;
	int size;
};

struct logdev_item {
	struct logdev_header hdr;
	union {
		struct logdev_switch_struct sw;
		struct logdev_print print;
		struct logdev_print_time print_time;
		struct logdev_print_time_func print_time_func;
		struct logdev_custom custom;
		char data[0];
	} u;
};

typedef void (*logdev_callback_func)(struct logdev_header *hdr,
				     struct logdev_custom *custom,
				     int cpu,
				     void *rec);
int logdev_register_callback(int custom_id, logdev_callback_func func);
int logdev_unregister_callback(int custom_id);

char *dump_log_version(void);

void process_switch(int cpu, struct logdev_item *buf);
void process_print(int cpu, struct logdev_item *buf);

char *find_sys(unsigned long key);
void load_sys(FILE *fp);
struct logdev_item *read_dump(int fd, struct logdev_header *hdr);
int open_logdev_cpu(char *uname, mode_t mode, int cpu);
int close_logdev(int fd);
int logdev_files(int **_fds, int **_cpus);

int process_buf (struct logdev_item *buf, int id, int cpu);
int process_dump(int *fd, int *cpus, int nr_cpus);
int logdev_print(int fd,const char *fmt, ...);
int logdev_switch_set(int on);
int logdev_switch_read(void);
int logdev_print_set(int on);
int logdev_print_read(void);
int logdev_probe_del(int id);
int logdev_probe_watch(const char *func, const char *var,
		       unsigned long addr, unsigned long watch);
int logdev_probe_func(const char *func, const char *fmt);
int logdev_probe_curr(const char *func, unsigned long addr,
		      int index, const char *fmt);
int logdev_probe_var(const char *func, unsigned long addr,
		     const char *type);
int logdev_open_probe(void);
int logdev_open_marker(void);
int logdev_close_probe(int fd);
int logdev_close_marker(int fd);

struct logdev_item *read_record(int fd);
void free_record(struct logdev_item *rec);

/* ------------------- probe cut place here ---------------------- */

#define LOGPROBE_IOCTL_BASE 'l'

#define PROBE_IOW(nr, type) _IOW(LOGPROBE_IOCTL_BASE, nr, type)

#define LOGPROBE_DELPOINT  PROBE_IOW(0, unsigned long)
#define LOGPROBE_ADDWATCH  PROBE_IOW(1, struct logdev_probe_watch)
#define LOGPROBE_ADDFUNC   PROBE_IOW(2, struct logdev_probe_func)
#define LOGPROBE_ADDCURR   PROBE_IOW(3, struct logdev_probe_curr)
#define LOGPROBE_ADDVAR    PROBE_IOW(4, struct logdev_probe_var)

#define LOGPROBE_ID_WATCH	0x56570001
#define LOGPROBE_ID_FUNC	0x56570002
#define LOGPROBE_ID_CURR	0x56570003
#define LOGPROBE_ID_VAR		0x56570004

#define LOGPROBE_TYPE_PREEMPT 0x1111

struct logdev_probe_hdr_log {
	unsigned long long t;
	char comm[TASK_COMM_LEN];
	int pid;
	unsigned long addr;
	unsigned long offset;
	int func_symbol_size;
};

struct logdev_probe_watch_log {
	struct logdev_probe_hdr_log hdr;
	unsigned long var;
	unsigned long value;
	int var_symbol_size;
	char symbols[];
};

struct logdev_probe_func_log {
	struct logdev_probe_hdr_log hdr;
	int str_size;
	char symbols[];
};

struct logdev_probe_curr_log {
	struct logdev_probe_hdr_log hdr;
	int index;
	unsigned long value;
	int prefix_size;
	char symbols[];
};

struct logdev_probe_var_log {
	struct logdev_probe_hdr_log hdr;
	int type;
	unsigned long value;
	int prefix_size;
	char symbols[];
};

struct logdev_probe_watch {
	unsigned long addr;
	unsigned long watch;
	const char *func;
	const char *var;
	int func_size;
	int var_size;
};

struct logdev_probe_func {
	const char *func;
	const char *fmt1;
	const char *fmt2;
	int func_size;
	int fmt1_size;
	int fmt2_size;
};

struct logdev_probe_curr {
	unsigned long addr;
	const char *func;
	const char *prefix;
	int offset;
	int func_size;
	int prefix_size;
};

struct logdev_probe_var {
	unsigned long addr;
	const char *func;
	const char *prefix;
	int type;
	int func_size;
	int prefix_size;
};

/* ---------------------- end probe cut ------------------------ */

/* -------------- start marker cut ------------------------- */


#define LOGMARK_IOCTL_BASE 'm'

#define MARK_IO(nr) _IO(LOGMARK_IOCTL_BASE, nr)
#define MARK_IOW(nr, type) _IOW(LOGMARK_IOCTL_BASE, nr, type)

#define LOGMARK_START  MARK_IO(0)
#define LOGMARK_STOP   MARK_IO(1)

#define LOGMARK_ID_MARK		0x56580000

#define LOGMARK_CNTXTSW		0xffff0001
#define LOGMARK_MM_FAULT	0xffff0002
#define LOGMARK_GETRES		0xffff0003

struct logdev_mark_hdr {
	unsigned long long t;
	int id;
};

struct logdev_mark_switch {
	struct logdev_mark_hdr hdr;
	short pid_prev;
	short pid_next;
	int prev_prio;
	int prev_static_prio;
	int prev_normal_prio;
	int prev_rt_prio;
	int prev_state;
	int next_prio;
	int next_static_prio;
	int next_normal_prio;
	int next_rt_prio;
	char prev_comm[TASK_COMM_LEN];
	char next_comm[TASK_COMM_LEN];
};

struct logdev_mark_mm_fault {
	struct logdev_mark_hdr hdr;
	short pid;
	char comm[TASK_COMM_LEN];
	struct mm_struct *mm;
	struct vm_area_struct *vma;
	unsigned long address;
	int write_access;
};

struct logdev_mark_getres {
	struct logdev_mark_hdr hdr;
	short pid;
	clockid_t clock;
	char comm[TASK_COMM_LEN];
};

/* ---------------- end marker cut ------------------------- */

#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#include "dump_log.h"

#define NSECS_PER_SEC 1000000000ULL

static void convert_to_ns(unsigned long long t,
			  unsigned long *secs, unsigned long *nsecs)
{
	*secs = (unsigned long)(t / 1000000000ULL);
	*nsecs = (unsigned long)(t % 1000000000ULL);
}

static void logdev_print_time_cpu(unsigned long long t, int cpu)
{
	unsigned long nsecs;
	unsigned long secs;

	convert_to_ns(t, &secs, &nsecs);

	printf("[%5lu.%06lu] cpu:%d ",
	       secs, nsecs/1000, cpu);
}

/* ------------------ place kernel printing here ----------------- */
static void logdev_print_hdr(int cpu,
			     struct logdev_mark_hdr *hdr)
{
	logdev_print_time_cpu(hdr->t, cpu);
}

static void logdev_mark_switch_print(int cpu, int size,
				     struct logdev_mark_switch *lm)
{
	logdev_print_hdr(cpu, &lm->hdr);

	printf("%s:%d(%d:%d:%d:%d) -->> ",
	       lm->prev_comm,
	       lm->pid_prev,
	       lm->prev_prio,
	       lm->prev_static_prio,
	       lm->prev_normal_prio,
	       lm->prev_rt_prio);

	printf("%s:%d(%d:%d:%d:%d)\n",
	       lm->next_comm,
	       lm->pid_next,
	       lm->next_prio,
	       lm->next_static_prio,
	       lm->next_normal_prio,
	       lm->next_rt_prio);
}

static void logdev_mark_mm_fault_print(int cpu, int size,
				       struct logdev_mark_mm_fault *lm)
{
	logdev_print_hdr(cpu, &lm->hdr);

	printf("%s:%d  mm=%p vma=%p  address=%lx  write_access=%d\n",
	       lm->comm,
	       lm->pid,
	       lm->mm,
	       lm->vma,
	       lm->address,
	       lm->write_access);
}

static void logdev_mark_getres_print(int cpu, int size,
				     struct logdev_mark_getres *lm)
{
	logdev_print_hdr(cpu, &lm->hdr);

	printf("%s:%d getres clock:%d\n", lm->comm, lm->pid, lm->clock);
}

static void logdev_mark_callback(struct logdev_header *hdr,
				 struct logdev_custom *custom,
				 int cpu,
				 void *rec)
{
	struct logdev_mark_hdr *lm = rec;

	switch (lm->id) {
	case LOGMARK_CNTXTSW:
		logdev_mark_switch_print(cpu, hdr->size, rec);
		break;
	case LOGMARK_MM_FAULT:
		logdev_mark_mm_fault_print(cpu, hdr->size, rec);
		break;
	case LOGMARK_GETRES:
		logdev_mark_getres_print(cpu, hdr->size, rec);
		break;
	default:
		printf("Unknown marker callback id %x\n",
		       custom->id);
		break;
	}
}

/* ---------------- end placement for user printing ------------ */

void logdev_setup_markers(void)
{
	logdev_register_callback(LOGMARK_ID_MARK, logdev_mark_callback);
}

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux