善用backtrace解决大问题【转】
转自:https://www.2cto.com/kf/201107/97270.html
一.用途:
主要用于程序异常退出时寻找错误原因
二.功能:
回溯堆栈,简单的说就是可以列出当前函数调用关系
三.原理:
. 通过对当前堆栈的分析,找到其上层函数在栈中的帧地址,再分析上层函数的堆栈,再找再上层的帧地址……一直找到最顶层为止,帧地址指的是一块:在栈上存放局部变量,上层返回地址,及寄存器值的空间。
. 由于不同处理器堆栈方式不同,此功能的具体实现是编译器的内建函数__buildin_frame_address及__buildin_return_address中,它涉及工具glibc和gcc, 如果编译器不支持此函数,也可自己实现此函数,举例中有arm上的实现
四.方法:
在程序中加入backtrace及相关函数调用
五.举例:
. 一般backtrace的实现
i. 程序
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#define PRINT_DEBUG
static void print_reason(int sig, siginfo_t * info, void *secret)
{
void *array[];
size_t size;
#ifdef PRINT_DEBUG
char **strings;
size_t i;
size = backtrace(array, );
strings = backtrace_symbols(array, size);
printf("Obtained %zd stack frames.\n", size);
for (i = ; i < size; i++)
printf("%s\n", strings[i]);
free(strings);
#else
int fd = open("err.log", O_CREAT | O_WRONLY);
size = backtrace(array, );
backtrace_symbols_fd(array, size, fd);
close(fd);
#endif
exit();
}
void die()
{
char *test1;
char *test2;
char *test3;
char *test4 = NULL;
strcpy(test4, "ab");
}
void test1()
{
die();
}
int main(int argc, char **argv)
{
struct sigaction myAction;
myAction.sa_sigaction = print_reason;
sigemptyset(&myAction.sa_mask);
myAction.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(SIGSEGV, &myAction, NULL);
sigaction(SIGUSR1, &myAction, NULL);
sigaction(SIGFPE, &myAction, NULL);
sigaction(SIGILL, &myAction, NULL);
sigaction(SIGBUS, &myAction, NULL);
sigaction(SIGABRT, &myAction, NULL);
sigaction(SIGSYS, &myAction, NULL);
test1();
}
ii. 编译参数
gcc main.c -o test -g -rdynamic
. 根据不同的处理器自已实现backtrace
i. arm的backtrace函数实现
static int backtrace_xy(void **BUFFER, int SIZE)
{
volatile int n = ;
volatile int *p;
volatile int *q;
volatile int ebp1;
volatile int eip1;
volatile int i = ;
p = &n;
ebp1 = p[];
eip1 = p[];
fprintf(stderr, "======================= backtrace_xy addr: 0x%0x, param1: 0x%0x, param2: 0x%0x\n",
backtrace_xy, &BUFFER, &SIZE);
fprintf(stderr, "n addr is 0x%0x\n", &n);
fprintf(stderr, "p addr is 0x%0x\n", &p);
for (i = ; i < SIZE; i++)
{
fprintf(stderr, "ebp1 is 0x%0x, eip1 is 0x%0x\n", ebp1, eip1);
BUFFER[i] = (void *)eip1;
p = (int*)ebp1;
q = p - ;
eip1 = q[];
ebp1 = q[];
if (ebp1 == || eip1 == )
break;
}
fprintf(stderr, "total level: %d\n", i);
return i;
}
六.举例2:
/*main.c*/
#include "sigsegv.h"
#include <string.h>
int die() {
char *err = NULL;
strcpy(err, "gonner");
return ;
}
int main() {
return die();
}
/*sigsegv.c*/
#define _GNU_SOURCE
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <ucontext.h>
#include <dlfcn.h>
#include <execinfo.h>
#define NO_CPP_DEMANGLE
#ifndef NO_CPP_DEMANGLE
#include <cxxabi.h>
#endif
#if defined(REG_RIP)
# define SIGSEGV_STACK_IA64
# define REGFORMAT "%016lx"
#elif defined(REG_EIP)
# define SIGSEGV_STACK_X86
# define REGFORMAT "%08x"
#else
# define SIGSEGV_STACK_GENERIC
# define REGFORMAT "%x"
#endif
static void signal_segv(int signum, siginfo_t* info, void*ptr) {
static const char *si_codes[] = {"", "SEGV_MAPERR", "SEGV_ACCERR"};
size_t i;
ucontext_t *ucontext = (ucontext_t*)ptr;
#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)
int f = ;
Dl_info dlinfo;
void **bp = ;
void *ip = ;
#else
void *bt[];
char **strings;
size_t sz;
#endif
fprintf(stderr, "Segmentation Fault!\n");
fprintf(stderr, "info->si_signo = %d\n", signum);
fprintf(stderr, "info->si_errno = %d\n", info->si_errno);
// fprintf(stderr, "info->si_code = %d (%s)\n", info->si_code, info->si_codes[si_code]);
fprintf(stderr, "info->si_addr = %p\n", info->si_addr);
for(i = ; i < NGREG; i++)
fprintf(stderr, "reg[%02d] = 0x" REGFORMAT "\n", i, ucontext->uc_mcontext.gregs[i]);
#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)
# if defined(SIGSEGV_STACK_IA64)
ip = (void*)ucontext->uc_mcontext.gregs[REG_RIP];
bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP];
# elif defined(SIGSEGV_STACK_X86)
ip = (void*)ucontext->uc_mcontext.gregs[REG_EIP];
bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP];
# endif
fprintf(stderr, "Stack trace:\n");
while(bp != & ip) {
if(!dladdr(ip, &dlinfo))
break;
const char *symname = dlinfo.dli_sname;
#ifndef NO_CPP_DEMANGLE
int status;
char *tmp = __cxa_demangle(symname, NULL, , &status);
if(status == !=& tmp)
symname = tmp;
#endif
fprintf(stderr, "% 2d: %p < %s+%u> (%s)\n",
++f,
ip,
symname,
(unsigned)(ip - dlinfo.dli_saddr),
dlinfo.dli_fname);
#ifndef NO_CPP_DEMANGLE
if(tmp)
free(tmp);
#endif
if(dlinfo.dli_sname != !strcmp(dlinfo.dli_sname, "main"))
break;
ip = bp[];
bp = (void**)bp[];
}
#else
fprintf(stderr, "Stack trace (non-dedicated):\n");
sz = backtrace(bt, );
strings = backtrace_symbols(bt, sz);
for(i = ; i < sz; ++i)
fprintf(stderr, "%s\n", strings[i]);
#endif
fprintf(stderr, "End of stack trace\n");
exit (-);
}
int setup_sigsegv() {
struct sigaction action;
memset(&action, , sizeof(action));
action.sa_sigaction = signal_segv;
action.sa_flags = SA_SIGINFO;
if(sigaction(SIGSEGV, &action, NULL) < ) {
perror("sigaction");
return ;
}
return ;
}
#ifndef SIGSEGV_NO_AUTO_INIT
static void __attribute((constructor)) init(void)
{
setup_sigsegv();
}
#endif
/*sigsegv.h*/
#ifndef __sigsegv_h__
#define __sigsegv_h__
#ifdef __cplusplus
extern "C" {
#endif
int setup_sigsegv();
#ifdef __cplusplus
}
#endif
#endif /* __sigsegv_h__ */
编译时需要加入-rdynamic -ldl –ggdb void
handle_signal_error(int rec_signal,siginfo_t* signal_info,void* context)
{
NE_Info* __attribute__ ((unused)) ne_info = NULL;
struct sigaction action;
FILE* file;
void* backtr[NUMBER_OF_BACKTRACE];
cpal_uns32 __attribute__ ((unused)) i = ;
cpal_uns32 backtr_size = ;
ucontext_t *u_context;
time_t seconds_time;
struct tm* time_struct;
cpal_si32 ret_t;
char filename[SIZE_OF_FILENAME]; if(g_handler_running)
return; g_handler_running = CPAL_TRUE;
ret_t = time(&seconds_time); if(ret_t != - )
{
time_struct = gmtime(&seconds_time); snprintf(filename,SIZE_OF_FILENAME,"%s%d%d%d-%d%d%d-%s",BACKTRACE_FILE_PATH,time_struct->tm_mon,time_struct->tm_mday,
(time_struct->tm_year-)+,time_struct->tm_hour,time_struct->tm_min,time_struct->tm_sec,BACKTRACE_FILE);
}
else
{
snprintf(filename,SIZE_OF_FILENAME,"%s",BACKTRACE_FILE);
}
file = fopen(filename,"w"); if(file == NULL)
{
return;
}
if(signal_info == NULL)
{
return;
} if(context == NULL)
{
return;
} u_context = (ucontext_t*)context;
/*Restore the default action for this signal and re-raise it, so that the default action occurs. */
action.sa_sigaction = SIG_DFL;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_RESTART; sigaction(rec_signal,&action,NULL); /* Print out the backtrace. */
backtr_size = backtrace(backtr,); /* The backtrace points to sigaction in libc, not to where the signal was actually raised.
This overwrites the sigaction with where the signal was sent, so we can resolve the sender. */
#if __WORDSIZE == 64
backtr[] = (void*)u_context->uc_mcontext.gregs[REG_RIP];
#else
backtr[] = (void*)u_context->uc_mcontext.gregs[REG_EIP];
#endif //__WORDSIZE backtrace_symbols_fd(backtr,backtr_size,fileno(file)); fprintf(file,"Backtrace is above.\nFatal signal %d received.\n",rec_signal);
#if __WORDSIZE == 64
fprintf(file,"Signal received at address %p from 0x%08x.\n",signal_info->si_addr,
u_context->uc_mcontext.gregs[REG_RIP]);
#else
fprintf(file,"Signal received at address %p from 0x%08x.\n",signal_info->si_addr,
u_context->uc_mcontext.gregs[REG_EIP]);
#endif //__WORDSIZE #if CPAL_LM_DEBUG
/* Print all NE_Infos */
for(; i < MAX_NO_OF_CONNS; i++)
{
ne_info = g_ne_hash_tab[i];
while(ne_info != NULL)
{
ne_info = ne_info->next_ne;
}
}
#endif fflush(file);
fclose(file);
sleep (); /* Sleep for 50 seconds */
g_handler_running = *_FALSE;
raise(rec_signal);
}
善用backtrace解决大问题【转】的更多相关文章
- 用它解决大问题啦,STRACE应用
脚本是沙沙,辉哥和我在去年解决一个PHP时弄出来的...强! 简单而实用. 抓到的TRC文件放在TRC目录下. 如果有异常的进程或输出,可以在里面详细的分析.. #!/bin/bash mkdir t ...
- 【小技巧解决大问题】使用 frp 突破阿里云主机无弹性公网 IP 不能用作 Web 服务器的限制
背景 今年 8 月份左右,打折价买了一个阿里云主机,比平常便宜了 2000 多块.买了之后,本想作为一个博客网站的,毕竟国内的服务器访问肯定快一些.满心欢喜的下单之后,却发现 http 服务,外网怎么 ...
- ruby环境sass编译中文出现Syntax error: Invalid GBK character错误解决方法
sass文件编译时候使用ruby环境,无论是界面化的koala工具还是命令行模式的都无法通过,真是令人烦恼. 容易出现中文注释时候无法编译通过,或者出现乱码,找了几天的解决方法终于解决了. 这个问题的 ...
- Sass编译时Invalid US-ASCII character解决办法
编译scss文件时,如果出现如下错误 Error: Invalid US-ASCII character "\xC2" on line 63 of src/assets/_scss ...
- html中通过移除空格的方法来解决浏览器上的留白间距该怎么理解?
今天在切图的时候,碰到一个兼容性的问题,很幸运最后通过张金鑫老师的文章解决了这个问题!但在阅读张老师文章的时候,我有个地方不明白,在网上查了下也没找到我想要的答案,后来自己想了半天好像是这么回事,现在 ...
- Java路径问题终于解决方式—可定位全部资源的相对路径寻址
1.在Java项目中,应该通过绝对路径訪问文件.下面为訪问的经常用法: 第一种方法:类名.class.getResource("/").getPath()+文件名称 另外一种方法: ...
- Web自动化---解决登录页面随机验证码问题
一.抛出问题 在日常的测试工作中,遇到了这样一个登录页面,如下图: 像我们之前做过UI自动化的同学就知道,自动输入账号和密码,这个简单,但是怎么样来识别验证码呢?验证码的形式有多种,有纯数字的,纯字母 ...
- php 基础代码大全(不断完善中)
下面是基础的PHP的代码,不断完善中~ //语法错误(syntax error)在语法分析阶段,源代码并未被执行,故不会有任何输出. /* [命名规则] */ 常量名 类常量建议全大写,单词间用下划线 ...
- 两千行PHP学习笔记
亲们,如约而至的PHP笔记来啦~绝对干货! 以下为我以前学PHP时做的笔记,时不时的也会添加一些基础知识点进去,有时还翻出来查查. MySQL笔记:一千行MySQL学习笔记http://www.cnb ...
随机推荐
- 第二阶段Sprint7
昨天:将“录制”及“保存”整合到一起,修复出现的Bug,使之能够正常运行. 今天:把视频录制整合到时间提醒里,实现视频提醒 遇到的问题:额,整进去直接就停止运行了..也没有报错..
- nginx安装(转发)
Nginx("engine x")是一款轻量级的HTTP和反向代理服务器.相比于Apache.lighttpd等,它具有占有内存少.并发能力强.稳定性高等优势.它最常见的用途就是提 ...
- 第十一周PSP&进度条
PSP 一.表格: D日期 C类型 C内容 S开始时间 E结束时间 I时间间隔 T净时间(mins) 预计花费时间(mins) 11月24号 站立会议 分配任务&设计final方案 1 ...
- 用友时空KSOA功能挖掘之zl_func函数
问题日常开发中,需要对界面进行控制,不符合条件时禁用某些功能菜单.例如[采购订单填制]界面,要实现供应商资质证书效期提醒功能,即近效期提醒,超效期禁止采购,如何实现呢? 分析使用KSOA新增加的zl_ ...
- EntityFramework 数据校验异常处理
public void Insert(PageHost entity) { try { db.pagehost.Add(entity); db.SaveChanges(); } catch (DbEn ...
- MT【200】一道自招的不等式
(2018武汉大学自招)设$x,y,z\ge0,xy+yz+zx=1$证明:$\dfrac{1}{x+y}+\dfrac{1}{y+z}+\dfrac{1}{z+x}\ge \dfrac{5}{2}$ ...
- 【转】PCB中3D相关功能详解
如果PCB Layout工程师能够在设计过程中,使用设计工具直观地看到自己设计板子的实际情况,将能够有效的帮助他们的工作.尤其现在PCB板的设计越来越复杂,密度越来越高,如果能够洞察多层板内部则可以帮 ...
- android编译时出现org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:compileDebugJavaWithJavac'.错误
android studio中使用terminal工具.在android studio最下面的底部菜单栏中有(如果没有那cmd中进入项目根目录后): 使用命令 gradlew compileDebu ...
- 如何修改Linux的TTL值
原文地址:http://www.linuxidc.com/Linux/2011-11/47701.htm 网络黑客如果用ping命令去探测 一个主机,根据TTL基数可以推测操作系统的类型.对于一个没 ...
- bzoj千题计划203:bzoj3994: [SDOI2015]约数个数和
http://www.lydsy.com/JudgeOnline/problem.php?id=3994 设d(x)为x的约数个数,给定N.M,求 用到的一个结论: 证明: 枚举n的约数i,枚举m的约 ...