1 /*
2 * File : mips_backtrace.c
3 * This file is part of RT-Thread RTOS
4 * COPYRIGHT (C) 2008 - 2012, RT-Thread Development Team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Change Logs:
21 * Date Author Notes
22 * 2016��9��11�� Urey the first version
23 */
24
25 #include <rtthread.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <stdint.h>
29
30 #include "mips.h"
31
32 /*********************************************************************************************************
33 ָ���
34 *********************************************************************************************************/
35 #define ADDUI_SP_INST 0x27bd0000
36 #define SW_RA_INST 0xafbf0000
37 #define JR_RA_INST 0x03e00008
38
39 #define INST_OP_MASK 0xffff0000
40 #define INST_OFFSET_MASK 0x0000ffff
41
42 #define abs(s) ((s) < 0 ? -(s):(s))
43
backtrace_ctx(mips_reg_ctx * ctx)44 int backtrace_ctx(mips_reg_ctx *ctx)
45 {
46 unsigned long *addr;
47 unsigned long *pc, *ra, *sp;
48 size_t ra_offset;
49 size_t stack_size;
50 int depth;
51 int size = 8;
52
53 pc = (unsigned long *)(unsigned long)ctx->CP0EPC;
54 ra = (unsigned long *)(unsigned long)ctx->regs[REG_RA];
55 sp = (unsigned long *)(unsigned long)ctx->regs[REG_SP];
56
57 rt_kprintf("[0x%08x]\n", pc);
58
59 if (size == 1) return 1;
60
61 ra_offset = stack_size = 0;
62
63 for (addr = ra; !ra_offset || !stack_size; --addr)
64 {
65 switch (*addr & INST_OP_MASK) {
66 case ADDUI_SP_INST:
67 stack_size = abs((short)(*addr&INST_OFFSET_MASK));
68 break;
69
70 case SW_RA_INST:
71 ra_offset = (short)(*addr&INST_OFFSET_MASK);
72 break;
73
74 case 0x3c1c0000:
75 goto out_of_loop;
76
77 default:
78 break;
79 }
80 }
81
82 out_of_loop:
83 if (ra_offset) ra = *(unsigned long **)((unsigned long)sp + ra_offset);
84 if (stack_size) sp = (unsigned long *)((unsigned long)sp + stack_size);
85
86 // repeat backwar scanning
87 for (depth = 1; depth < size && ra && ra != (unsigned long *)0xffffffff; ++depth)
88 {
89 rt_kprintf("RA[%2d] : [0x%08x]\n", depth ,ra);
90
91 ra_offset = 0;
92 stack_size = 0;
93
94 for ( addr = ra; !ra_offset || !stack_size; -- addr )
95 {
96 switch( *addr & INST_OP_MASK)
97 {
98 case ADDUI_SP_INST:
99 stack_size = abs((short)(*addr&INST_OFFSET_MASK));
100 break;
101
102 case SW_RA_INST:
103 ra_offset = abs((short)(*addr&INST_OFFSET_MASK));
104 break;
105
106 case 0x3c1c0000:
107 return depth +1;
108
109 default:
110 break;
111 }
112 }
113
114 ra = *(unsigned long **)((unsigned long)sp + ra_offset);
115 sp = (unsigned long *)((unsigned long)sp + stack_size);
116 }
117
118 return depth;
119 }
120
backtrace(void)121 int backtrace(void)
122 {
123 unsigned long *addr;
124 unsigned long *ra;
125 unsigned long *sp;
126 int size = 8, depth;
127
128 size_t ra_offset;
129 size_t stack_size;
130
131 // get current $a and $sp
132 __asm__ __volatile__ (
133 " move %0, $ra\n"
134 " move %1, $sp\n"
135 : "=r"(ra), "=r"(sp)
136 );
137
138 // scanning to find the size of hte current stack frame
139 stack_size = 0;
140
141 for ( addr = (unsigned long *)backtrace; !stack_size; ++addr)
142 {
143 if ((*addr & INST_OP_MASK ) == ADDUI_SP_INST )
144 stack_size = abs((short)(*addr&INST_OFFSET_MASK));
145 else if ( *addr == JR_RA_INST )
146 break;
147 }
148
149 sp = (unsigned long *) (( unsigned long )sp + stack_size);
150
151 // repeat backwar scanning
152 for ( depth = 0; depth < size && ((( unsigned long )ra > KSEG0BASE) && (( unsigned long )ra < KSEG1BASE)); ++ depth )
153 {
154 rt_kprintf("RA[%2d] : [0x%08x]\n", depth, ra);
155 {
156 extern void rt_thread_exit(void);
157 if ((uint32_t)ra == (uint32_t)(rt_thread_exit))
158 return depth;
159 }
160
161 ra_offset = 0;
162 stack_size = 0;
163
164 for ( addr = ra; !ra_offset || !stack_size; -- addr )
165 {
166 switch( *addr & INST_OP_MASK)
167 {
168 case ADDUI_SP_INST:
169 stack_size = abs((short)(*addr&INST_OFFSET_MASK));
170 break;
171
172 case SW_RA_INST:
173 ra_offset = (short)(*addr&INST_OFFSET_MASK);
174 break;
175
176 case 0x3c1c0000:
177 return depth +1;
178
179 default:
180 break;
181 }
182 }
183
184 ra = *(unsigned long **)((unsigned long)sp + ra_offset);
185 sp = (unsigned long*) ((unsigned long)sp+stack_size );
186 }
187
188 return depth;
189 }
190
191 #include <rtthread.h>
192 extern long list_thread(void);
assert_hook(const char * ex,const char * func,rt_size_t line)193 void assert_hook(const char* ex, const char* func, rt_size_t line)
194 {
195 backtrace();
196
197 list_thread();
198 rt_kprintf("(%s) assertion failed at function:%s, line number:%d \n", ex, func, line);
199 }
200
backtrace_init(void)201 int backtrace_init(void)
202 {
203 #ifdef RT_DEBUG
204 rt_assert_set_hook(assert_hook);
205 #endif
206 return 0;
207 }
208 INIT_DEVICE_EXPORT(backtrace_init);
209