| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * @file common.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @remark Copyright 2004 Oprofile Authors | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  |  * @remark Copyright 2010 ARM Ltd. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  * @remark Read the file COPYING | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @author Zwane Mwaikambo | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  |  * @author Will Deacon [move to perf] | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | #include <linux/cpumask.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <linux/init.h>
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | #include <linux/mutex.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <linux/oprofile.h>
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | #include <linux/perf_event.h>
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:38:39 +01:00
										 |  |  | #include <linux/platform_device.h>
 | 
					
						
							| 
									
										
										
										
											2006-03-16 11:32:51 +00:00
										 |  |  | #include <linux/slab.h>
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | #include <asm/stacktrace.h>
 | 
					
						
							|  |  |  | #include <linux/uaccess.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/perf_event.h>
 | 
					
						
							|  |  |  | #include <asm/ptrace.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | #ifdef CONFIG_HW_PERF_EVENTS
 | 
					
						
							| 
									
										
										
										
											2010-10-08 21:42:17 +01:00
										 |  |  | char *op_name_from_perf_id(void) | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2010-10-08 21:42:17 +01:00
										 |  |  | 	enum arm_perf_pmu_ids id = armpmu_get_pmu_id(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | 	switch (id) { | 
					
						
							|  |  |  | 	case ARM_PERF_PMU_ID_XSCALE1: | 
					
						
							|  |  |  | 		return "arm/xscale1"; | 
					
						
							|  |  |  | 	case ARM_PERF_PMU_ID_XSCALE2: | 
					
						
							|  |  |  | 		return "arm/xscale2"; | 
					
						
							|  |  |  | 	case ARM_PERF_PMU_ID_V6: | 
					
						
							|  |  |  | 		return "arm/armv6"; | 
					
						
							|  |  |  | 	case ARM_PERF_PMU_ID_V6MP: | 
					
						
							|  |  |  | 		return "arm/mpcore"; | 
					
						
							|  |  |  | 	case ARM_PERF_PMU_ID_CA8: | 
					
						
							|  |  |  | 		return "arm/armv7"; | 
					
						
							|  |  |  | 	case ARM_PERF_PMU_ID_CA9: | 
					
						
							|  |  |  | 		return "arm/armv7-ca9"; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2011-01-20 13:57:19 -05:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | static int report_trace(struct stackframe *frame, void *d) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | 	unsigned int *depth = d; | 
					
						
							| 
									
										
										
										
											2005-10-28 14:56:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | 	if (*depth) { | 
					
						
							|  |  |  | 		oprofile_add_trace(frame->pc); | 
					
						
							|  |  |  | 		(*depth)--; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2007-02-27 12:09:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | 	return *depth == 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2005-10-28 14:56:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * 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)); | 
					
						
							| 
									
										
										
										
											2006-12-19 12:41:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | static struct frame_tail* user_backtrace(struct frame_tail *tail) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct frame_tail buftail[2]; | 
					
						
							| 
									
										
										
										
											2006-12-19 14:17:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | 	/* 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; | 
					
						
							| 
									
										
										
										
											2008-08-12 19:07:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | 	oprofile_add_trace(buftail[0].lr); | 
					
						
							| 
									
										
										
										
											2005-10-28 14:56:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | 	/* frame pointers should strictly progress back up the stack
 | 
					
						
							|  |  |  | 	 * (towards higher addresses) */ | 
					
						
							| 
									
										
										
										
											2011-02-09 11:35:12 +01:00
										 |  |  | 	if (tail + 1 >= buftail[0].fp) | 
					
						
							| 
									
										
										
										
											2010-04-30 11:36:54 +01:00
										 |  |  | 		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; | 
					
						
							|  |  |  | 		frame.fp = regs->ARM_fp; | 
					
						
							|  |  |  | 		frame.sp = regs->ARM_sp; | 
					
						
							|  |  |  | 		frame.lr = regs->ARM_lr; | 
					
						
							|  |  |  | 		frame.pc = regs->ARM_pc; | 
					
						
							|  |  |  | 		walk_stackframe(&frame, report_trace, &depth); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (depth-- && tail && !((unsigned long) tail & 3)) | 
					
						
							|  |  |  | 		tail = user_backtrace(tail); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-27 20:35:29 +01:00
										 |  |  | int __init oprofile_arch_init(struct oprofile_operations *ops) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-01-20 13:57:19 -05:00
										 |  |  | 	/* provide backtrace support also in timer mode: */ | 
					
						
							| 
									
										
										
										
											2010-09-27 20:35:29 +01:00
										 |  |  | 	ops->backtrace		= arm_backtrace; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return oprofile_perf_init(ops); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-22 16:15:40 +01:00
										 |  |  | void oprofile_arch_exit(void) | 
					
						
							| 
									
										
										
										
											2010-09-27 20:35:29 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	oprofile_perf_exit(); | 
					
						
							|  |  |  | } |