1 #include <linux/oprofile.h>
2 #include <linux/sched.h>
4 #include <linux/uaccess.h>
5 #include <asm/ptrace.h>
6 #include <asm/stacktrace.h>
7 #include <linux/stacktrace.h>
8 #include <linux/kernel.h>
9 #include <asm/sections.h>
18 static inline int get_mem(unsigned long addr
, unsigned long *result
)
20 unsigned long *address
= (unsigned long *) addr
;
21 if (!access_ok(VERIFY_READ
, addr
, sizeof(unsigned long)))
23 if (__copy_from_user_inatomic(result
, address
, sizeof(unsigned long)))
29 * These two instruction helpers were taken from process.c
31 static inline int is_ra_save_ins(union mips_instruction
*ip
)
33 /* sw / sd $ra, offset($sp) */
34 return (ip
->i_format
.opcode
== sw_op
|| ip
->i_format
.opcode
== sd_op
)
35 && ip
->i_format
.rs
== 29 && ip
->i_format
.rt
== 31;
38 static inline int is_sp_move_ins(union mips_instruction
*ip
)
40 /* addiu/daddiu sp,sp,-imm */
41 if (ip
->i_format
.rs
!= 29 || ip
->i_format
.rt
!= 29)
43 if (ip
->i_format
.opcode
== addiu_op
|| ip
->i_format
.opcode
== daddiu_op
)
49 * Looks for specific instructions that mark the end of a function.
50 * This usually means we ran into the code area of the previous function.
52 static inline int is_end_of_function_marker(union mips_instruction
*ip
)
55 if (ip
->r_format
.func
== jr_op
&& ip
->r_format
.rs
== 31)
58 if (ip
->i_format
.opcode
== lui_op
&& ip
->i_format
.rt
== 28)
64 * TODO for userspace stack unwinding:
65 * - handle cases where the stack is adjusted inside a function
66 * (generally doesn't happen)
67 * - find optimal value for max_instr_check
68 * - try to find a way to handle leaf functions
71 static inline int unwind_user_frame(struct stackframe
*old_frame
,
72 const unsigned int max_instr_check
)
74 struct stackframe new_frame
= *old_frame
;
76 size_t stack_size
= 0;
79 if (old_frame
->pc
== 0 || old_frame
->sp
== 0 || old_frame
->ra
== 0)
82 for (addr
= new_frame
.pc
; (addr
+ max_instr_check
> new_frame
.pc
)
83 && (!ra_offset
|| !stack_size
); --addr
) {
84 union mips_instruction ip
;
86 if (get_mem(addr
, (unsigned long *) &ip
))
89 if (is_sp_move_ins(&ip
)) {
90 int stack_adjustment
= ip
.i_format
.simmediate
;
91 if (stack_adjustment
> 0)
92 /* This marks the end of the previous function,
93 which means we overran. */
95 stack_size
= (unsigned) stack_adjustment
;
96 } else if (is_ra_save_ins(&ip
)) {
97 int ra_slot
= ip
.i_format
.simmediate
;
99 /* This shouldn't happen. */
102 } else if (is_end_of_function_marker(&ip
))
106 if (!ra_offset
|| !stack_size
)
110 new_frame
.ra
= old_frame
->sp
+ ra_offset
;
111 if (get_mem(new_frame
.ra
, &(new_frame
.ra
)))
116 new_frame
.sp
= old_frame
->sp
+ stack_size
;
117 if (get_mem(new_frame
.sp
, &(new_frame
.sp
)))
121 if (new_frame
.sp
> old_frame
->sp
)
124 new_frame
.pc
= old_frame
->ra
;
125 *old_frame
= new_frame
;
130 static inline void do_user_backtrace(unsigned long low_addr
,
131 struct stackframe
*frame
,
134 const unsigned int max_instr_check
= 512;
135 const unsigned long high_addr
= low_addr
+ THREAD_SIZE
;
137 while (depth
-- && !unwind_user_frame(frame
, max_instr_check
)) {
138 oprofile_add_trace(frame
->ra
);
139 if (frame
->sp
< low_addr
|| frame
->sp
> high_addr
)
144 #ifndef CONFIG_KALLSYMS
145 static inline void do_kernel_backtrace(unsigned long low_addr
,
146 struct stackframe
*frame
,
147 unsigned int depth
) { }
149 static inline void do_kernel_backtrace(unsigned long low_addr
,
150 struct stackframe
*frame
,
153 while (depth
-- && frame
->pc
) {
154 frame
->pc
= unwind_stack_by_address(low_addr
,
158 oprofile_add_trace(frame
->ra
);
163 void notrace
op_mips_backtrace(struct pt_regs
*const regs
, unsigned int depth
)
165 struct stackframe frame
= { .sp
= regs
->regs
[29],
167 .ra
= regs
->regs
[31] };
168 const int userspace
= user_mode(regs
);
169 const unsigned long low_addr
= ALIGN(frame
.sp
, THREAD_SIZE
);
172 do_user_backtrace(low_addr
, &frame
, depth
);
174 do_kernel_backtrace(low_addr
, &frame
, depth
);