| /* |
| * 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); |
| } |
| } |
| |
| |