我使用C++17,GCC,Qt及其集成的GDB调试器。
我的代码可以简化如下:
#include <iostream>
#include <iomanip>
// Example-implementation
#define assert(Condition) { if (!(Condition)) { std::cerr << "Assert failed, condition is false: " #Condition << std::endl; } }
#include <execinfo.h>
#include <signal.h>
#include <unistd.h>
void printStackTrace()
{
constexpr int requestedFrameCount = 20;
void* frames[requestedFrameCount];
auto actualFrameCount = backtrace(frames, requestedFrameCount);
std::cout << "Stack trace (" << actualFrameCount << " of " << requestedFrameCount << " requested frames):" << std::endl;
backtrace_symbols_fd(frames, actualFrameCount, STDOUT_FILENO);
std::cout << "End of stack trace." << std::endl;
}
void signalHandler(int signalNumber)
{
std::cout << "Signal " << signalNumber << " (" << sys_siglist[signalNumber] << ") happened!" << std::endl;
assert(signalNumber == SIGABRT);
printStackTrace();
}
__attribute_noinline__ void someFunction()
{
throw std::invalid_argument("Bad things happened");
}
__attribute_noinline__ void someFunctionInTheStandardLibraryThatICantChange()
{
try
{
someFunction();
}
catch (...)
{
throw;
}
}
__attribute_noinline__ int main()
{
signal(SIGABRT, signalHandler);
someFunctionInTheStandardLibraryThatICantChange();
return 0;
}
someFunctionInTheStandardLibraryThatICantChange
是这个东西的占位符:
template<bool _TrivialValueTypes>
struct __uninitialized_copy
{
template<typename _InputIterator, typename _ForwardIterator>
static _ForwardIterator
__uninit_copy(_InputIterator __first, _InputIterator __last,
_ForwardIterator __result)
{
_ForwardIterator __cur = __result;
__try
{
for (; __first != __last; ++__first, (void)++__cur)
std::_Construct(std::__addressof(*__cur), *__first);
return __cur;
}
__catch(...)
{
std::_Destroy(__result, __cur);
__throw_exception_again;
}
}
};
该程序的输出如下所示:
On standard output:
Signal 6 (Aborted) happened!
Stack trace (13 of 20 requested frames):
/foo/Test(_Z15printStackTracev+0x1c)[0xaaaab9886d30]
/foo/Test(_Z13signalHandleri+0xbc)[0xaaaab9886e94]
linux-vdso.so.1(__kernel_rt_sigreturn+0x0)[0xffff95f3a5c8]
/lib64/libc.so.6(gsignal+0xc8)[0xffff94e15330]
/lib64/libc.so.6(abort+0xfc)[0xffff94e02b54]
/lib64/libstdc++.so.6(_ZN9__gnu_cxx27__verbose_terminate_handlerEv+0x188)[0xffff950d9358]
/lib64/libstdc++.so.6(_ZN10__cxxabiv111__terminateEPFvvE+0xc)[0xffff950d70ac]
/lib64/libstdc++.so.6(_ZN10__cxxabiv112__unexpectedEPFvvE+0x0)[0xffff950d7100]
/lib64/libstdc++.so.6(__cxa_rethrow+0x60)[0xffff950d7428]
/foo/Test(_Z47someFunctionInTheStandardLibraryThatICantChangev+0x1c)[0xaaaab9886f10]
/foo/Test(main+0x1c)[0xaaaab9886f48]
/lib64/libc.so.6(__libc_start_main+0xe4)[0xffff94e02fac]
/foo/Test(+0x2774)[0xaaaab9886774]
End of stack trace.
On standard error:
terminate called after throwing an instance of 'std::invalid_argument'
what(): Bad things happened
注意堆栈跟踪是如何直接从someFunctionInTheStandardLibraryThatICantChange
到rethrow
的。someFunction
没有内联(如果您不信任我,请从someFunction
打电话给printStackTrace
)。
我无法更改库函数,但我需要知道异常最初抛出的位置。我怎么才能得到这些信息?
一种可能的方法是使用调试器并设置一个“抛出C++异常时中断”断点。但是这有一些明显的缺点,只有在调试时,它才能工作,它是程序外部的,只有当你不抛出一堆你不关心的异常时,它才是真正可行的。
发布于 2022-09-28 15:33:58
@n.1.8e9-我的股份在哪里。评论中的建议到头来起作用了。当您在C++中抛出异常时,在幕后调用函数__cxa_throw
。您可以替换该函数,查看堆栈跟踪,然后调用替换函数。
以下是一个简单的概念证明:
#include <dlfcn.h>
#include <cxxabi.h>
typedef void (*ThrowFunction)(void*, void*, void(*)(void*)) __attribute__ ((__noreturn__));
ThrowFunction oldThrowFunction;
namespace __cxxabiv1
{
extern "C" void __cxa_throw(void* thrownException, std::type_info* thrownTypeInfo, void (*destructor)(void *))
{
if (oldThrowFunction == nullptr)
{
oldThrowFunction = (ThrowFunction)dlsym(RTLD_NEXT, "__cxa_throw");
}
// At this point, you can get the current stack trace and do something with it (e.g. print it, like follows).
// You can also set a break point here to have the debugger stop while the stack trace is still useful.
std::cout << "About to throw an exception of type " << thrownTypeInfo->name() << "! Current stack trace is as follows:" << std::endl;
printStackTrace();
std::cout << std::endl;
oldThrowFunction(thrownException, thrownTypeInfo, destructor);
}
}
结合问题中的例子,输出如下:
About to throw an exception of type St16invalid_argument! Current stack trace is as follows:
Stack trace (7 of 20 requested frames):
/foo/Test(_Z15printStackTracev+0x3c)[0x55570996b385]
/foo/Test(__cxa_throw+0xa1)[0x55570996b653]
/foo/Test(_Z12someFunctionv+0x43)[0x55570996b555]
/foo/Test(_Z47someFunctionInTheStandardLibraryThatICantChangev+0x12)[0x55570996b581]
/foo/Test(main+0x21)[0x55570996b6ac]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x7fb71560f0b3]
/foo/Test(_start+0x2e)[0x55570996b28e]
End of stack trace.
Signal 6 (Aborted) happened!
Stack trace (13 of 20 requested frames):
/foo/Test(_Z15printStackTracev+0x3c)[0x55570996b385]
/foo/Test(_Z13signalHandleri+0xbd)[0x55570996b50f]
/lib/x86_64-linux-gnu/libc.so.6(+0x46210)[0x7fb71562e210]
/lib/x86_64-linux-gnu/libc.so.6(gsignal+0xcb)[0x7fb71562e18b]
/lib/x86_64-linux-gnu/libc.so.6(abort+0x12b)[0x7fb71560d859]
/lib/x86_64-linux-gnu/libstdc++.so.6(+0x9e911)[0x7fb715893911]
/lib/x86_64-linux-gnu/libstdc++.so.6(+0xaa38c)[0x7fb71589f38c]
/lib/x86_64-linux-gnu/libstdc++.so.6(+0xaa3f7)[0x7fb71589f3f7]
/lib/x86_64-linux-gnu/libstdc++.so.6(__cxa_rethrow+0x4d)[0x7fb71589f6fd]
/foo/Test(_Z47someFunctionInTheStandardLibraryThatICantChangev+0x25)[0x55570996b594]
/foo/Test(main+0x21)[0x55570996b6ac]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x7fb71560f0b3]
/foo/Test(_start+0x2e)[0x55570996b28e]
End of stack trace.
terminate called after throwing an instance of 'std::invalid_argument'
what(): Bad things happened
这可以通过以下方式加以改进:
通过检查type_info
.
void*
中提取出来(而不是控制台)。请注意,如果由于进程内存不足而引发异常,则这可能是不可取的。但是对于99.9%的用例来说,这是一个选项。,
抓取和打印。
如果我有足够的时间和耐心,我会加上这个答案,但我可能会在此之前死于挫折。
https://stackoverflow.com/questions/73788875
复制相似问题