1/* 2 * Arm specific backtracing code for oprofile 3 * 4 * Copyright 2005 Openedhand Ltd. 5 * 6 * Author: Richard Purdie <rpurdie@openedhand.com> 7 * 8 * Based on i386 oprofile backtrace code by John Levon, David Smith 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 * 14 */ 15 16#include <linux/oprofile.h> 17#include <linux/sched.h> 18#include <linux/mm.h> 19#include <linux/uaccess.h> 20#include <asm/ptrace.h> 21#include <asm/stacktrace.h> 22 23static int report_trace(struct stackframe *frame, void *d) 24{ 25 unsigned int *depth = d; 26 27 if (*depth) { 28 oprofile_add_trace(frame->pc); 29 (*depth)--; 30 } 31 32 return *depth == 0; 33} 34 35/* 36 * The registers we're interested in are at the end of the variable 37 * length saved register structure. The fp points at the end of this 38 * structure so the address of this struct is: 39 * (struct frame_tail *)(xxx->fp)-1 40 */ 41struct frame_tail { 42 struct frame_tail *fp; 43 unsigned long sp; 44 unsigned long lr; 45} __attribute__((packed)); 46 47static struct frame_tail* user_backtrace(struct frame_tail *tail) 48{ 49 struct frame_tail buftail[2]; 50 51 /* Also check accessibility of one struct frame_tail beyond */ 52 if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) 53 return NULL; 54 if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) 55 return NULL; 56 57 oprofile_add_trace(buftail[0].lr); 58 59 /* frame pointers should strictly progress back up the stack 60 * (towards higher addresses) */ 61 if (tail >= buftail[0].fp) 62 return NULL; 63 64 return buftail[0].fp-1; 65} 66 67void arm_backtrace(struct pt_regs * const regs, unsigned int depth) 68{ 69 struct frame_tail *tail = ((struct frame_tail *) regs->ARM_fp) - 1; 70 71 if (!user_mode(regs)) { 72 struct stackframe frame; 73 frame.fp = regs->ARM_fp; 74 frame.sp = regs->ARM_sp; 75 frame.lr = regs->ARM_lr; 76 frame.pc = regs->ARM_pc; 77 walk_stackframe(&frame, report_trace, &depth); 78 return; 79 } 80 81 while (depth-- && tail && !((unsigned long) tail & 3)) 82 tail = user_backtrace(tail); 83} 84