diff options
Diffstat (limited to 'arch/avr32/oprofile')
| -rw-r--r-- | arch/avr32/oprofile/Makefile | 2 | ||||
| -rw-r--r-- | arch/avr32/oprofile/backtrace.c | 81 | ||||
| -rw-r--r-- | arch/avr32/oprofile/op_model_avr32.c | 22 |
3 files changed, 94 insertions, 11 deletions
diff --git a/arch/avr32/oprofile/Makefile b/arch/avr32/oprofile/Makefile index 1fe81c3c1e8..e0eb520e028 100644 --- a/arch/avr32/oprofile/Makefile +++ b/arch/avr32/oprofile/Makefile @@ -5,4 +5,4 @@ oprofile-y := $(addprefix ../../../drivers/oprofile/, \ event_buffer.o oprofile_files.o \ oprofilefs.o oprofile_stats.o \ timer_int.o) -oprofile-y += op_model_avr32.o +oprofile-y += op_model_avr32.o backtrace.o diff --git a/arch/avr32/oprofile/backtrace.c b/arch/avr32/oprofile/backtrace.c new file mode 100644 index 00000000000..75d9ad6f99c --- /dev/null +++ b/arch/avr32/oprofile/backtrace.c @@ -0,0 +1,81 @@ +/* + * AVR32 specific backtracing code for oprofile + * + * Copyright 2008 Weinmann GmbH + * + * Author: Nikolaus Voss <n.voss@weinmann.de> + * + * Based on i386 oprofile backtrace code by John Levon and David Smith + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/oprofile.h> +#include <linux/sched.h> +#include <linux/uaccess.h> + +/* The first two words of each frame on the stack look like this if we have + * frame pointers */ +struct frame_head { + unsigned long lr; + struct frame_head *fp; +}; + +/* copied from arch/avr32/kernel/process.c */ +static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p) +{ + return (p > (unsigned long)tinfo) + && (p < (unsigned long)tinfo + THREAD_SIZE - 3); +} + +/* copied from arch/x86/oprofile/backtrace.c */ +static struct frame_head *dump_user_backtrace(struct frame_head *head) +{ + struct frame_head bufhead[2]; + + /* Also check accessibility of one struct frame_head beyond */ + if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) + return NULL; + if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) + return NULL; + + oprofile_add_trace(bufhead[0].lr); + + /* frame pointers should strictly progress back up the stack + * (towards higher addresses) */ + if (bufhead[0].fp <= head) + return NULL; + + return bufhead[0].fp; +} + +void avr32_backtrace(struct pt_regs * const regs, unsigned int depth) +{ + /* Get first frame pointer */ + struct frame_head *head = (struct frame_head *)(regs->r7); + + if (!user_mode(regs)) { +#ifdef CONFIG_FRAME_POINTER + /* + * Traverse the kernel stack from frame to frame up to + * "depth" steps. + */ + while (depth-- && valid_stack_ptr(task_thread_info(current), + (unsigned long)head)) { + oprofile_add_trace(head->lr); + if (head->fp <= head) + break; + head = head->fp; + } +#endif + } else { + /* Assume we have frame pointers in user mode process */ + while (depth-- && head) + head = dump_user_backtrace(head); + } +} + + diff --git a/arch/avr32/oprofile/op_model_avr32.c b/arch/avr32/oprofile/op_model_avr32.c index df42325c7f8..08308be2c02 100644 --- a/arch/avr32/oprofile/op_model_avr32.c +++ b/arch/avr32/oprofile/op_model_avr32.c @@ -17,11 +17,12 @@ #include <linux/types.h> #include <asm/sysreg.h> -#include <asm/system.h> #define AVR32_PERFCTR_IRQ_GROUP 0 #define AVR32_PERFCTR_IRQ_LINE 1 +void avr32_backtrace(struct pt_regs * const regs, unsigned int depth); + enum { PCCNT, PCNT0, PCNT1, NR_counter }; struct avr32_perf_counter { @@ -96,8 +97,7 @@ static irqreturn_t avr32_perf_counter_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int avr32_perf_counter_create_files(struct super_block *sb, - struct dentry *root) +static int avr32_perf_counter_create_files(struct dentry *root) { struct dentry *dir; unsigned int i; @@ -105,21 +105,21 @@ static int avr32_perf_counter_create_files(struct super_block *sb, for (i = 0; i < NR_counter; i++) { snprintf(filename, sizeof(filename), "%u", i); - dir = oprofilefs_mkdir(sb, root, filename); + dir = oprofilefs_mkdir(root, filename); - oprofilefs_create_ulong(sb, dir, "enabled", + oprofilefs_create_ulong(dir, "enabled", &counter[i].enabled); - oprofilefs_create_ulong(sb, dir, "event", + oprofilefs_create_ulong(dir, "event", &counter[i].event); - oprofilefs_create_ulong(sb, dir, "count", + oprofilefs_create_ulong(dir, "count", &counter[i].count); /* Dummy entries */ - oprofilefs_create_ulong(sb, dir, "kernel", + oprofilefs_create_ulong(dir, "kernel", &counter[i].kernel); - oprofilefs_create_ulong(sb, dir, "user", + oprofilefs_create_ulong(dir, "user", &counter[i].user); - oprofilefs_create_ulong(sb, dir, "unit_mask", + oprofilefs_create_ulong(dir, "unit_mask", &counter[i].unit_mask); } @@ -223,6 +223,8 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) memcpy(ops, &avr32_perf_counter_ops, sizeof(struct oprofile_operations)); + ops->backtrace = avr32_backtrace; + printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n"); return 0; |
