~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to arch/mips/oprofile/backtrace.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <linux/oprofile.h>
 
2
#include <linux/sched.h>
 
3
#include <linux/mm.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>
 
10
#include <asm/inst.h>
 
11
 
 
12
struct stackframe {
 
13
        unsigned long sp;
 
14
        unsigned long pc;
 
15
        unsigned long ra;
 
16
};
 
17
 
 
18
static inline int get_mem(unsigned long addr, unsigned long *result)
 
19
{
 
20
        unsigned long *address = (unsigned long *) addr;
 
21
        if (!access_ok(VERIFY_READ, addr, sizeof(unsigned long)))
 
22
                return -1;
 
23
        if (__copy_from_user_inatomic(result, address, sizeof(unsigned long)))
 
24
                return -3;
 
25
        return 0;
 
26
}
 
27
 
 
28
/*
 
29
 * These two instruction helpers were taken from process.c
 
30
 */
 
31
static inline int is_ra_save_ins(union mips_instruction *ip)
 
32
{
 
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;
 
36
}
 
37
 
 
38
static inline int is_sp_move_ins(union mips_instruction *ip)
 
39
{
 
40
        /* addiu/daddiu sp,sp,-imm */
 
41
        if (ip->i_format.rs != 29 || ip->i_format.rt != 29)
 
42
                return 0;
 
43
        if (ip->i_format.opcode == addiu_op || ip->i_format.opcode == daddiu_op)
 
44
                return 1;
 
45
        return 0;
 
46
}
 
47
 
 
48
/*
 
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.
 
51
 */
 
52
static inline int is_end_of_function_marker(union mips_instruction *ip)
 
53
{
 
54
        /* jr ra */
 
55
        if (ip->r_format.func == jr_op && ip->r_format.rs == 31)
 
56
                return 1;
 
57
        /* lui gp */
 
58
        if (ip->i_format.opcode == lui_op && ip->i_format.rt == 28)
 
59
                return 1;
 
60
        return 0;
 
61
}
 
62
 
 
63
/*
 
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
 
69
 */
 
70
 
 
71
static inline int unwind_user_frame(struct stackframe *old_frame,
 
72
                                    const unsigned int max_instr_check)
 
73
{
 
74
        struct stackframe new_frame = *old_frame;
 
75
        off_t ra_offset = 0;
 
76
        size_t stack_size = 0;
 
77
        unsigned long addr;
 
78
 
 
79
        if (old_frame->pc == 0 || old_frame->sp == 0 || old_frame->ra == 0)
 
80
                return -9;
 
81
 
 
82
        for (addr = new_frame.pc; (addr + max_instr_check > new_frame.pc)
 
83
                && (!ra_offset || !stack_size); --addr) {
 
84
                union mips_instruction ip;
 
85
 
 
86
                if (get_mem(addr, (unsigned long *) &ip))
 
87
                        return -11;
 
88
 
 
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. */
 
94
                                break;
 
95
                        stack_size = (unsigned) stack_adjustment;
 
96
                } else if (is_ra_save_ins(&ip)) {
 
97
                        int ra_slot = ip.i_format.simmediate;
 
98
                        if (ra_slot < 0)
 
99
                                /* This shouldn't happen. */
 
100
                                break;
 
101
                        ra_offset = ra_slot;
 
102
                } else if (is_end_of_function_marker(&ip))
 
103
                        break;
 
104
        }
 
105
 
 
106
        if (!ra_offset || !stack_size)
 
107
                return -1;
 
108
 
 
109
        if (ra_offset) {
 
110
                new_frame.ra = old_frame->sp + ra_offset;
 
111
                if (get_mem(new_frame.ra, &(new_frame.ra)))
 
112
                        return -13;
 
113
        }
 
114
 
 
115
        if (stack_size) {
 
116
                new_frame.sp = old_frame->sp + stack_size;
 
117
                if (get_mem(new_frame.sp, &(new_frame.sp)))
 
118
                        return -14;
 
119
        }
 
120
 
 
121
        if (new_frame.sp > old_frame->sp)
 
122
                return -2;
 
123
 
 
124
        new_frame.pc = old_frame->ra;
 
125
        *old_frame = new_frame;
 
126
 
 
127
        return 0;
 
128
}
 
129
 
 
130
static inline void do_user_backtrace(unsigned long low_addr,
 
131
                                     struct stackframe *frame,
 
132
                                     unsigned int depth)
 
133
{
 
134
        const unsigned int max_instr_check = 512;
 
135
        const unsigned long high_addr = low_addr + THREAD_SIZE;
 
136
 
 
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)
 
140
                        break;
 
141
        }
 
142
}
 
143
 
 
144
#ifndef CONFIG_KALLSYMS
 
145
static inline void do_kernel_backtrace(unsigned long low_addr,
 
146
                                       struct stackframe *frame,
 
147
                                       unsigned int depth) { }
 
148
#else
 
149
static inline void do_kernel_backtrace(unsigned long low_addr,
 
150
                                       struct stackframe *frame,
 
151
                                       unsigned int depth)
 
152
{
 
153
        while (depth-- && frame->pc) {
 
154
                frame->pc = unwind_stack_by_address(low_addr,
 
155
                                                    &(frame->sp),
 
156
                                                    frame->pc,
 
157
                                                    &(frame->ra));
 
158
                oprofile_add_trace(frame->ra);
 
159
        }
 
160
}
 
161
#endif
 
162
 
 
163
void notrace op_mips_backtrace(struct pt_regs *const regs, unsigned int depth)
 
164
{
 
165
        struct stackframe frame = { .sp = regs->regs[29],
 
166
                                    .pc = regs->cp0_epc,
 
167
                                    .ra = regs->regs[31] };
 
168
        const int userspace = user_mode(regs);
 
169
        const unsigned long low_addr = ALIGN(frame.sp, THREAD_SIZE);
 
170
 
 
171
        if (userspace)
 
172
                do_user_backtrace(low_addr, &frame, depth);
 
173
        else
 
174
                do_kernel_backtrace(low_addr, &frame, depth);
 
175
}