应用场景:嵌入式跑死问题追溯
提供办法:打印函数的调用关系
backtrace原理

1. main函数运行,main函数调用func1, func1调用func2...2. 当函数调用产生时,会将arm的寄存器PC/LR/SP/FP顺次压栈,造成栈帧,本次的配角是FP寄存器3. main函数调用func1, 会将main函数的栈帧起始地址放入func1的FP寄存器,即func1的FP寄存器指向栈中用于寄存main函数栈的起始地位,即main函数PC指针的前一个地位。如下图所示,留神图中的箭头


接下来咱们看下如下代码:

#include <stdio.h>#include <stdint.h>#include <string.h>//-------------------- backtrace --------------------------extern void backtrace(int fp);extern int div(int a, int b);extern int main(int argc, char **argv);#define TOSTRING(x) #x#define READ_REGISTER(var) __asm volatile("mov %[" TOSTRING(var) "], " TOSTRING(var) "\n\t" : [var] "=r" (var))typedef uint32_t volatile *volatile vmemptr;#define VMEM(x) (*(vmemptr)(x))void backtrace(int fp){    printf("\n-------- bt ------\n");    if (fp == NULL) {        printf("fp is NULL\n");        return;    }    printf("bt fp: %p\n", fp);    printf("bt fp - 4: 0x%08x\n", (void *) ((*(int *)(fp - 4))));    backtrace((void *) ((*(int *)(fp - 4))));}int div(int a, int b){    int c = ++a;    int d = ++b;    int e = a * b / c;    printf("----- backtrace ----- %s\n", __func__);    int fp = 0;    // 2 methods of getting fp value    READ_REGISTER(fp);    printf("fp: %p\n", fp);    printf("frame div: %p\n", __builtin_frame_address (0));    backtrace(fp);    printf("\n");    return e;}int multi(int a, int b){    int c = ++a;    int d = ++b;    int e = a * b * div(c, d);    printf("frame multi: %p\n", __builtin_frame_address (0));    return e;}int minus(int a, int b){    int c = ++a;    int d = ++b;    int e = a - b - multi(c, d);    printf("frame minus: %p\n", __builtin_frame_address (0));    return e;}    int add(int a, int b){    int c = ++a;    int d = ++b;    int e = a + b + minus(c, d);    printf("frame add: %p\n", __builtin_frame_address (0));    return e;}int main(int argc, char **argv){    int a = 5;    int b = 4;    int c = add(a, b);    printf("frame main: %p\n", __builtin_frame_address (0));    // compare the address of variables with frame pointer    printf("func main:\t a: %p, b: %p, c: %p\n", &a, &b, &c);    printf("func main:\t argc: %p, argv: %p\n", &argc, argv);    printf("\n");    return 0;}

运行办法

arm-linux-gnueabi-gcc main.c -o main -w -gqemu-arm main

运行环境:Ubuntu18.04
运行后果

留神看后果中各个子函数的frame pointer,以及backtrace追溯时FP内容的变动

参考

  1. ARM FP寄存器及frame pointer介绍

未完待续

  1. arm hardfault寄存器及其callback解决
  2. linux中异样信号的捕捉