SIGSEGV时自动打印栈帧(2014.02.28 更新)

来源:互联网 发布:网站域名是什么 编辑:程序博客网 时间:2024/06/02 02:12
有些嵌入式设备内存,flash 小,根本不能跑gdb, 连带调试符号的软件flash都装不下。这时调试死机问题就比较麻烦,下面我介绍一种方法:

让程序死时自动把栈帧打印出来。

 

/*backtrace.c -- print back trace on linux. * * test: *   gcc -g -D LINUX_X86 backtrace.c -o bt *   ./bt | gawk '{system("addr2line -f -s -e bt "  $4);}' * * author: ludi 2013.12 * */#include <stdio.h>#include <string.h>#include <inttypes.h>static uintptr_t get_max_stack(void){    static const char file_name[] = "/proc/self/maps";    char line[1024];    int line_number;    FILE *f;    f = fopen(file_name, "r");    if (f == NULL) {        printf("opening %s failed", file_name);        return -1;    }    for (line_number = 1; fgets(line, sizeof line, f); line_number++) {        if (strstr(line, "[stack]")) {            uintptr_t end;            if (sscanf(line, "%*x-%"SCNxPTR, &end) != 1) {                printf("%s:%d: parse error", file_name, line_number);                continue;            }            fclose(f);            return end;        }    }    fclose(f);    printf("%s: no stack found", file_name);    return -1;}#if defined(LINUX_ARM)/*arm stack address from high to low: * PC LR SP FP params locals PC LR SP FP params locals * ^                                  | * |__________________________________| * */int backtrace( int size){uintptr_t fp, lr;uintptr_t low = (uintptr_t)&fp, high = get_max_stack();int i;__asm__ __volatile__(" \              mov %0, r11"              :"=r" (fp)              :       );for(i = 0; i < size; ++i){if(!(low <= fp  && fp < get_max_stack() ))break;lr = *((uintptr_t*)fp - 1);fp = *((uintptr_t*)fp - 3);printf("backtrace #%02d at 0x%x\n", i, lr);}return i;}#endif#if defined(LINUX_X86)/* x86 stack address from hight to low:* params eip ebp locals*            ebp    esp* */int backtrace(int size){       uintptr_t ebp, eip;   int i;   uintptr_t low = (uintptr_t)&ebp, high = get_max_stack();       __asm__ __volatile__(" \              movl %%ebp, %0"              :"=g" (ebp)              :              :"memory"       );       for(i = 0; i < size; i++){  if(!(low <= ebp  && ebp < get_max_stack() ))break;              eip = (uintptr_t)((uintptr_t*)ebp + 1);              eip = *(uintptr_t*)eip;              ebp = *(uintptr_t*)ebp;  printf("backtrace #%02d at 0x%x\n", i, eip);       }       return i;}#endiffunc3(){backtrace(100);}func2(){func3();}void func1(){func2();}int main(int argc, char*argv[]){func1();return 0;}


 

我实测

像这样的可以正确打印:

void func_segv()
{
    * ((volatile int *) 0x0) = 0xDEAD;
}

 

但是像一些复杂的死机问题, 连gdb打印不出来。(gdb 也不能准确定位,有时运气好只给出有关的文件)

 

注记:

实际运用中,放到flash上的bt是strip过的,没有strip 的bt 还要保留给addr2line用。

 

2014.02.28 更新:

如果没有带调试符号的, 应该还有map 文件。$S 给驱动开发人员就是提供调试符号文件的。

但是map文件中地址是函数定义地址,栈帧打出来的是调用点地址,如何对应回去呢? 其实注意观察map 文件发现地址都是递增的,并且栈帧地址位于两个高定义地址中间,

我们就可以使用这两点就够了,详细的见下面:

/*search_map_file.c -- search name of a backtrace address in a map file.how to generate a map file? gcc -Wl,-Map,a.def a.c */#include <stdio.h>#include <string.h>char*  search_map_file(FILE *mapfile, char *key){char buf[1024*2];static char field[3][512], last[3][512];char key2[16];int lineno = 0, is_text_part = 0;sprintf(key2, "0x%08x", strtol(key, NULL, 16));while(fgets(buf, sizeof(buf)-1, mapfile)){lineno++;if((2 == sscanf(buf, "%s%s%s", field[0], field[1], field[2]))&&(field[0][0] == '0' && field[0][1] == 'x')&&(is_text_part)){if((strcmp(key2, field[0]) < 0)&&(strcmp(key2, last[0]) > 0)){//printf("key2 %s f0 %s lineno %d buf %s\n", key2, field[0], lineno, buf);return last[1];}memcpy(last, field, sizeof(last));}if(!is_text_part){is_text_part = !strcmp(field[0], ".text") ;}memset(buf, 0, sizeof(buf));}return NULL;}int main(int ac, char **av){FILE *fp = NULL;if(ac < 3){return printf("usage: ./search_map_file a.def hex_addr\n");}fp = fopen(av[1], "r");if(!fp){return printf("can't open %s\n", av[1]);}printf("%s %s\n", av[2], search_map_file(fp, av[2]) );fclose(fp);return 0;}


另外,打印栈帧还有一个应用,就是检查谁引起内存泄露:

static int alloc_en(void *addr, unsigned int size);void* malloc_wrapper(unsigned int size){void *ptr = (void*)malloc(size); alloc_en(ptr, size); return ptr;}static int alloc_en(void *addr, unsigned int size){ for(i = 0; i < MAX_CALL; ++i){  if(0 == s_array[i].addr)break; } if(i >= MAX_CALL){  printf("no free slot");  return -1; } s_array[i].addr = (U32)addr; s_array[i].size = size; s_array[i].caller = backtrace(3);}


 

 

0 0
原创粉丝点击