1 /*
2 * Copyright (C) 2015-2021 Alibaba Group Holding Limited
3 */
4
5 #include <stddef.h>
6 #include <stdint.h>
7 #include <string.h>
8 #include <limits.h>
9 #include <stdio.h>
10 #include "k_api.h"
11
12 /* part of ktask_t */
13 typedef struct
14 {
15 void *task_stack;
16 }ktask_t_shadow;
17
18 //#define OS_BACKTRACE_DEBUG
19
20 extern void krhino_task_deathbed(void);
21 extern ktask_t_shadow *debug_task_find(char *name);
22 extern int debug_task_is_running(ktask_t_shadow *task);
23 extern void *debug_task_stack_bottom(ktask_t_shadow *task);
24 extern char *k_int2str(int num, char *str);
25
26 void backtrace_handle(char *PC, int *SP, char *LR, int (*print_func)(const char *fmt, ...));
27
28 #if defined(__CC_ARM)
29 #ifdef __BIG_ENDIAN
30 #error "Not support big-endian!"
31 #endif
32 #elif defined(__ICCARM__)
33 #if (__LITTLE_ENDIAN__ == 0)
34 #error "Not support big-endian!"
35 #endif
36 #elif defined(__GNUC__)
37 #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
38 #error "Not support big-endian!"
39 #endif
40 #endif
41
42 #define BT_FUNC_LIMIT 0x2000
43 #define BT_LVL_LIMIT 64
44 #define BT_PC2ADDR(pc) ((char *)(((uintptr_t)(pc)) & 0xfffffffe))
45
46 /* alios_debug_pc_check depends on mcu*/
alios_debug_pc_check(char * pc)47 __attribute__((weak)) int alios_debug_pc_check(char *pc)
48 {
49 return 0;
50 }
51
52 #if defined(__ICCARM__)
__builtin_popcount(unsigned int u)53 static unsigned int __builtin_popcount(unsigned int u)
54 {
55 unsigned int ret = 0;
56 while (u) {
57 u = (u & (u - 1));
58 ret++;
59 }
60 return ret;
61 }
62 #endif
63
getPLSfromCtx(void * context,char ** PC,char ** LR,int ** SP)64 void getPLSfromCtx(void *context, char **PC, char **LR, int **SP)
65 {
66 int *ptr = context;
67 int exc_return;
68
69 /* reference to cpu_task_stack_init */
70 exc_return = ptr[8];
71
72 if ((exc_return & 0x10) == 0x10) {
73 *PC = (char *)ptr[15];
74 *LR = (char *)ptr[14];
75 *SP = ptr + 17;
76 } else {
77 *PC = (char *)ptr[31];
78 *LR = (char *)ptr[30];
79 *SP = ptr + 51;
80 }
81 }
82
83 /* get "blx" or "bl" before LR, return offset */
backtraceFindLROffset(char * LR,int (* print_func)(const char * fmt,...))84 static int backtraceFindLROffset(char *LR,
85 int (*print_func)(const char *fmt, ...))
86 {
87 unsigned short ins16;
88 char s_panic_call[] = "backtrace : 0x \r\n";
89
90 LR = BT_PC2ADDR(LR);
91
92 /* callstack bottom */
93 if (((int)LR & 0xffffffe0) == 0xffffffe0) {
94 /* EXC_RETURN, so here is callstack bottom of interrupt handler */
95 if (print_func != NULL) {
96 print_func("backtrace : ^interrupt^\r\n");
97 }
98 return 0;
99 }
100
101 if (LR == BT_PC2ADDR(&krhino_task_deathbed)) {
102 /* task delete, so here is callstack bottom of task */
103 if (print_func != NULL) {
104 print_func("backtrace : ^task entry^\r\n");
105 }
106 return 0;
107 }
108
109 if (alios_debug_pc_check(LR) != 0) {
110 if (print_func) {
111 print_func("backtrace : invalid pc : 0x%x\r\n", LR);
112 }
113 return -1;
114 }
115
116 ins16 = *(unsigned short *)(LR - 4);
117 if ((ins16 & 0xf000) == 0xf000) {
118 if (print_func != NULL) {
119 k_int2str((int)LR - 4, &s_panic_call[14]);
120 print_func(s_panic_call);
121 }
122 return 5;
123 } else {
124 if (print_func != NULL) {
125 k_int2str((int)LR - 2, &s_panic_call[14]);
126 print_func(s_panic_call);
127 }
128 return 3;
129 }
130 }
131
132 /* find current function caller, update PC and SP
133 returns: 0 success
134 1 success and find buttom
135 -1 fail */
backtraceFromStack(int ** pSP,char ** pPC,int (* print_func)(const char * fmt,...))136 static int backtraceFromStack(int **pSP, char **pPC,
137 int (*print_func)(const char *fmt, ...))
138 {
139 char *CodeAddr = NULL;
140 int *SP = *pSP;
141 char *PC = *pPC;
142 char *LR;
143 int i;
144 unsigned short ins16;
145 unsigned int ins32;
146 unsigned int framesize = 0;
147 unsigned int shift = 0;
148 unsigned int sub = 0;
149 unsigned int offset = 1;
150 #ifdef OS_BACKTRACE_DEBUG
151 printf("[backtraceFromStack in ] SP = %p, PC = %p\n\r", *pSP, *pPC);
152 #endif
153
154 if (SP == debug_task_stack_bottom(NULL)) {
155 if (print_func != NULL) {
156 print_func("backtrace : ^task entry^\r\n");
157 }
158 return 1;
159 }
160
161 if (alios_debug_pc_check(*pPC) != 0) {
162 if (print_func) {
163 print_func("backtrace : invalid pc : 0x%x\r\n", *pPC);
164 }
165 return -1;
166 }
167
168 /* func call ways:
169 1. "stmdb sp!, ..." or "push ..." to open stack frame and save LR
170 2. "sub sp, ..." or "sub.w sp, ..." to open stack more
171 3. call
172 */
173
174 /* 1. scan code, find frame size from "push" or "stmdb sp!" */
175 for (i = 2; i < BT_FUNC_LIMIT; i += 2) {
176 /* find nearest "push {..., lr}" */
177 ins16 = *(unsigned short *)(PC - i);
178 if ((ins16 & 0xff00) == 0xb500) {
179 framesize = __builtin_popcount((unsigned char)ins16);
180 framesize++;
181 /* find double push */
182 ins16 = *(unsigned short *)(PC - i - 2);
183 if ((ins16 & 0xff00) == 0xb400) {
184 offset += __builtin_popcount((unsigned char)ins16);
185 framesize += __builtin_popcount((unsigned char)ins16);
186 }
187 CodeAddr = PC - i;
188 break;
189 }
190
191 /* find "stmdb sp!, ..." */
192 /* The Thumb instruction stream is a sequence of halfword-aligned
193 * halfwords */
194 ins32 = *(unsigned short *)(PC - i);
195 ins32 <<= 16;
196 ins32 |= *(unsigned short *)(PC - i + 2);
197 if ((ins32 & 0xFFFFF000) == 0xe92d4000) {
198 framesize = __builtin_popcount(ins32 & 0xfff);
199 framesize++;
200 CodeAddr = PC - i;
201 break;
202 }
203 }
204
205 if (CodeAddr == NULL) {
206 /* error branch */
207 if (print_func != NULL) {
208 print_func("Backtrace fail!\r\n");
209 }
210 return -1;
211 }
212
213 /* 2. scan code, find frame size from "sub" or "sub.w" */
214 for (i = 0; i < BT_FUNC_LIMIT;) {
215 if (CodeAddr + i > PC) {
216 break;
217 }
218 /* find "sub sp, ..." */
219 ins16 = *(unsigned short *)(CodeAddr + i);
220 if ((ins16 & 0xff80) == 0xb080) {
221 framesize += (ins16 & 0x7f);
222 break;
223 }
224
225 /* find "sub.w sp, sp, ..." */
226 ins32 = *(unsigned short *)(CodeAddr + i);
227 ins32 <<= 16;
228 ins32 |= *(unsigned short *)(CodeAddr + i + 2);
229 if ((ins32 & 0xFBFF8F00) == 0xF1AD0D00) {
230 sub = 128 + (ins32 & 0x7f);
231 shift = (ins32 >> 7) & 0x1;
232 shift += ((ins32 >> 12) & 0x7) << 1;
233 shift += ((ins32 >> 26) & 0x1) << 4;
234 framesize += sub << (30 - shift);
235 break;
236 }
237
238 if ((ins16 & 0xf800) >= 0xe800) {
239 i += 4;
240 } else {
241 i += 2;
242 }
243 }
244
245 #ifdef OS_BACKTRACE_DEBUG
246 printf("[backtraceFromStack out] frsz = %d offset = %d SP=%p\n\r", framesize, offset, SP);
247 #endif
248 /* 3. output */
249 LR = (char *)*(SP + framesize - offset);
250 offset = backtraceFindLROffset(LR, print_func);
251 if (offset < 0) {
252 return -1;
253 }
254 *pSP = SP + framesize;
255 *pPC = LR - offset;
256
257 return offset == 0 ? 1 : 0;
258 }
259
260 /* find current function caller, update PC and SP
261 returns: 0 success
262 1 success and find buttom
263 -1 fail */
backtraceFromLR(int ** pSP,char ** pPC,char * LR,int (* print_func)(const char * fmt,...))264 static int backtraceFromLR(int **pSP, char **pPC, char *LR,
265 int (*print_func)(const char *fmt, ...))
266 {
267 int *SP = *pSP;
268 char *PC = *pPC;
269 char *CodeAddr = NULL;
270 int i;
271 unsigned short ins16;
272 unsigned int framesize = 0;
273 unsigned int offset;
274
275 #ifdef OS_BACKTRACE_DEBUG
276 printf("[backtraceFromLR in ] SP = %p, PC = %p, LR = %p\n\r", *pSP, *pPC, LR);
277 #endif
278 if (alios_debug_pc_check(*pPC) != 0) {
279 offset = backtraceFindLROffset(LR, print_func);
280 if ( offset < 0 ){
281 return -1;
282 }
283 PC = LR - offset;
284 *pPC = PC;
285 return offset == 0 ? 1 : 0;
286 }
287
288 /*find stack framesize:
289 1. "push ..." to open stack
290 2. "sub sp, ..." to open stack
291 3. 1 + 2
292 4. do not open stack
293 */
294
295 /* 1. scan code, find frame size from "push" or "sub" */
296 for (i = 2; i < BT_FUNC_LIMIT; i += 2) {
297 ins16 = *(unsigned short *)(PC - i);
298 /* find "push {..., lr}" */
299 if ((ins16 & 0xff00) == 0xb500) {
300 /* another function */
301 break;
302 }
303 /* find "push {...}" */
304 if ((ins16 & 0xff00) == 0xb400) {
305 framesize = __builtin_popcount((unsigned char)ins16);
306 CodeAddr = PC - i;
307 break;
308 }
309 /* find "sub sp, ..." */
310 if ((ins16 & 0xff80) == 0xb080) {
311 framesize = (ins16 & 0x7f);
312 CodeAddr = PC - i;
313 /* find push before sub */
314 ins16 = *(unsigned short *)(PC - i - 2);
315 if ((ins16 & 0xff00) == 0xb400) {
316 framesize += __builtin_popcount((unsigned char)ins16);
317 CodeAddr = PC - i - 2;
318 }
319 break;
320 }
321 }
322
323 /* 2. check the "push" or "sub sp" belongs to another function */
324 if (CodeAddr != NULL) {
325 for (i = 2; i < PC - CodeAddr; i += 2) {
326 ins16 = *(unsigned short *)(PC - i);
327 /* find "pop {..., pc}" or "bx lr" */
328 if ((ins16 & 0xff00) == 0xbd00 || ins16 == 0x4770) {
329 /* SP no changed */
330 framesize = 0;
331 }
332 }
333 } /* else: SP no changed */
334
335 #ifdef OS_BACKTRACE_DEBUG
336 printf("[backtraceFromLR out] frsz = %d offset = %d SP=%p\n\r", framesize, offset, SP);
337 #endif
338 /* 3. output */
339 offset = backtraceFindLROffset(LR, print_func);
340 if ( offset < 0 ){
341 return -1;
342 }
343 *pSP = SP + framesize;
344 *pPC = LR - offset;
345
346 return offset == 0 ? 1 : 0;
347 }
348
349 /* printf call stack
350 return levels of call stack */
backtrace_now(int (* print_func)(const char * fmt,...))351 int backtrace_now(int (*print_func)(const char *fmt, ...))
352 {
353 char *PC;
354 int *SP;
355 int lvl;
356 int ret;
357
358 if (print_func == NULL) {
359 print_func = printf;
360 }
361
362 /* compiler specific */
363 #if defined(__CC_ARM)
364 SP = (int *)__current_sp();
365 PC = (char *)__current_pc();
366 #elif defined(__ICCARM__)
367 asm volatile("mov %0, sp\n" : "=r"(SP));
368 asm volatile("mov %0, pc\n" : "=r"(PC));
369 #elif defined(__GNUC__)
370 __asm__ volatile("mov %0, sp\n" : "=r"(SP));
371 __asm__ volatile("mov %0, pc\n" : "=r"(PC));
372 #endif
373
374 print_func("========== Call stack ==========\r\n");
375 for (lvl = 0; lvl < BT_LVL_LIMIT; lvl++) {
376 ret = backtraceFromStack(&SP, &PC, print_func);
377 if (ret != 0) {
378 break;
379 }
380 }
381 print_func("========== End ==========\r\n");
382 return lvl;
383 }
384
385 /* printf call stack for task
386 return levels of call stack */
backtrace_task(void * task,int (* print_func)(const char * fmt,...))387 void backtrace_task(void *task, int (*print_func)(const char *fmt, ...))
388 {
389 char *PC;
390 char *LR;
391 int *SP;
392 int lvl = 0;
393 int ret;
394 char panic_call[] = "backtrace : 0x \r\n";
395 //ktask_t_shadow *task;
396
397 if (print_func == NULL) {
398 print_func = printf;
399 }
400
401 getPLSfromCtx(((ktask_t *)task)->task_stack, &PC, &LR, &SP);
402
403 #ifdef OS_BACKTRACE_DEBUG
404 printf("[backtrace_task] SP = %p, PC = %p, LR = %p\r\n", SP, PC, LR);
405 #endif
406
407 print_func("========== Call stack ==========\r\n");
408 k_int2str((int)BT_PC2ADDR(PC), &panic_call[14]);
409 if (print_func != NULL) {
410 print_func(panic_call);
411 }
412
413 ret = debug_task_is_running(task);
414 switch (ret) {
415 case 0 :
416 case 1 :
417 case 2 :
418 backtrace_handle(PC, SP, LR, print_func);
419 break;
420
421 default:
422 print_func("Status of task \"%s\" is 'Running', Can not backtrace!\n",
423 ((ktask_t *)task)->task_name ? ((ktask_t *)task)->task_name : "anonym");
424 break;
425 }
426
427 print_func("========== End ==========\r\n");
428 }
429
430 /* backtrace start with PC and SP, find LR from stack memory
431 return levels of call stack */
backtrace_caller(char * PC,int * SP,int (* print_func)(const char * fmt,...))432 int backtrace_caller(char *PC, int *SP,
433 int (*print_func)(const char *fmt, ...))
434 {
435 int *bt_sp;
436 char *bt_pc;
437 int lvl, ret;
438 char s_panic_call[] = "backtrace : 0x \r\n";
439
440 /* caller must save LR in stack, so find LR from stack */
441
442 if (SP == NULL) {
443 return 0;
444 }
445
446 /* try with no LR */
447 bt_sp = SP;
448 bt_pc = BT_PC2ADDR(PC);
449 ret = -1;
450 for (lvl = 0; lvl < BT_LVL_LIMIT; lvl++) {
451 ret = backtraceFromStack(&bt_sp, &bt_pc, NULL);
452 if (ret != 0) {
453 break;
454 }
455 }
456 if (ret == 1) {
457 /* try success, output */
458 k_int2str((int)PC, &s_panic_call[14]);
459 if (print_func != NULL) {
460 print_func(s_panic_call);
461 }
462 bt_sp = SP;
463 bt_pc = PC;
464 ret = -1;
465 for (lvl = 1; lvl < BT_LVL_LIMIT; lvl++) {
466 ret = backtraceFromStack(&bt_sp, &bt_pc, print_func);
467 if (ret != 0) {
468 break;
469 }
470 }
471 return lvl;
472 }
473
474 return 0;
475 }
476
477 /* backtrace start with PC SP and LR
478 return levels of call stack */
backtrace_callee(char * PC,int * SP,char * LR,int (* print_func)(const char * fmt,...))479 int backtrace_callee(char *PC, int *SP, char *LR,
480 int (*print_func)(const char *fmt, ...))
481 {
482 int *bt_sp;
483 char *bt_pc;
484 char *bt_lr;
485 int lvl, ret;
486 char s_panic_call[] = "backtrace : 0x \r\n";
487
488 if (SP == NULL) {
489 return 0;
490 }
491
492 /* Backtrace: assume ReturnAddr is saved in LR when exception */
493 k_int2str((int)PC, &s_panic_call[14]);
494 if (print_func != NULL) {
495 print_func(s_panic_call);
496 }
497 lvl = 1;
498 bt_sp = SP;
499 bt_pc = PC;
500 bt_lr = LR;
501
502 /* try, with LR */
503 ret = backtraceFromLR(&bt_sp, &bt_pc, bt_lr, print_func);
504 if (ret == 0) {
505 for (; lvl < BT_LVL_LIMIT; lvl++) {
506 ret = backtraceFromStack(&bt_sp, &bt_pc, print_func);
507 if (ret != 0) {
508 break;
509 }
510 }
511 }
512
513 return lvl;
514 }
515
516
517 void *g_back_trace;
518 /**
519 Get call stack, return levels of call stack
520 trace[] output buffer
521 size buffer size
522 offset which lvl start
523 */
backtrace_now_get(void * trace[],int size,int offset)524 int backtrace_now_get(void *trace[], int size, int offset)
525 {
526 char *PC;
527 int *SP;
528 int lvl;
529 int ret;
530
531 /* compiler specific */
532 #if defined(__CC_ARM)
533 SP = (int *)__current_sp();
534 PC = (char *)__current_pc();
535 #elif defined(__ICCARM__)
536 asm volatile("mov %0, sp\n" : "=r"(SP));
537 asm volatile("mov %0, pc\n" : "=r"(PC));
538 #elif defined(__GNUC__)
539 __asm__ volatile("mov %0, sp\n" : "=r"(SP));
540 __asm__ volatile("mov %0, pc\n" : "=r"(PC));
541 #endif
542
543 memset(trace, 0, size*sizeof(void *));
544
545 g_back_trace = trace;
546
547 for (lvl = 0; lvl < BT_LVL_LIMIT; lvl++) {
548 ret = backtraceFromStack(&SP, &PC, NULL);
549 if (ret != 0) {
550 break;
551 }
552 if (lvl >= offset && lvl - offset < size) {
553 trace[lvl - offset] = PC;
554 }
555 if (lvl - offset >= size) {
556 break;
557 }
558 }
559 return lvl - offset < 0 ? 0 : lvl - offset;
560 }
561
backtrace_handle(char * PC,int * SP,char * LR,int (* print_func)(const char * fmt,...))562 void backtrace_handle(char *PC, int *SP, char *LR,
563 int (*print_func)(const char *fmt, ...))
564 {
565 if (SP == NULL) {
566 print_func("SP is NULL, Can't backtrace\r\n");
567 return;
568 }
569
570 if (backtrace_caller(PC, SP, print_func) > 0) {
571 /* Backtrace 1st try: assume ReturnAddr is saved in stack when exception */
572 /* backtrace success, do not try other way */
573 goto exit;
574 } else if (backtrace_callee(PC, SP, LR, print_func) > 0) {
575 /* Backtrace 2nd try: assume ReturnAddr is saved in LR when exception */
576 /* backtrace success, do not try other way */
577 goto exit;
578 } else {
579 /* Backtrace 3rd try: assume PC is invalalb, backtrace from LR */
580 backtrace_caller(LR, SP, print_func);
581 }
582
583 exit:
584 return;
585 }
586