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