/**1* @file backtrace.c2*3* @remark Copyright 2004 Silicon Graphics Inc. All Rights Reserved.4* @remark Read the file COPYING5*6* @author Greg Banks <[email protected]>7* @author Keith Owens <[email protected]>8* Based on work done for the ia64 port of the SGI kernprof patch, which is9* Copyright (c) 2003-2004 Silicon Graphics Inc. All Rights Reserved.10*/1112#include <linux/oprofile.h>13#include <linux/sched.h>14#include <linux/mm.h>15#include <asm/ptrace.h>16#include <asm/system.h>1718/*19* For IA64 we need to perform a complex little dance to get both20* the struct pt_regs and a synthetic struct switch_stack in place21* to allow the unwind code to work. This dance requires our unwind22* using code to be called from a function called from unw_init_running().23* There we only get a single void* data pointer, so use this struct24* to hold all the data we need during the unwind.25*/26typedef struct27{28unsigned int depth;29struct pt_regs *regs;30struct unw_frame_info frame;31unsigned long *prev_pfs_loc; /* state for WAR for old spinlock ool code */32} ia64_backtrace_t;3334/* Returns non-zero if the PC is in the Interrupt Vector Table */35static __inline__ int in_ivt_code(unsigned long pc)36{37extern char ia64_ivt[];38return (pc >= (u_long)ia64_ivt && pc < (u_long)ia64_ivt+32768);39}4041/*42* Unwind to next stack frame.43*/44static __inline__ int next_frame(ia64_backtrace_t *bt)45{46/*47* Avoid unsightly console message from unw_unwind() when attempting48* to unwind through the Interrupt Vector Table which has no unwind49* information.50*/51if (in_ivt_code(bt->frame.ip))52return 0;5354/*55* WAR for spinlock contention from leaf functions. ia64_spinlock_contention_pre3_456* has ar.pfs == r0. Leaf functions do not modify ar.pfs so ar.pfs remains57* as 0, stopping the backtrace. Record the previous ar.pfs when the current58* IP is in ia64_spinlock_contention_pre3_4 then unwind, if pfs_loc has not changed59* after unwind then use pt_regs.ar_pfs which is where the real ar.pfs is for60* leaf functions.61*/62if (bt->prev_pfs_loc && bt->regs && bt->frame.pfs_loc == bt->prev_pfs_loc)63bt->frame.pfs_loc = &bt->regs->ar_pfs;64bt->prev_pfs_loc = NULL;6566return unw_unwind(&bt->frame) == 0;67}686970static void do_ia64_backtrace(struct unw_frame_info *info, void *vdata)71{72ia64_backtrace_t *bt = vdata;73struct switch_stack *sw;74int count = 0;75u_long pc, sp;7677sw = (struct switch_stack *)(info+1);78/* padding from unw_init_running */79sw = (struct switch_stack *)(((unsigned long)sw + 15) & ~15);8081unw_init_frame_info(&bt->frame, current, sw);8283/* skip over interrupt frame and oprofile calls */84do {85unw_get_sp(&bt->frame, &sp);86if (sp >= (u_long)bt->regs)87break;88if (!next_frame(bt))89return;90} while (count++ < 200);9192/* finally, grab the actual sample */93while (bt->depth-- && next_frame(bt)) {94unw_get_ip(&bt->frame, &pc);95oprofile_add_trace(pc);96if (unw_is_intr_frame(&bt->frame)) {97/*98* Interrupt received on kernel stack; this can99* happen when timer interrupt fires while processing100* a softirq from the tail end of a hardware interrupt101* which interrupted a system call. Don't laugh, it102* happens! Splice the backtrace into two parts to103* avoid spurious cycles in the gprof output.104*/105/* TODO: split rather than drop the 2nd half */106break;107}108}109}110111void112ia64_backtrace(struct pt_regs * const regs, unsigned int depth)113{114ia64_backtrace_t bt;115unsigned long flags;116117/*118* On IA64 there is little hope of getting backtraces from119* user space programs -- the problems of getting the unwind120* information from arbitrary user programs are extreme.121*/122if (user_mode(regs))123return;124125bt.depth = depth;126bt.regs = regs;127bt.prev_pfs_loc = NULL;128local_irq_save(flags);129unw_init_running(do_ia64_backtrace, &bt);130local_irq_restore(flags);131}132133134