4 * @remark Copyright 2004 Silicon Graphics Inc. All Rights Reserved.
5 * @remark Read the file COPYING
7 * @author Greg Banks <gnb@melbourne.sgi.com>
8 * @author Keith Owens <kaos@melbourne.sgi.com>
9 * Based on work done for the ia64 port of the SGI kernprof patch, which is
10 * Copyright (c) 2003-2004 Silicon Graphics Inc. All Rights Reserved.
13 #include <linux/oprofile.h>
14 #include <linux/sched.h>
16 #include <asm/ptrace.h>
19 * For IA64 we need to perform a complex little dance to get both
20 * the struct pt_regs and a synthetic struct switch_stack in place
21 * to allow the unwind code to work. This dance requires our unwind
22 * 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 struct
24 * to hold all the data we need during the unwind.
30 struct unw_frame_info frame
;
31 unsigned long *prev_pfs_loc
; /* state for WAR for old spinlock ool code */
34 /* Returns non-zero if the PC is in the Interrupt Vector Table */
35 static __inline__
int in_ivt_code(unsigned long pc
)
37 extern char ia64_ivt
[];
38 return (pc
>= (u_long
)ia64_ivt
&& pc
< (u_long
)ia64_ivt
+32768);
42 * Unwind to next stack frame.
44 static __inline__
int next_frame(ia64_backtrace_t
*bt
)
47 * Avoid unsightly console message from unw_unwind() when attempting
48 * to unwind through the Interrupt Vector Table which has no unwind
51 if (in_ivt_code(bt
->frame
.ip
))
55 * WAR for spinlock contention from leaf functions. ia64_spinlock_contention_pre3_4
56 * has ar.pfs == r0. Leaf functions do not modify ar.pfs so ar.pfs remains
57 * as 0, stopping the backtrace. Record the previous ar.pfs when the current
58 * IP is in ia64_spinlock_contention_pre3_4 then unwind, if pfs_loc has not changed
59 * after unwind then use pt_regs.ar_pfs which is where the real ar.pfs is for
62 if (bt
->prev_pfs_loc
&& bt
->regs
&& bt
->frame
.pfs_loc
== bt
->prev_pfs_loc
)
63 bt
->frame
.pfs_loc
= &bt
->regs
->ar_pfs
;
64 bt
->prev_pfs_loc
= NULL
;
66 return unw_unwind(&bt
->frame
) == 0;
70 static void do_ia64_backtrace(struct unw_frame_info
*info
, void *vdata
)
72 ia64_backtrace_t
*bt
= vdata
;
73 struct switch_stack
*sw
;
77 sw
= (struct switch_stack
*)(info
+1);
78 /* padding from unw_init_running */
79 sw
= (struct switch_stack
*)(((unsigned long)sw
+ 15) & ~15);
81 unw_init_frame_info(&bt
->frame
, current
, sw
);
83 /* skip over interrupt frame and oprofile calls */
85 unw_get_sp(&bt
->frame
, &sp
);
86 if (sp
>= (u_long
)bt
->regs
)
90 } while (count
++ < 200);
92 /* finally, grab the actual sample */
93 while (bt
->depth
-- && next_frame(bt
)) {
94 unw_get_ip(&bt
->frame
, &pc
);
95 oprofile_add_trace(pc
);
96 if (unw_is_intr_frame(&bt
->frame
)) {
98 * Interrupt received on kernel stack; this can
99 * happen when timer interrupt fires while processing
100 * a softirq from the tail end of a hardware interrupt
101 * which interrupted a system call. Don't laugh, it
102 * happens! Splice the backtrace into two parts to
103 * avoid spurious cycles in the gprof output.
105 /* TODO: split rather than drop the 2nd half */
112 ia64_backtrace(struct pt_regs
* const regs
, unsigned int depth
)
118 * On IA64 there is little hope of getting backtraces from
119 * user space programs -- the problems of getting the unwind
120 * information from arbitrary user programs are extreme.
127 bt
.prev_pfs_loc
= NULL
;
128 local_irq_save(flags
);
129 unw_init_running(do_ia64_backtrace
, &bt
);
130 local_irq_restore(flags
);