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 +#include + +#define LD_CALLER_NAME __logdev_caller__ +#define LDCALLER(caller) __logdev_caller__ ## caller + +#elif CONFIG_LOGDEV_MARKER_DIRECT + +#include + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 +#include +#include +#include + +/* ---------------- 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) }