如何在linux中打印程序堆栈信息

1 篇文章 0 订阅

​ 如何在linux中打印程序堆栈信息,用于调试。
有时候在写完代码之后需要自己手动测试功能,在linux环境中往往需要gdb调试打断点查看堆栈。往往公司的服务器一般是多人同时使用的。往往性能不是太强。gdb调试的时候载入的时候Reading symbols from的时候往往会卡那么一会儿虽然时间不长,但是很烦。我在这里写了简单实用打印堆栈的工具类。算是减少一点工作量。

​ 偶尔会遇到程序崩溃的情况,但是在一些线上部署环境的时候通常调试环境很复杂,通常由于特殊情况下core文件无法产生,或者无法拿到。这个类也可以添加到日记类中,在程序奔溃前用于打印出具体信息。

​ 首先在linux下有个3个函数可以用于配合打印堆栈信息。

//linux 下 使用man可以查看具体详细信息,这里信息大长了。我就截取一部分把。
/*backtrace()  returns a backtrace for the calling program, in the array pointed to by buffer.*/
int backtrace(void **buffer, int size);

/*Given the set of addresses returned by backtrace() in buffer, backtrace_symbols() translates the addresses into an array of strings that  describe  the  addresses  symbolically.
This array is malloc(3)ed by backtrace_symbols(), and must be freed by the caller.*/
char **backtrace_symbols(void *const *buffer, int size);

/*backtrace_symbols_fd()  takes  the  same buffer and size arguments as backtrace_symbols(), but instead of returning an array of strings to the caller, it writes the strings, one
       per line, to the file descriptor fd.  backtrace_symbols_fd() does not call malloc(3), and so can be employed in situations where the latter function might fail.*/
void backtrace_symbols_fd(void *const *buffer, int size, int fd);

以下就是我封装的一个backtrace类,可以称之为回溯信息类;

backtrace.h

/**
 * @File backtrace.h
 * @Brief 打印程序堆栈类
 * @Author 公众号:游戏服务器学习
 */

#ifndef __BACKTRACE_UTIL_H__
#define __BACKTRACE_UTIL_H__

#include <vector>
#include <memory>
#include <execinfo.h>

class BackTrace {
private:
    static std::string to_string( const std::vector<void *> bt );
    static std::vector<void *> backtrace(int framesToSkip = 0);
public:
    static std::string prinfBackTrace(int framesToSkip = 0);
};

#endif

backtrace.cpp

#include "backtrace.h"

#include <iostream>
#include <sstream>

std::vector<void*> BackTrace::backtrace(int framesToSkip) {
    std::vector<void *> result;
    result.resize(64);
    //返回回溯地址
    int count = ::backtrace(&result[0], 64);
    result.resize(count);
    framesToSkip = std::min(count, framesToSkip + 1);
    result.erase(result.begin(), result.begin() + framesToSkip);
    return result;
}

std::string BackTrace::to_string(const std::vector<void *> backtrace) {
    	//函数结束执行free 清空sybols
        std::shared_ptr<char *> symbols(backtrace_symbols(&backtrace[0],backtrace.size()), &free);
        std::ostringstream os;
        for (size_t i = 0; i < backtrace.size(); ++i) {
                if (i != 0)
                        os << std::endl;
                if (symbols) {
                    os << symbols.get()[i];
                }
                else
                    os << backtrace[i];
        }
        return os.str();
}

std::string BackTrace::prinfBackTrace(int framesToSkip) {
    return to_string(backtrace());
}

测试方法

main.cpp

#include <iostream>

#include "backtrace.h"

using namespace std;

void func() {
    std::cout<<BackTrace::prinfBackTrace()<<std::endl;
}

int test() {
    func();
    return 0;
}
int main(int argc, char* argv[]) {
    test();
    return 0;
}
                                                                 

执行结果可以很清澈的看到 堆栈信息啦。

ubuntu@server:~/server/GameServer$ g++ -std=c++11 -rdynamic main.cpp backtrace.cpp -o hello
ubuntu@server:~/server/GameServer$ ./hello 
./hello(_ZN9BackTrace14prinfBackTraceEi+0x21) [0x405a0b]
./hello(_Z4funcv+0x1a) [0x4055c7]
./hello(_Z4testv+0x9) [0x40561d]
./hello(main+0x14) [0x405638]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7f4682713f45]
./hello() [0x4054e9]

NOTE:

  1. backtrace的实现依赖于栈指针(fp寄存器),在g++编译过程中需要关闭编译器优化。
  2. backtrace_symbols的实现需要符号名称的支持,在g++编译过程中需要加入-rdynamic参数;
  3. 内联函数没有栈帧,它在编译过程中被展开在调用的位置;
  4. 尾调用优化(Tail-call Optimization)将复用当前函数栈,而不再生成新的函数栈,这将导致栈信息不能正确被获取。
  5. -rdynamic 要在加在前方

各位有需要可以直接直接拿走使用。如果有什么问题欢迎交流。

—The end—

感谢大家看到最后,文章持续更新,欢迎大家指出我文章的不足,也欢迎大家关注,收藏+分享
如果这个办法对您有用,或者您希望持续关注,也可以扫描下方二维码或者微信关注【游戏服务器学习】共同学习,不要让我一个人学习。

在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值