Initial commit of vpp code.
[vpp.git] / vppinfra / vppinfra / backtrace.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16   Copyright (c) 2004 Eliot Dresselhaus
17
18   Permission is hereby granted, free of charge, to any person obtaining
19   a copy of this software and associated documentation files (the
20   "Software"), to deal in the Software without restriction, including
21   without limitation the rights to use, copy, modify, merge, publish,
22   distribute, sublicense, and/or sell copies of the Software, and to
23   permit persons to whom the Software is furnished to do so, subject to
24   the following conditions:
25
26   The above copyright notice and this permission notice shall be
27   included in all copies or substantial portions of the Software.
28
29   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 */
37
38 #include <vppinfra/clib.h>
39 #include <vppinfra/error.h>
40
41 #ifdef __mips__
42
43 /* Let code below know we've defined _clib_backtrace */
44 #define clib_backtrace_defined
45
46 #include <vppinfra/asm_mips.h>
47
48 uword
49 clib_backtrace (uword * callers,
50                 uword max_callers,
51                 uword n_frames_to_skip)
52 {
53   u32 * pc;
54   void * sp;
55   uword i, saved_pc;
56
57   /* Figure current PC, saved PC and stack pointer. */
58   asm volatile (".set push\n"
59                 ".set noat\n"
60                 "move %[saved_pc], $31\n"
61                 "move %[sp], $29\n"
62                 /* Fetches current PC. */
63                 "la $at, 1f\n"
64                 "jalr %[pc], $at\n"
65                 "nop\n"
66                 "1:\n"
67                 ".set pop\n"
68                 : [pc] "=r" (pc),
69                 [saved_pc] "=r" (saved_pc),
70                 [sp] "=r" (sp));
71
72   /* Also skip current frame. */
73   n_frames_to_skip += 1;
74
75   for (i = 0; i < max_callers + n_frames_to_skip; i++)
76     {
77       mips_insn_opcode_t op;
78       mips_insn_special_funct_t funct;
79       i32 insn, rs, rt, rd, immediate, found_saved_pc;
80       u32 * start_pc;
81
82       /* Parse instructions until we reach prologue for this
83          stack frame.  We'll need to figure out where saved
84          PC is and where previous stack frame lives. */
85       start_pc = pc;
86       found_saved_pc = 0;
87       while (1)
88         {
89           insn = *--pc;
90           op = mips_insn_get_op (insn);
91           funct = mips_insn_get_funct (insn);
92           rs = mips_insn_get_rs (insn);
93           rt = mips_insn_get_rt (insn);
94           rd = mips_insn_get_rd (insn);
95           immediate = mips_insn_get_immediate (insn);
96
97           switch (op) {
98           default:
99             break;
100
101           case MIPS_OPCODE_sd:
102           case MIPS_OPCODE_sw:
103             /* Trace stores of return address. */
104             if (rt == MIPS_REG_RA)
105               {
106                 void * addr = sp + immediate;
107
108                 /* If RA is stored somewhere other than in the
109                    stack frame, give up. */
110                 if (rs != MIPS_REG_SP)
111                   goto backtrace_done;
112
113                 ASSERT (immediate % 4 == 0);
114                 if (op == MIPS_OPCODE_sw)
115                   saved_pc = ((u32 *) addr)[0];
116                 else
117                   saved_pc = ((u64 *) addr)[0];
118                 found_saved_pc = 1;
119               }
120             break;
121
122           case MIPS_OPCODE_addiu:
123           case MIPS_OPCODE_daddiu:
124           case MIPS_OPCODE_addi:
125           case MIPS_OPCODE_daddi:
126             if (rt == MIPS_REG_SP)
127               {
128                 if (rs != MIPS_REG_SP)
129                   goto backtrace_done;
130
131                 ASSERT (immediate % 4 == 0);
132
133                 /* Assume positive offset is part of the epilogue.
134                    E.g.
135                       jr ra
136                       add sp,sp,100
137                 */
138                 if (immediate > 0)
139                   continue;
140
141                 /* Negative offset means allocate stack space.
142                    This could either be the prologue or could be due to
143                    alloca. */
144                 sp -= immediate;
145
146                 /* This frame will not save RA. */
147                 if (i == 0)
148                   goto found_prologue;
149
150                 /* Assume that addiu sp,sp,-N without store of ra means
151                    that we have not found the prologue yet. */
152                 if (found_saved_pc)
153                   goto found_prologue;
154               }
155             break;
156
157           case MIPS_OPCODE_slti:
158           case MIPS_OPCODE_sltiu:
159           case MIPS_OPCODE_andi:
160           case MIPS_OPCODE_ori:
161           case MIPS_OPCODE_xori:
162           case MIPS_OPCODE_lui:
163           case MIPS_OPCODE_ldl:
164           case MIPS_OPCODE_ldr:
165           case MIPS_OPCODE_lb:
166           case MIPS_OPCODE_lh:
167           case MIPS_OPCODE_lwl:
168           case MIPS_OPCODE_lw:
169           case MIPS_OPCODE_lbu:
170           case MIPS_OPCODE_lhu:
171           case MIPS_OPCODE_lwr:
172           case MIPS_OPCODE_lwu:
173           case MIPS_OPCODE_ld:
174             /* Give up when we find anyone setting the stack pointer. */
175             if (rt == MIPS_REG_SP)
176               goto backtrace_done;
177             break;
178
179           case MIPS_OPCODE_SPECIAL:
180           if (rd == MIPS_REG_SP)
181             switch (funct) {
182             default:
183               /* Give up when we find anyone setting the stack pointer. */
184               goto backtrace_done;
185
186             case MIPS_SPECIAL_FUNCT_break:
187             case MIPS_SPECIAL_FUNCT_jr:
188             case MIPS_SPECIAL_FUNCT_sync:
189             case MIPS_SPECIAL_FUNCT_syscall:
190             case MIPS_SPECIAL_FUNCT_tge:
191             case MIPS_SPECIAL_FUNCT_tgeu:
192             case MIPS_SPECIAL_FUNCT_tlt:
193             case MIPS_SPECIAL_FUNCT_tltu:
194             case MIPS_SPECIAL_FUNCT_teq:
195             case MIPS_SPECIAL_FUNCT_tne:
196               /* These instructions can validly have rd == MIPS_REG_SP */
197               break;
198             }
199           break;
200           }
201         }
202
203     found_prologue:
204       /* Check sanity of saved pc. */
205       if (saved_pc & 3)
206           goto backtrace_done;
207       if (saved_pc == 0)
208           goto backtrace_done;
209
210       if (i >= n_frames_to_skip)
211         callers[i - n_frames_to_skip] = saved_pc;
212       pc = uword_to_pointer (saved_pc, u32 *);
213     }
214
215  backtrace_done:
216   if (i < n_frames_to_skip)
217     return 0;
218   else
219     return i - n_frames_to_skip;
220 }
221 #endif /* __mips__ */
222
223 #ifndef clib_backtrace_defined
224 #define clib_backtrace_defined
225
226 typedef struct clib_generic_stack_frame_t {
227   struct clib_generic_stack_frame_t * prev;
228   void * return_address;
229 } clib_generic_stack_frame_t;
230
231 /* This will only work if we have a frame pointer.
232    Without a frame pointer we have to parse the machine code to
233    parse the stack frames. */
234 uword
235 clib_backtrace (uword * callers,
236                 uword max_callers,
237                 uword n_frames_to_skip)
238 {
239   clib_generic_stack_frame_t * f;
240   uword i;
241
242   f = __builtin_frame_address (0);
243
244   /* Also skip current frame. */
245   n_frames_to_skip += 1;
246
247   for (i = 0; i < max_callers + n_frames_to_skip; i++)
248     {
249       f = f->prev;
250       if (! f)
251         goto backtrace_done;
252       if (clib_abs ((void *) f - (void *) f->prev) > (64*1024))
253         goto backtrace_done;
254       if (i >= n_frames_to_skip)
255         callers[i - n_frames_to_skip] = pointer_to_uword (f->return_address);
256     }
257
258  backtrace_done:
259   if (i < n_frames_to_skip)
260       return 0;
261   else
262       return i - n_frames_to_skip;
263 }
264 #endif /* clib_backtrace_defined */