转自: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 &lt; 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 &lt;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-&gt;si_signo = %d\n", signum);
fprintf(stderr, "info-&gt;si_errno = %d\n", info-&gt;si_errno);
// fprintf(stderr, "info-&gt;si_code = %d (%s)\n", info-&gt;si_code, info-&gt;si_codes[si_code]);
fprintf(stderr, "info-&gt;si_addr = %p\n", info-&gt;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-&gt;uc_mcontext.gregs[REG_RIP];
bp = (void**)ucontext-&gt;uc_mcontext.gregs[REG_RBP];
# elif defined(SIGSEGV_STACK_X86)
ip = (void*)ucontext-&gt;uc_mcontext.gregs[REG_EIP];
bp = (void**)ucontext-&gt;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) &lt; ) {
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-&gt;tm_mday,
(time_struct-&gt;tm_year-)+,time_struct-&gt;tm_hour,time_struct-&gt;tm_min,time_struct-&gt;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-&gt;uc_mcontext.gregs[REG_RIP];
#else
backtr[] = (void*)u_context-&gt;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-&gt;si_addr,
u_context-&gt;uc_mcontext.gregs[REG_RIP]);
#else
fprintf(file,"Signal received at address %p from 0x%08x.\n",signal_info-&gt;si_addr,
u_context-&gt;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解决大问题【转】的更多相关文章

  1. 用它解决大问题啦,STRACE应用

    脚本是沙沙,辉哥和我在去年解决一个PHP时弄出来的...强! 简单而实用. 抓到的TRC文件放在TRC目录下. 如果有异常的进程或输出,可以在里面详细的分析.. #!/bin/bash mkdir t ...

  2. 【小技巧解决大问题】使用 frp 突破阿里云主机无弹性公网 IP 不能用作 Web 服务器的限制

    背景 今年 8 月份左右,打折价买了一个阿里云主机,比平常便宜了 2000 多块.买了之后,本想作为一个博客网站的,毕竟国内的服务器访问肯定快一些.满心欢喜的下单之后,却发现 http 服务,外网怎么 ...

  3. ruby环境sass编译中文出现Syntax error: Invalid GBK character错误解决方法

    sass文件编译时候使用ruby环境,无论是界面化的koala工具还是命令行模式的都无法通过,真是令人烦恼. 容易出现中文注释时候无法编译通过,或者出现乱码,找了几天的解决方法终于解决了. 这个问题的 ...

  4. Sass编译时Invalid US-ASCII character解决办法

    编译scss文件时,如果出现如下错误 Error: Invalid US-ASCII character "\xC2" on line 63 of src/assets/_scss ...

  5. html中通过移除空格的方法来解决浏览器上的留白间距该怎么理解?

    今天在切图的时候,碰到一个兼容性的问题,很幸运最后通过张金鑫老师的文章解决了这个问题!但在阅读张老师文章的时候,我有个地方不明白,在网上查了下也没找到我想要的答案,后来自己想了半天好像是这么回事,现在 ...

  6. Java路径问题终于解决方式—可定位全部资源的相对路径寻址

    1.在Java项目中,应该通过绝对路径訪问文件.下面为訪问的经常用法: 第一种方法:类名.class.getResource("/").getPath()+文件名称 另外一种方法: ...

  7. Web自动化---解决登录页面随机验证码问题

    一.抛出问题 在日常的测试工作中,遇到了这样一个登录页面,如下图: 像我们之前做过UI自动化的同学就知道,自动输入账号和密码,这个简单,但是怎么样来识别验证码呢?验证码的形式有多种,有纯数字的,纯字母 ...

  8. php 基础代码大全(不断完善中)

    下面是基础的PHP的代码,不断完善中~ //语法错误(syntax error)在语法分析阶段,源代码并未被执行,故不会有任何输出. /* [命名规则] */ 常量名 类常量建议全大写,单词间用下划线 ...

  9. 两千行PHP学习笔记

    亲们,如约而至的PHP笔记来啦~绝对干货! 以下为我以前学PHP时做的笔记,时不时的也会添加一些基础知识点进去,有时还翻出来查查. MySQL笔记:一千行MySQL学习笔记http://www.cnb ...

随机推荐

  1. LeetCode 167. 两数之和 II - 输入有序数组

    题目: 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数. 函数应该返回这两个下标值index1 和 index2,其中 index1 必须小于 index2. 说明: 返回的 ...

  2. A-Softmax的总结及与L-Softmax的对比——SphereFace

    A-Softmax的总结及与L-Softmax的对比--SphereFace \(\quad\)[引言]SphereFace在MegaFace数据集上识别率在2017年排名第一,用的A-Softmax ...

  3. MongoDB ,cursor not found异常

    查询mongoDB集合数据更新,数据有400w多.我一次用cursor(游标)取1w,处理更新.程序在某段时间运行中遍历游标时发生异常! DBCursor cursor = tabColl.find( ...

  4. 部分机器进入bios 的 方法

  5. Vue中methods(方法)、computed(计算属性)、watch(侦听器)的区别

    1.computed和methods 共同点:computed能现实的methods也能实现: 不同点:computed是基于它的依赖进行缓存的.computed只有在它的相关依赖发生变化才会重新计算 ...

  6. Android studio Gradle编译错误: Unable to tunnel through proxy. Proxy returns "HTTP/1.1 400 Bad Reques

    两种处理方法: 1.修改distributionUrl链接 gradle-wrapper.properties文件 distributionUrl=https\://services.gradle.o ...

  7. ehcache、redis应用场景比较

    应用场景: ehcache是Hibernate中默认的CacheProvider,直接在jvm虚拟机中缓存,速度快,效率高:但是缓存共享麻烦,集群分布式应用不方便.  . 缓存数据有两级:内存和磁盘, ...

  8. Chrome Ajax 跨域设置

    一.前言 web 开发中 Ajax 是十分常见的技术,但是在前后端使用接口对接的调试过程中不可避免会碰到跨域问题.今天我给大家介绍一个十分简单有效的方法. 跨域经典错误 二.Chrome 跨域设置 首 ...

  9. 【大数据】Zookeeper学习笔记

    第1章 Zookeeper入门 1.1 概述 Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目. 1.2 特点 1.3 数据结构 1.4 应用场景 提供的服务包括:统 ...

  10. BZOJ4559 JLOI2016成绩比较(容斥原理+组合数学+斯特林数)

    容斥一发改为计算至少碾压k人的情况数量,这样对于每门课就可以分开考虑再相乘了.剩下的问题是给出某人的排名和分数的值域,求方案数.枚举出现了几种不同的分数,再枚举被给出的人的分数排第几,算一个类似斯特林 ...