bbfb792c656cef3ab055bb1ef4b76759f6cc951e
[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, uword max_callers, uword n_frames_to_skip)
50 {
51   u32 *pc;
52   void *sp;
53   uword i, saved_pc;
54
55   /* Figure current PC, saved PC and stack pointer. */
56   asm volatile (".set push\n"
57                 ".set noat\n" "move %[saved_pc], $31\n" "move %[sp], $29\n"
58                 /* Fetches current PC. */
59                 "la $at, 1f\n"
60                 "jalr %[pc], $at\n"
61                 "nop\n"
62                 "1:\n"
63                 ".set pop\n":[pc] "=r" (pc),
64                 [saved_pc] "=r" (saved_pc),[sp] "=r" (sp));
65
66   /* Also skip current frame. */
67   n_frames_to_skip += 1;
68
69   for (i = 0; i < max_callers + n_frames_to_skip; i++)
70     {
71       mips_insn_opcode_t op;
72       mips_insn_special_funct_t funct;
73       i32 insn, rs, rt, rd, immediate, found_saved_pc;
74       u32 *start_pc;
75
76       /* Parse instructions until we reach prologue for this
77          stack frame.  We'll need to figure out where saved
78          PC is and where previous stack frame lives. */
79       start_pc = pc;
80       found_saved_pc = 0;
81       while (1)
82         {
83           insn = *--pc;
84           op = mips_insn_get_op (insn);
85           funct = mips_insn_get_funct (insn);
86           rs = mips_insn_get_rs (insn);
87           rt = mips_insn_get_rt (insn);
88           rd = mips_insn_get_rd (insn);
89           immediate = mips_insn_get_immediate (insn);
90
91           switch (op)
92             {
93             default:
94               break;
95
96             case MIPS_OPCODE_sd:
97             case MIPS_OPCODE_sw:
98               /* Trace stores of return address. */
99               if (rt == MIPS_REG_RA)
100                 {
101                   void *addr = sp + immediate;
102
103                   /* If RA is stored somewhere other than in the
104                      stack frame, give up. */
105                   if (rs != MIPS_REG_SP)
106                     goto backtrace_done;
107
108                   ASSERT (immediate % 4 == 0);
109                   if (op == MIPS_OPCODE_sw)
110                     saved_pc = ((u32 *) addr)[0];
111                   else
112                     saved_pc = ((u64 *) addr)[0];
113                   found_saved_pc = 1;
114                 }
115               break;
116
117             case MIPS_OPCODE_addiu:
118             case MIPS_OPCODE_daddiu:
119             case MIPS_OPCODE_addi:
120             case MIPS_OPCODE_daddi:
121               if (rt == MIPS_REG_SP)
122                 {
123                   if (rs != MIPS_REG_SP)
124                     goto backtrace_done;
125
126                   ASSERT (immediate % 4 == 0);
127
128                   /* Assume positive offset is part of the epilogue.
129                      E.g.
130                      jr ra
131                      add sp,sp,100
132                    */
133                   if (immediate > 0)
134                     continue;
135
136                   /* Negative offset means allocate stack space.
137                      This could either be the prologue or could be due to
138                      alloca. */
139                   sp -= immediate;
140
141                   /* This frame will not save RA. */
142                   if (i == 0)
143                     goto found_prologue;
144
145                   /* Assume that addiu sp,sp,-N without store of ra means
146                      that we have not found the prologue yet. */
147                   if (found_saved_pc)
148                     goto found_prologue;
149                 }
150               break;
151
152             case MIPS_OPCODE_slti:
153             case MIPS_OPCODE_sltiu:
154             case MIPS_OPCODE_andi:
155             case MIPS_OPCODE_ori:
156             case MIPS_OPCODE_xori:
157             case MIPS_OPCODE_lui:
158             case MIPS_OPCODE_ldl:
159             case MIPS_OPCODE_ldr:
160             case MIPS_OPCODE_lb:
161             case MIPS_OPCODE_lh:
162             case MIPS_OPCODE_lwl:
163             case MIPS_OPCODE_lw:
164             case MIPS_OPCODE_lbu:
165             case MIPS_OPCODE_lhu:
166             case MIPS_OPCODE_lwr:
167             case MIPS_OPCODE_lwu:
168             case MIPS_OPCODE_ld:
169               /* Give up when we find anyone setting the stack pointer. */
170               if (rt == MIPS_REG_SP)
171                 goto backtrace_done;
172               break;
173
174             case MIPS_OPCODE_SPECIAL:
175               if (rd == MIPS_REG_SP)
176                 switch (funct)
177                   {
178                   default:
179                     /* Give up when we find anyone setting the stack pointer. */
180                     goto backtrace_done;
181
182                   case MIPS_SPECIAL_FUNCT_break:
183                   case MIPS_SPECIAL_FUNCT_jr:
184                   case MIPS_SPECIAL_FUNCT_sync:
185                   case MIPS_SPECIAL_FUNCT_syscall:
186                   case MIPS_SPECIAL_FUNCT_tge:
187                   case MIPS_SPECIAL_FUNCT_tgeu:
188                   case MIPS_SPECIAL_FUNCT_tlt:
189                   case MIPS_SPECIAL_FUNCT_tltu:
190                   case MIPS_SPECIAL_FUNCT_teq:
191                   case MIPS_SPECIAL_FUNCT_tne:
192                     /* These instructions can validly have rd == MIPS_REG_SP */
193                     break;
194                   }
195               break;
196             }
197         }
198
199     found_prologue:
200       /* Check sanity of saved pc. */
201       if (saved_pc & 3)
202         goto backtrace_done;
203       if (saved_pc == 0)
204         goto backtrace_done;
205
206       if (i >= n_frames_to_skip)
207         callers[i - n_frames_to_skip] = saved_pc;
208       pc = uword_to_pointer (saved_pc, u32 *);
209     }
210
211 backtrace_done:
212   if (i < n_frames_to_skip)
213     return 0;
214   else
215     return i - n_frames_to_skip;
216 }
217 #endif /* __mips__ */
218
219 #ifndef clib_backtrace_defined
220 #define clib_backtrace_defined
221
222 typedef struct clib_generic_stack_frame_t
223 {
224   struct clib_generic_stack_frame_t *prev;
225   void *return_address;
226 } clib_generic_stack_frame_t;
227
228 /* This will only work if we have a frame pointer.
229    Without a frame pointer we have to parse the machine code to
230    parse the stack frames. */
231 uword
232 clib_backtrace (uword * callers, uword max_callers, uword n_frames_to_skip)
233 {
234   clib_generic_stack_frame_t *f;
235   uword i;
236
237   f = __builtin_frame_address (0);
238
239   /* Also skip current frame. */
240   n_frames_to_skip += 1;
241
242   for (i = 0; i < max_callers + n_frames_to_skip; i++)
243     {
244       f = f->prev;
245       if (!f)
246         goto backtrace_done;
247       if (clib_abs ((void *) f - (void *) f->prev) > (64 * 1024))
248         goto backtrace_done;
249       if (i >= n_frames_to_skip)
250         callers[i - n_frames_to_skip] = pointer_to_uword (f->return_address);
251     }
252
253 backtrace_done:
254   if (i < n_frames_to_skip)
255     return 0;
256   else
257     return i - n_frames_to_skip;
258 }
259 #endif /* clib_backtrace_defined */
260
261 /*
262  * fd.io coding-style-patch-verification: ON
263  *
264  * Local Variables:
265  * eval: (c-set-style "gnu")
266  * End:
267  */