Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | /** * @file common.c * * @remark Copyright 2004 Oprofile Authors * @remark Copyright 2010 ARM Ltd. * @remark Read the file COPYING * * @author Zwane Mwaikambo * @author Will Deacon [move to perf] */ #include <linux/cpumask.h> #include <linux/init.h> #include <linux/mutex.h> #include <linux/oprofile.h> #include <linux/perf_event.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <asm/stacktrace.h> #include <linux/uaccess.h> #include <asm/perf_event.h> #include <asm/ptrace.h> #ifdef CONFIG_HW_PERF_EVENTS /* * OProfile has a curious naming scheme for the ARM PMUs, but they are * part of the user ABI so we need to map from the perf PMU name for * supported PMUs. */ static struct op_perf_name { char *perf_name; char *op_name; } op_perf_name_map[] = { { "armv5_xscale1", "arm/xscale1" }, { "armv5_xscale2", "arm/xscale2" }, { "armv6_1136", "arm/armv6" }, { "armv6_1156", "arm/armv6" }, { "armv6_1176", "arm/armv6" }, { "armv6_11mpcore", "arm/mpcore" }, { "armv7_cortex_a8", "arm/armv7" }, { "armv7_cortex_a9", "arm/armv7-ca9" }, }; char *op_name_from_perf_id(void) { int i; struct op_perf_name names; const char *perf_name = perf_pmu_name(); for (i = 0; i < ARRAY_SIZE(op_perf_name_map); ++i) { names = op_perf_name_map[i]; if (!strcmp(names.perf_name, perf_name)) return names.op_name; } return NULL; } #endif static int report_trace(struct stackframe *frame, void *d) { unsigned int *depth = d; if (*depth) { oprofile_add_trace(frame->pc); (*depth)--; } return *depth == 0; } /* * The registers we're interested in are at the end of the variable * length saved register structure. The fp points at the end of this * structure so the address of this struct is: * (struct frame_tail *)(xxx->fp)-1 */ struct frame_tail { struct frame_tail *fp; unsigned long sp; unsigned long lr; } __attribute__((packed)); static struct frame_tail* user_backtrace(struct frame_tail *tail) { struct frame_tail buftail[2]; /* Also check accessibility of one struct frame_tail beyond */ if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) return NULL; if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) return NULL; oprofile_add_trace(buftail[0].lr); /* frame pointers should strictly progress back up the stack * (towards higher addresses) */ if (tail + 1 >= buftail[0].fp) return NULL; return buftail[0].fp-1; } static void arm_backtrace(struct pt_regs * const regs, unsigned int depth) { struct frame_tail *tail = ((struct frame_tail *) regs->ARM_fp) - 1; if (!user_mode(regs)) { struct stackframe frame; arm_get_current_stackframe(regs, &frame); walk_stackframe(&frame, report_trace, &depth); return; } while (depth-- && tail && !((unsigned long) tail & 3)) tail = user_backtrace(tail); } int __init oprofile_arch_init(struct oprofile_operations *ops) { /* provide backtrace support also in timer mode: */ ops->backtrace = arm_backtrace; return oprofile_perf_init(ops); } void oprofile_arch_exit(void) { oprofile_perf_exit(); } |