Linux C/C++ 对于SIGBUS、SIGSEGV等崩溃异常捕获实现
前段时间我们发表过类似得 Try-Catch 实现,但是该实现本身是存在局限性的,因为它们不能无视 SIGSEGV,SIGSYS,SIGTRAP等崩溃信号,对于某些3RD代码根本无法处理(这些代码没有源代码只有库,总不可能去静态反编译在改汇编)或者是短时间无法解决得内存BUG、例如毫无问题得代码在ARM平台会发生结构内存对齐问题(SIGBUS)导致强行崩溃(仅限于恶意崩溃,即运行相同代码+结构上万次以后才会发生SIGBUS提示内存对齐故障)别认为不可能,这在ARM上面存在潜在发生得可能性,解决办法查汇编查代码对所有的结构进行对齐检查,等等。
本文代码对于下文链接得扩展:
C/C++ 11 try-catch-finally 扩展(函数式)_liulilittle的博客-CSDN博客_c++ finally
假设...我们希望:类似以下的代码不要导致程序停止工作那么应该怎么处理?
int64_t llPtr = 0;
char* byPtr = (char*)llPtr;
*byPtr = rand() & 0xff;
... ...
printf("%s\n", "OK!");
我们知道上述代码务必会遭遇 “SIGSEGV” 导致崩溃并且产生 dump 文件/信息(若设置)那么我们不希望这里出现故障应该怎么做?
SIGSEGV 是可以被认为捕获得,有一个偷懒得宏定义叫做 SIG_IGN(忽略信号)但要注意,它的确忽略信号,确保程序不会因为崩溃信号停止工作,但这是有限度的,类似 SIGSEGV、SIGBUS 等信号是无效的,不要考虑这种没有意义得东西,就算要放弃某个信号也不应该是这么粗放式得操作,SIG_IGN 这类型仅适用于程序主动得 raise 得崩溃信号,比如:SIG_PIPE。
OK,那么可以适用最新改进得:“Try-Catch-Finally” 进行处理,但发生异常时可能潜在导致内存泄漏问题。
Try([] {
int64_t llPtr = 0;
char* byPtr = (char*)llPtr;
*byPtr = rand() & 0xff;
}).Catch<std::exception>([] (std::exception& e) {
printf("%s\n", "Error!");
});
printf("%s\n", "OK!");
以上代码是可以正确执行到 OK!部分,并且不会发生内存泄漏问题,但一旦我们在 lambda 内操作关于内存相关的东西就有可能发生异常,比如在函数内复制了一个 shared_ptr 并且在析构以前出现异常,那么 shared_ptr 得引用就无法在减少导致内存资源泄露,那么类似得好办法是不要使用 shared_ptr,或者在可以确定不会发生异常得情况下使用,如果你真不确定还非得头铁用得话,有个办法:weak_ptr 可能很适合您。
潜在发生异常得 lambda 函数是可以捕获类似 shared_ptr 之类的东西,它们并不会引起不能调用捕获 shared_ptr 得析构函数,所以是是线上内存安全的,但是 lambda 内部操作不是内存安全的,这个必须要说清楚!
Try.h
#pragma once #include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>
#include <memory>
#include <exception>
#include <mutex>
#include <unordered_map>#ifndef MAX_STACK_FRAME
#define MAX_STACK_FRAME 10
#endifnamespace My {class Try {private:typedef sigjmp_buf Context;struct Stack {private:mutable std::shared_ptr<Context> rbp;mutable Context* rsp;public:Stack();void New() const;void Release() const;inline Context* Ptr() const {return this->rbp.get();}inline Context* Push() const {Context* max = this->rbp.get() + MAX_STACK_FRAME;if (this->rsp >= max) {return NULL;}return this->rsp++;}inline Context* Pop() const {if (this->rsp <= this->rbp.get()) {return NULL;}return --this->rsp;}inline Context* Peek() const {Context* max = this->rbp.get() + MAX_STACK_FRAME;if (this->rsp > max || this->rsp <= this->rbp.get()) {return NULL;}return this->rsp - 1;}};typedef std::unordered_map<int, Stack> StackTable;friend class Hosting;friend class __TRY_SEH_SIGNAL__;public:inline Try(const std::function<void()>& block): ___try(block) {if (!block) {throw std::runtime_error("The block part of the try is not allowed to be Null References");}}inline ~Try() {___try = NULL;___catch = NULL;___finally = NULL;}public:template<typename TException>inline Try& Catch() const {return Catch<TException>([](TException& e) {});}template<typename TException>inline Try& Catch(const std::function<void(TException& e)>& block) const {return Catch<TException>(block, NULL);}template<typename TException>inline Try& Catch(const std::function<void(TException& e)>& block, const std::function<bool(TException& e)>& when) const {if (!block) {throw std::runtime_error("The block part of the try-catch is not allowed to be Null References");}std::function<bool(std::exception*)> previous = ___catch;___catch = [block, previous, when](std::exception* e) {if (previous) {if (previous(e)) {return true;}}TException* exception = dynamic_cast<TException*>(e);if (!exception) {return false;}if (when) {if (!when(*exception)) {return false;}}if (block) {block(*exception);}return true;};return const_cast<Try&>(*this);}inline Try& Finally(const std::function<void()>& block) const {___finally = block;return const_cast<Try&>(*this);}inline void Invoke() const {Context* stack_context = Try::PushContext();std::exception* stack_exception = NULL;try {if (!stack_context) {___try();}else {if (sigsetjmp(*stack_context, 1) == 0) {___try();}else {stack_exception = &Try::___exception;if (___catch) {if (___catch(stack_exception)) {stack_exception = NULL;}}}}}catch (std::exception& throwable) {stack_exception = &throwable;if (___catch) {if (___catch(stack_exception)) {stack_exception = NULL;}}}catch (...) {stack_exception = &Try::___exception;if (___catch) {if (___catch(stack_exception)) {stack_exception = NULL;}}}if (___finally) {___finally();}if (stack_context) {Try::PopContext();}if (stack_exception) {throw *stack_exception;}}#ifndef ANDROIDstatic void PrintStackTrace();#endifprivate:static Context* PushContext();static Context* PopContext();static Context* PeekContext();static void AllocStack(bool important);static void ReleaseStack();private:mutable std::function<void()> ___try;mutable std::function<bool(std::exception*)> ___catch;mutable std::function<void()> ___finally;static std::runtime_error ___exception;static std::mutex ___syncobj;static StackTable ___stack;static struct sigaction ___osas[15]; };
}
Try.cpp
#include <stdio.h>
#include <signal.h>
#include <limits.h>
#include <sys/file.h>
#ifndef ANDROID
#include <unwind.h>
#include <execinfo.h>
#include <sys/resource.h>
#endif
#include <My/Try.h>
#include <My/Environment.h>#ifndef ANDROID
#define MAX_BACKTRACE_SIZE 100
#endifnamespace My {class __TRY_SEH_SIGNAL__ {public:inline __TRY_SEH_SIGNAL__() {memset(Try::___osas, 0, sizeof(Try::___osas));/*retrieve old and set new handlers*//*restore prevouis signal actions*/#ifdef ANDROIDWatch(35, 0); // FDSCAN(SI_QUEUE)#endifWatch(SIGBUS, 1);Watch(SIGPIPE, 2);Watch(SIGFPE, 3);Watch(SIGSEGV, 4);Watch(SIGILL, 5);Watch(SIGTRAP, 6);Watch(SIGSYS, 7);Watch(SIGQUIT, 8);Watch(SIGIOT, 9);Watch(SIGUSR1, 10);Watch(SIGUSR2, 11);Watch(SIGXCPU, 12);Watch(SIGXFSZ, 13);Watch(SIGSTKFLT, 14);}inline ~__TRY_SEH_SIGNAL__() {struct sigaction* osas = Try::___osas;/*restore prevouis signal actions*/#ifdef ANDROIDUnwatch(35, 0); // FDSCAN(SI_QUEUE)#endifUnwatch(SIGBUS, 1);Unwatch(SIGPIPE, 2);Unwatch(SIGFPE, 3);Unwatch(SIGSEGV, 4);Unwatch(SIGILL, 5);Unwatch(SIGTRAP, 6);Unwatch(SIGSYS, 7);Unwatch(SIGQUIT, 8);Unwatch(SIGIOT, 9);Unwatch(SIGUSR1, 10);Unwatch(SIGUSR2, 11);Unwatch(SIGXCPU, 12);Unwatch(SIGXFSZ, 13);Unwatch(SIGSTKFLT, 14);}public:inline int Watch(int signo, int index) {struct sigaction sa;struct sigaction* osas = Try::___osas;memset(&sa, 0, sizeof(sa));/*init new handler struct*/ sa.sa_handler = [] (int signo) {Try::Context* context = Try::PeekContext();if (context) {siglongjmp(*context, 1);} else {#ifndef ANDROIDTry::PrintStackTrace();#endifsignal(signo, SIG_DFL);raise(signo);}};sigemptyset(&sa.sa_mask); sa.sa_flags = 0;return sigaction(signo, &sa, &osas[index]);}inline int Unwatch(int signo, int index) {struct sigaction* osas = Try::___osas;return sigaction(signo, &osas[index], NULL);}} g__TRY_SEH_SIGNAL__;std::unordered_map<int, Try::Stack> Try::___stack;std::mutex Try::___syncobj;std::runtime_error Try::___exception("Received an error signal thrown by the system or application during code execution.");struct sigaction Try::___osas[15];Try::Stack::Stack() : rbp(NULL), rsp(NULL) {this->New();}void Try::Stack::New() const {this->rbp = make_shared_alloc<Context>(MAX_STACK_FRAME);this->rsp = this->rbp.get();}void Try::Stack::Release() const {this->rbp = NULL;this->rsp = NULL;}Try::Context* Try::PushContext() {std::lock_guard<std::mutex> scope(Try::___syncobj);return Try::___stack[Hosting::GetCurrentThreadId()].Push();}Try::Context* Try::PopContext() {std::lock_guard<std::mutex> scope(Try::___syncobj);return Try::___stack[Hosting::GetCurrentThreadId()].Pop();}Try::Context* Try::PeekContext() {std::lock_guard<std::mutex> scope(Try::___syncobj);return Try::___stack[Hosting::GetCurrentThreadId()].Peek();}void Try::AllocStack(bool important) {std::lock_guard<std::mutex> scope(Try::___syncobj);int id = Hosting::GetCurrentThreadId();StackTable::iterator kv = Try::___stack.find(id);if (kv != Try::___stack.end()) {const Stack& stack = kv->second;if (important || !stack.Ptr()) {stack.New();}}else {Stack stack;Try::___stack.insert(std::make_pair(id, stack));}}void Try::ReleaseStack() {std::lock_guard<std::mutex> scope(Try::___syncobj);StackTable::iterator kv = Try::___stack.find(Hosting::GetCurrentThreadId());if (kv != Try::___stack.end()) {const Stack& stack = kv->second;stack.Release();Try::___stack.erase(kv);}}#ifndef ANDROIDvoid Try::PrintStackTrace() {char maps_string[1000] = "0";sprintf(maps_string, "cat /proc/%d/maps", getpid());system(maps_string); {printf("\nStack Addresses:\n");_Unwind_Backtrace([](struct _Unwind_Context* context, void* arg) -> _Unwind_Reason_Code {uintptr_t pc = _Unwind_GetIP(context);if (pc) {printf(" at pc:0x%llx\n", (unsigned long long)pc);}return _URC_NO_REASON;}, 0); // _Unwind_Reason_Code rc, rc == _URC_END_OF_STACK ? 0 : -1;}void* stack_frames[MAX_BACKTRACE_SIZE];int nptrs = backtrace(stack_frames, MAX_BACKTRACE_SIZE);{printf("\nStack Frame: %d\n", nptrs);}char** frame_strings = backtrace_symbols(stack_frames, nptrs);if (NULL != frame_strings) {for (int i = 0; i < nptrs; i++) {printf(" at [%02d] %s\n", i, frame_strings[i]);}free(frame_strings);}}#endif
}
Linux C/C++ 对于SIGBUS、SIGSEGV等崩溃异常捕获实现相关推荐
- iOS Crash崩溃异常教你查看和分析
要学会看crash崩溃和报告 一个应用程序并不总会一直运行的很好,它总会有出现crash崩溃的情况.如果在应用程序中接入了一些第三方的crash收集工具或者自建crash收集报告平台的话将会很好的帮助 ...
- Android中处理崩溃异常
2019独角兽企业重金招聘Python工程师标准>>> 大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象 ...
- Android application捕获崩溃异常
Java代码 个人笔记: 通用 application 1.收集所有 avtivity 用于彻底退出应用 2.捕获崩溃异常,保存错误日志,并重启应用 public class HKBaseApplic ...
- Android中处理崩溃异常 (转)
大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了 ...
- Linux系统 设置 cockpit 自动开机启动时异常问题处理(设置systemctl enable cockpit 异常【解决办法】)
Linux系统 设置 cockpit 自动开机启动时异常问题处理 问题系统: Centos7 Fedora29 RedHat7 systemctl enable cockpit 时,会出现如下错误: ...
- 【知识必备】如何优雅的退出应用和处理崩溃异常并重启
[知识必备]如何优雅的退出应用和处理崩溃异常并重启 参考文章: (1)[知识必备]如何优雅的退出应用和处理崩溃异常并重启 (2)https://www.cnblogs.com/liushilin/p/ ...
- android 崩溃日志捕获,安卓Java崩溃的捕获和日志记录
Android的两种崩溃 Android 崩溃分为 Java 崩溃和 Native崩溃两种. Java崩溃的知识点 Java崩溃.png Java崩溃的原因 简单来说,Java崩溃就是在Java代码中 ...
- java语言中application异常退出和线程异常崩溃的捕获方法,并且在捕获的钩子方法中进行异常处理
java语言中application异常退出和线程异常崩溃的捕获方法,并且在捕获的钩子方法中进行异常处理 参考文章: (1)java语言中application异常退出和线程异常崩溃的捕获方法,并且在 ...
- 麒麟系统开发笔记(十):在国产麒麟系统上使用gdb定位崩溃异常方法流程以及测试Demo
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/129858821 红胖子网络科技博文大全:开发技术集合( ...
最新文章
- CSRF verification failed. Request aborted. 表单提交方法为POST时的报错
- python的难点_初学python的操作难点总结(新手必看篇)
- arthas classclassloader相关命令之二:jad、mc、redifine实现代码热更新(热替换)
- 信息学奥赛一本通 1173:阶乘和 | OpenJudge NOI 1.6 15 | 洛谷 P1009 [NOIP1998 普及组] 阶乘之和
- 最详细的Mask R-CNN论文笔记
- javascript实现窗口随着鼠标移动且移动路径重现
- 计算机一级ftp协议传输,文件传输协议(FTP)必将消亡
- 实现RPC就是这么简单
- 【数据结构和算法笔记】AOE网和关键路径
- 在robotstudio中使用smart组件喷绘七彩圆环
- sublime text3 python找不到文件路径_sublime text3 系统找不到指定的文件
- 知乎Redis的演进之路:从单机到2000万QPS的挑战
- 开篇词 | 算法是程序的“灵魂”
- 欧几里得距离转换(EDT)算法
- Android Cursor的一些浅显见解
- stm32f429基于ymodem传输的bootloader
- 传输线理论基础01——相关定义、信号速率、分布参数与电报方程
- 《应用回归分析》何晓群 最新版数据下载
- SpringBoot整合MQ消息队列
- 灰狼优化算法求函数最优值matlab,GWO(灰狼优化)算法MATLAB源码逐行中文注解(转载)...