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
- SIGSEGV时自动打印栈帧(2014.02.28 更新)
- SIGSEGV异常时打印函数调用链
- 捕捉信号SIGSEGV并回溯栈帧
- 捕捉信号SIGSEGV并回溯栈帧
- GDB 栈指针 SIGSEGV
- SIGSEGV
- 捕捉信号SIGSEGV并回溯栈帧backtrace
- 程序退出自动打印函数栈
- Web页面载入时自动打印
- 页面自动打印
- IE打印自动分页
- 字符串打印自动换行
- 输入行列自动打印
- Qt+OpenCV在不同ubuntu系统移植时SIGSEGV问题解决
- SIGSEGV错误
- catch sigsegv
- SIGSEGV 11
- SIGBUS SIGSEGV
- JSP note
- AJAX Note
- grep之字符串搜索算法Boyer-Moore由浅入深(比KMP快3-5倍)
- Android广告平台介绍
- Struts1.x note
- SIGSEGV时自动打印栈帧(2014.02.28 更新)
- Unique Paths II
- 弹出无边框页面,(将页面以div的形式弹出)
- Struts2.x note
- Spring note
- EJB note
- Android SDCard Mount 流程分析(一)
- WebService note
- Android SDCard Mount 流程分析(二)