我想打印异常的回溯(该点处的状态是抛出的位置),但是在catch块中。
我也只是针对gcc,但是希望将不可移植的代码保持在最低限度(我知道我在示例中显示的堆栈打印代码是不可移植的)。
据我所知,到目前为止,在catch区块中,堆栈已经部分不合适,因此不再需要读取。 公平地说,它应该仍然存在(在内存中),如果没有创建局部变量并且没有完成方法调用,但我不确定我会以这种方式读取它是多么安全。
我还注意到,如果我注册一个terminate方法(通过std :: set_terminate)并且没有catch块,那么处理程序可以使用抛出未处理异常的位置的完整堆栈。我猜这是因为它完全解开了,但堆栈中的原始值没有被覆盖(并且很可能终止处理程序以某种方式具有独立的调用堆栈。)
我在gcc下测试了这个:
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <execinfo.h>
using namespace std;
void handler()
{
void *trace_elems[20];
int trace_elem_count(backtrace(trace_elems, 20));
char **stack_syms(backtrace_symbols(trace_elems, trace_elem_count));
for(int i=0 ; i != trace_elem_count; ++i)
{
cout << stack_syms[i] << "\n";
}
free(stack_syms);
exit(1);
}
void level3() { throw std::runtime_error("oops"); }
void level2() { level3(); }
void level1() { level2(); }
如果我像这样使用它,则会丢失异常回溯(只有main和handler在调用堆栈中):
void main()
{
try { level1(); }
catch(...) { handler();}
}
但如果我这样称呼它,那么抛出异常时的回溯就会被打印出来:
void main()
{
std::set_terminate(handler);
level1();
}
我的用例的一些上下文:我有两个正在运行的进程,一个生成请求,另一个将处理它们。执行这些请求有时会导致异常。在这一点上,回溯有助于弄清楚它失败的原因(what()经常没有足够的信息),但我不能让异常达到主要,因为两个进程都需要继续工作。我只需要打印回溯并返回一条消息,表示无法执行。
编辑:一个常见的解决方案就是建议解决这个问题,它有一个基本的异常类,可以在构造时捕获调用堆栈,并且可以在以后打印出来。这当然是一种可能的解决方案(如果找不到更好的解决方案,我可能不得不求助于此。)
我现在正在避免它,因为:
答案 0 :(得分:1)
您可以创建一个自定义异常类,在其构造函数中调用backtrace
并将缓冲区存储在其自身内。
捕获时,此类异常将具有打印跟踪所需的数据。实际上,处理程序很可能是异常的成员函数。请注意,跟踪将包括对异常构造函数的额外调用。
只要您只抛出自定义异常或从中派生的异常,这就有效。
答案 1 :(得分:0)
我能建议的最好是在投掷时收集堆栈跟踪。
这段代码可能需要一些改进,但它应该给你一个想法:
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <vector>
#include <execinfo.h>
using namespace std;
inline
auto make_stack_trace(int depth = 20) {
auto trace_elems = std::vector<void *>(depth);
auto trace_elem_count = backtrace(trace_elems.data(), depth);
char **stack_syms = backtrace_symbols(trace_elems.data(), trace_elem_count);
std::vector<std::string> symbols;
symbols.reserve(trace_elem_count);
for (int i = 0; i < trace_elem_count; ++i) {
symbols.emplace_back(stack_syms[i]);
}
free(stack_syms);
return symbols;
}
struct tracer {
tracer(const std::vector<std::string> &trace)
: trace_(trace) {}
friend std::ostream &operator<<(std::ostream &os, const tracer &t) {
const char *sep = "";
for (const auto &line : t.trace_) {
os << sep << line;
sep = "\n";
}
return os;
}
const std::vector<std::string> &trace_;
};
struct has_stack_trace {
has_stack_trace(std::vector<std::string> trace) : trace_(std::move(trace)) {}
auto trace() const {
return tracer(trace_);
}
virtual const char* what() const noexcept = 0;
std::vector<std::string> trace_;
};
template<class Exception>
struct with_stack_trace : has_stack_trace, Exception {
template<class...Args>
with_stack_trace(Args &&...args)
: has_stack_trace(make_stack_trace()), Exception(std::forward<Args>(args)...) {
}
virtual const char* what() const noexcept override
{
return Exception::what();
}
};
void level3() { throw with_stack_trace<std::runtime_error>("oops"); }
void level2() { level3(); }
void level1() { level2(); }
int main() {
try {
level1();
}
catch (has_stack_trace const &e) {
std::cout << e.what() << std::endl;
std::cout << e.trace() << std::endl;
}
catch (std::exception const& e) {
std::cout << e.what() << std::endl;
}
}
示例输出:
oops
0 parallel-find 0x000000010bad09e9 _Z16make_stack_tracei + 105
1 parallel-find 0x000000010bad08ec _ZN16with_stack_traceISt13runtime_errorEC2IJRA5_KcEEEDpOT_ + 44
2 parallel-find 0x000000010bacf46d _ZN16with_stack_traceISt13runtime_errorEC1IJRA5_KcEEEDpOT_ + 29
3 parallel-find 0x000000010bacf40a _Z6level3v + 42
4 parallel-find 0x000000010bacf4a9 _Z6level2v + 9
5 parallel-find 0x000000010bacf4b9 _Z6level1v + 9
6 parallel-find 0x000000010bacf4d7 main + 23
7 libdyld.dylib 0x00007fffa6346255 start + 1
8 ??? 0x0000000000000001 0x0 + 1
Process finished with exit code 0