善用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 ...
随机推荐
- UIO,大页内存,CPU亲和性,NUMA机制等
Linux环境下的UIO(Userspace I/O) UIO 用户空间下驱动程序的支持机制.DPDK使用UIO机制使网卡驱动程序运行在用户态,并采用轮询和零拷贝方式从网卡收取报文,提高收发报文的性能 ...
- ElasticSearch 2 (32) - 信息聚合系列之范围限定
ElasticSearch 2 (32) - 信息聚合系列之范围限定 摘要 到目前为止我们看到的所有聚合的例子都省略了搜索请求,完整的请求就是聚合本身. 聚合与搜索请求同时执行,但是我们需要理解一个新 ...
- Git从零开始(一)
一.首先windows安装git客户端 官网下载地址:https://git-for-windows.github.io/,这里下载会很慢,我试了好几次都失败了. 百度网盘资源: https://pa ...
- information_schema系列十一
1: INNODB_CMP 和INNODB_CMP_RESET 这两个表存储的是关于压缩INNODB信息表的时候的相关信息, Column name Description PAGE_SIZE Com ...
- Java 策略模式(Strategy)
创建一个能够根据所传递的参数对象的不同而具有不同行为的方法 要执行的算法固定不变,封装到一个类(Context)中 策略就是传递进去的参数对象,它包含执行代码 策略接口 /** * 策略接口 */ p ...
- sqlserver2017安装及连接过程中发现的问题
1.SSMS安装报错,如下图 根据搜索资料发现是防火墙的问题,关闭防火墙就行了. 2.连接用户时报错 这个是因为远程连接相关问题. 首先打开服务器远程连接: 其次点击: SqlServer配置管理器- ...
- C#小技巧
1.将字符串转换成大写ToUpper string a="zxc"; a.ToUpper() 输出结果ZXC; a.ToUpper().Contains("Z" ...
- BZOJ4589 Hard Nim(博弈+FWT)
即使n个数的异或为0.如果只有两堆,将质数筛出来设为1,做一个异或卷积即可.显然这个东西满足结合律,多堆时直接快速幂.可以在点值表示下进行. #include<iostream> #inc ...
- MySQL中使用Like模糊查询太慢
问题:明明建立了索引,为何Like模糊查询速度还是特别慢? Like是否使用索引? 1.like %keyword 索引失效,使用全表扫描.但可以通过翻转函数+like前模糊查询+建立翻转函数索 ...
- SSH & Git
SSH基本用法 SSH服务详解 work with git branch some tips for git setup and git config git and github ssh servi ...