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 <asm/ptrace.h> 20#include <asm/uaccess.h> 21 22#include "../kernel/stacktrace.h" 23 24static int report_trace(struct stackframe *frame, void *d) 25{ 26 unsigned int *depth = d; 27 28 if (*depth) { 29 oprofile_add_trace(frame->lr); 30 (*depth)--; 31 } 32 33 return *depth == 0; 34} 35 36struct frame_tail { 37 struct frame_tail *fp; 38 unsigned long sp; 39 unsigned long lr; 40} __attribute__((packed)); 41 42static struct frame_tail* user_backtrace(struct frame_tail *tail) 43{ 44 struct frame_tail buftail[2]; 45 46 /* Also check accessibility of one struct frame_tail beyond */ 47 if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) 48 return NULL; 49 if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) 50 return NULL; 51 52 oprofile_add_trace(buftail[0].lr); 53 54 /* frame pointers should strictly progress back up the stack 55 * (towards higher addresses) */ 56 if (tail >= buftail[0].fp) 57 return NULL; 58 59 return buftail[0].fp-1; 60} 61 62void arm_backtrace(struct pt_regs * const regs, unsigned int depth) 63{ 64 struct frame_tail *tail = ((struct frame_tail *) regs->ARM_fp) - 1; 65 66 if (!user_mode(regs)) { 67 unsigned long base = ((unsigned long)regs) & ~(THREAD_SIZE - 1); 68 walk_stackframe(regs->ARM_fp, base, base + THREAD_SIZE, 69 report_trace, &depth); 70 return; 71 } 72 73 while (depth-- && tail && !((unsigned long) tail & 3)) 74 tail = user_backtrace(tail); 75} 76