2005-04-17 02:20:36 +04:00
/**
* @ file backtrace . c
*
* @ remark Copyright 2004 Silicon Graphics Inc . All Rights Reserved .
* @ remark Read the file COPYING
*
* @ author Greg Banks < gnb @ melbourne . sgi . com >
* @ author Keith Owens < kaos @ melbourne . sgi . com >
* Based on work done for the ia64 port of the SGI kernprof patch , which is
* Copyright ( c ) 2003 - 2004 Silicon Graphics Inc . All Rights Reserved .
*/
# include <linux/oprofile.h>
# include <linux/sched.h>
# include <linux/mm.h>
# include <asm/ptrace.h>
/*
* For IA64 we need to perform a complex little dance to get both
* the struct pt_regs and a synthetic struct switch_stack in place
* to allow the unwind code to work . This dance requires our unwind
* using code to be called from a function called from unw_init_running ( ) .
* There we only get a single void * data pointer , so use this struct
* to hold all the data we need during the unwind .
*/
typedef struct
{
unsigned int depth ;
struct pt_regs * regs ;
struct unw_frame_info frame ;
2011-04-07 22:05:56 +04:00
unsigned long * prev_pfs_loc ; /* state for WAR for old spinlock ool code */
2005-04-17 02:20:36 +04:00
} ia64_backtrace_t ;
/* Returns non-zero if the PC is in the Interrupt Vector Table */
static __inline__ int in_ivt_code ( unsigned long pc )
{
extern char ia64_ivt [ ] ;
return ( pc > = ( u_long ) ia64_ivt & & pc < ( u_long ) ia64_ivt + 32768 ) ;
}
/*
* Unwind to next stack frame .
*/
static __inline__ int next_frame ( ia64_backtrace_t * bt )
{
/*
* Avoid unsightly console message from unw_unwind ( ) when attempting
* to unwind through the Interrupt Vector Table which has no unwind
* information .
*/
if ( in_ivt_code ( bt - > frame . ip ) )
return 0 ;
/*
* WAR for spinlock contention from leaf functions . ia64_spinlock_contention_pre3_4
* has ar . pfs = = r0 . Leaf functions do not modify ar . pfs so ar . pfs remains
* as 0 , stopping the backtrace . Record the previous ar . pfs when the current
* IP is in ia64_spinlock_contention_pre3_4 then unwind , if pfs_loc has not changed
* after unwind then use pt_regs . ar_pfs which is where the real ar . pfs is for
* leaf functions .
*/
if ( bt - > prev_pfs_loc & & bt - > regs & & bt - > frame . pfs_loc = = bt - > prev_pfs_loc )
bt - > frame . pfs_loc = & bt - > regs - > ar_pfs ;
2009-09-25 19:42:16 +04:00
bt - > prev_pfs_loc = NULL ;
2005-04-17 02:20:36 +04:00
return unw_unwind ( & bt - > frame ) = = 0 ;
}
static void do_ia64_backtrace ( struct unw_frame_info * info , void * vdata )
{
ia64_backtrace_t * bt = vdata ;
struct switch_stack * sw ;
int count = 0 ;
u_long pc , sp ;
sw = ( struct switch_stack * ) ( info + 1 ) ;
/* padding from unw_init_running */
sw = ( struct switch_stack * ) ( ( ( unsigned long ) sw + 15 ) & ~ 15 ) ;
unw_init_frame_info ( & bt - > frame , current , sw ) ;
/* skip over interrupt frame and oprofile calls */
do {
unw_get_sp ( & bt - > frame , & sp ) ;
if ( sp > = ( u_long ) bt - > regs )
break ;
if ( ! next_frame ( bt ) )
return ;
} while ( count + + < 200 ) ;
/* finally, grab the actual sample */
while ( bt - > depth - - & & next_frame ( bt ) ) {
unw_get_ip ( & bt - > frame , & pc ) ;
oprofile_add_trace ( pc ) ;
if ( unw_is_intr_frame ( & bt - > frame ) ) {
/*
* Interrupt received on kernel stack ; this can
* happen when timer interrupt fires while processing
* a softirq from the tail end of a hardware interrupt
* which interrupted a system call . Don ' t laugh , it
* happens ! Splice the backtrace into two parts to
* avoid spurious cycles in the gprof output .
*/
/* TODO: split rather than drop the 2nd half */
break ;
}
}
}
void
ia64_backtrace ( struct pt_regs * const regs , unsigned int depth )
{
ia64_backtrace_t bt ;
unsigned long flags ;
/*
* On IA64 there is little hope of getting backtraces from
* user space programs - - the problems of getting the unwind
* information from arbitrary user programs are extreme .
*/
if ( user_mode ( regs ) )
return ;
bt . depth = depth ;
bt . regs = regs ;
bt . prev_pfs_loc = NULL ;
local_irq_save ( flags ) ;
unw_init_running ( do_ia64_backtrace , & bt ) ;
local_irq_restore ( flags ) ;
}