有时候在写完代码之后需要自己手动测试功能,在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:
- backtrace的实现依赖于栈指针(fp寄存器),在g++编译过程中需要关闭编译器优化。
- backtrace_symbols的实现需要符号名称的支持,在g++编译过程中需要加入-rdynamic参数;
- 内联函数没有栈帧,它在编译过程中被展开在调用的位置;
- 尾调用优化(Tail-call Optimization)将复用当前函数栈,而不再生成新的函数栈,这将导致栈信息不能正确被获取。
- -rdynamic 要在加在前方
各位有需要可以直接直接拿走使用。如果有什么问题欢迎交流。
如果您想持续关注,可以关注公众号:游戏服务器学习 一起探讨。获取更多资料。