在信号处理程序中捕获C++未处理的异常并恢复应用程序

Catch a C++ unhandled exception in the signal handler and resume application

本文关键字:异常 恢复 应用程序 未处理 C++ 程序 信号处理      更新时间:2023-10-16

首先是一些上下文:

我正在测试一些C编写的服务的健壮性,运行在嵌入式Linux中。我所有的测试都是用C++编写的,并调用服务的 C API。

请注意,服务和应用程序在 2 个单独的进程中运行。应用程序在其上下文中打开代理,以通过 tcp/ip 与服务通信。

为了检查编码错误的回调无法破坏服务,我给它提供了一个函数,它只是抛出一个C++异常。正如预期的那样,在此 C 回调中引发异常会导致应用程序崩溃。

到目前为止,该服务似乎对此是健壮的:回调是从在客户端应用程序的上下文中运行的线程调用的。这意味着,只有客户端应用程序崩溃,服务保持活动状态。

当我说应用程序崩溃时,我的意思是它接收到SIGABRT信号,这是来自gdb的调用堆栈:

(gdb) info stack
0  0x4c22cb94 in raise () from /lib/libc.so.6
1  0x4c230670 in abort () from /lib/libc.so.6
2  0xb6e9e6c4 in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/libstdc++.so.6
3  0xb6e9c214 in ?? () from /usr/lib/libstdc++.so.6
4  0xb6e9c288 in std::terminate() () from /usr/lib/libstdc++.so.6
5  0xb6e9c5ac in __cxa_throw () from /usr/lib/libstdc++.so.6
6  0x0011824c in LocationTest::crashingLocCb (location=<optimized out>)
at ../../../TestLibrary/200-Location/src/locationtest.cpp:427
7  0x00144f64 in locationCb (location=<optimized out>)
at ../../PAL/src/tms/pallocationprovider.cpp:109
8  0xb6fbdb50 in locationcallback_thread (thread_info=0x1a82b8)
at ../lib_c/src-gen/location_proxy.c:273
9  0x4c33defc in ?? () from /lib/libpthread.so.0

我现在想做什么:

我想捕获信号SIGABRT并恢复测试应用程序,以进一步(检查服务是否某些内部状态是否损坏;检查它是否引起一些泄漏等。

拥有尽可能干净的东西的最佳方法是什么?

这是用信号处理程序完成的,但是其他正在运行的线程会受到伤害吗?那么将信号处理程序中捕获的信息发送回相关线程的最佳方法是什么?

(我对那些信号还没有太多经验(

这是我解决问题的方式(在伪代码中(。可能还有其他方法,我仍然睁开耳朵:

void malfunction()
{
terminate_handler previousTerminateHandler = set_terminate(callbackUnhandledExceptionTerminateHandler);
registerCallback(throwingCb);
launchTheTest();
waitForErrorEvent();
saveTheResult();
doSomeCleanUp();
set_terminate(previousTerminateHandler);
return;
}
static void callbackUnhandledExceptionTerminateHandler()
{
try{ throw; }
catch(const exception& e) { 
cout << e.what() << endl;
notifyTheErrorEvent();
}catch(...){
cout << "callbackUnhandledExceptionTerminateHandler : else ???" << endl;
abort();
}
//Log whatever information, you need such as pid, stack or whatever
cout << "processId = " << getpid() << endl;
//Here if you return from this handler, it will call abort();
//You could let it ends, if you already logged what you needed.
//You could also trap the thread, if this is something you already do elsewhere before stopping the application.

}

就我而言,我可以检查该服务是否能够适应客户应用程序的崩溃,并且仍然能够让其他(甚至相同!(打开另一个代理并执行更多工作。现在剩下要做的就是确保这里不会导致内存泄漏。(只是回答谁会质疑为什么这一切(。

在更一般的情况下,相同的处理可用于在未处理的异常时打印堆栈,而无需 gdb。