forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
3.0 KiB
132 lines
3.0 KiB
/** |
|
* @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(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(); |
|
}
|
|
|