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
2012-07-06 15:45:00 +01:00
/*
* 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 [ ] = {
{ " xscale1 " , " arm/xscale1 " } ,
{ " xscale1 " , " arm/xscale2 " } ,
{ " v6 " , " arm/armv6 " } ,
{ " v6mpcore " , " arm/mpcore " } ,
{ " ARMv7 Cortex-A8 " , " arm/armv7 " } ,
{ " ARMv7 Cortex-A9 " , " arm/armv7-ca9 " } ,
} ;
2010-10-08 21:42:17 +01:00
char * op_name_from_perf_id ( void )
2010-04-30 11:36:54 +01:00
{
2012-07-06 15:45:00 +01:00
int i ;
struct op_perf_name names ;
const char * perf_name = perf_pmu_name ( ) ;
2010-10-08 21:42:17 +01:00
2012-07-06 15:45:00 +01:00
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 ;
2010-04-30 11:36:54 +01:00
}
2012-07-06 15:45:00 +01:00
return NULL ;
2010-04-30 11:36:54 +01:00
}
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 ( ) ;
}