[PATCH] ARM: 2761/1: OProfile: Add call graphing support for arm

Patch from Richard Purdie

Add functions to generate backtraces of both kernel and user processes
which allows oprofile's call graphing functionality to be used on arm.
This requires unstripped binaries/libs which use a frame pointer.

Signed-off-by: Richard Purdie
Signed-off-by: Zwane Mwaikambo <zwane@arm.linux.org.uk>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by Richard Purdie and committed by Russell King fa0ebff6 99f95e52

+149 -1
+1 -1
arch/arm/oprofile/Makefile
··· 6 6 oprofilefs.o oprofile_stats.o \ 7 7 timer_int.o ) 8 8 9 - oprofile-y := $(DRIVER_OBJS) init.o 9 + oprofile-y := $(DRIVER_OBJS) init.o backtrace.o 10 10 oprofile-$(CONFIG_CPU_XSCALE) += common.o op_model_xscale.o 11 11
+144
arch/arm/oprofile/backtrace.c
··· 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 + 23 + /* 24 + * The registers we're interested in are at the end of the variable 25 + * length saved register structure. The fp points at the end of this 26 + * structure so the address of this struct is: 27 + * (struct frame_tail *)(xxx->fp)-1 28 + */ 29 + struct frame_tail { 30 + struct frame_tail *fp; 31 + unsigned long sp; 32 + unsigned long lr; 33 + } __attribute__((packed)); 34 + 35 + 36 + #ifdef CONFIG_FRAME_POINTER 37 + static struct frame_tail* kernel_backtrace(struct frame_tail *tail) 38 + { 39 + oprofile_add_trace(tail->lr); 40 + 41 + /* frame pointers should strictly progress back up the stack 42 + * (towards higher addresses) */ 43 + if (tail >= tail->fp) 44 + return NULL; 45 + 46 + return tail->fp-1; 47 + } 48 + #endif 49 + 50 + static struct frame_tail* user_backtrace(struct frame_tail *tail) 51 + { 52 + struct frame_tail buftail; 53 + 54 + /* hardware pte might not be valid due to dirty/accessed bit emulation 55 + * so we use copy_from_user and benefit from exception fixups */ 56 + if (copy_from_user(&buftail, tail, sizeof(struct frame_tail))) 57 + return NULL; 58 + 59 + oprofile_add_trace(buftail.lr); 60 + 61 + /* frame pointers should strictly progress back up the stack 62 + * (towards higher addresses) */ 63 + if (tail >= buftail.fp) 64 + return NULL; 65 + 66 + return buftail.fp-1; 67 + } 68 + 69 + /* Compare two addresses and see if they're on the same page */ 70 + #define CMP_ADDR_EQUAL(x,y,offset) ((((unsigned long) x) >> PAGE_SHIFT) \ 71 + == ((((unsigned long) y) + offset) >> PAGE_SHIFT)) 72 + 73 + /* check that the page(s) containing the frame tail are present */ 74 + static int pages_present(struct frame_tail *tail) 75 + { 76 + struct mm_struct * mm = current->mm; 77 + 78 + if (!check_user_page_readable(mm, (unsigned long)tail)) 79 + return 0; 80 + 81 + if (CMP_ADDR_EQUAL(tail, tail, 8)) 82 + return 1; 83 + 84 + if (!check_user_page_readable(mm, ((unsigned long)tail) + 8)) 85 + return 0; 86 + 87 + return 1; 88 + } 89 + 90 + /* 91 + * | | /\ Higher addresses 92 + * | | 93 + * --------------- stack base (address of current_thread_info) 94 + * | thread info | 95 + * . . 96 + * | stack | 97 + * --------------- saved regs->ARM_fp value if valid (frame_tail address) 98 + * . . 99 + * --------------- struct pt_regs stored on stack (struct pt_regs *) 100 + * | | 101 + * . . 102 + * | | 103 + * --------------- %esp 104 + * | | 105 + * | | \/ Lower addresses 106 + * 107 + * Thus, &pt_regs <-> stack base restricts the valid(ish) fp values 108 + */ 109 + static int valid_kernel_stack(struct frame_tail *tail, struct pt_regs *regs) 110 + { 111 + unsigned long tailaddr = (unsigned long)tail; 112 + unsigned long stack = (unsigned long)regs; 113 + unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE; 114 + 115 + return (tailaddr > stack) && (tailaddr < stack_base); 116 + } 117 + 118 + void arm_backtrace(struct pt_regs const *regs, unsigned int depth) 119 + { 120 + struct frame_tail *tail; 121 + unsigned long last_address = 0; 122 + 123 + tail = ((struct frame_tail *) regs->ARM_fp) - 1; 124 + 125 + if (!user_mode(regs)) { 126 + 127 + #ifdef CONFIG_FRAME_POINTER 128 + while (depth-- && tail && valid_kernel_stack(tail, regs)) { 129 + tail = kernel_backtrace(tail); 130 + } 131 + #endif 132 + return; 133 + } 134 + 135 + while (depth-- && tail && !((unsigned long) tail & 3)) { 136 + if ((!CMP_ADDR_EQUAL(last_address, tail, 0) 137 + || !CMP_ADDR_EQUAL(last_address, tail, 8)) 138 + && !pages_present(tail)) 139 + return; 140 + last_address = (unsigned long) tail; 141 + tail = user_backtrace(tail); 142 + } 143 + } 144 +
+2
arch/arm/oprofile/init.c
··· 20 20 ret = pmu_init(ops, &op_xscale_spec); 21 21 #endif 22 22 23 + ops->backtrace = arm_backtrace; 24 + 23 25 return ret; 24 26 } 25 27
+2
arch/arm/oprofile/op_arm_model.h
··· 24 24 extern struct op_arm_model_spec op_xscale_spec; 25 25 #endif 26 26 27 + extern void arm_backtrace(struct pt_regs * const regs, unsigned int depth); 28 + 27 29 extern int __init pmu_init(struct oprofile_operations *ops, struct op_arm_model_spec *spec); 28 30 extern void pmu_exit(void); 29 31 #endif /* OP_ARM_MODEL_H */