Some time ago, we published a similar try catch implementation, but the implementation itself has limitations, because they can't ignore SIGSEGV, SIGSYS, sigrap and other crash signals, and can't process some 3RD codes at all (these codes have no source code, only libraries, and it's always impossible to statically decompile and reassemble) or memory bugs that can't be solved in a short time For example, the structure memory alignment problem (SIGBUS) of the code without problems will occur on the ARM platform, resulting in forced crash (only limited to malicious crash, that is, the SIGBUS prompt memory alignment fault will occur after running the same code + structure for tens of thousands of times). Don't think it's impossible, which may occur on the ARM, The solution is to check the assembly code, check the alignment of all structures, and so on.
The code in this article is an extension of the following link:
C / C + + 11 try catch finally extension (functional)_ Liullittle blog - CSDN blog_ c++ finally
Suppose We hope that the following code will not cause the program to stop working, so what should we do?
int64_t llPtr = 0;
char* byPtr = (char*)llPtr;
*byPtr = rand() & 0xff;
... ...
printf("%s\n", "OK!");
We know that the above code must crash due to "SIGSEGV" and generate dump file / information (if set), so what should we do if we don't want a failure here?
SIGSEGV can be considered to be captured. There is a definition of lazy macro called SIG_IGN (ignore signal) but note that it does ignore the signal to ensure that the program will not stop working due to the crash signal, but this is limited. Signals such as SIGSEGV and SIGBUS are invalid. Don't consider such meaningless things. Even if you want to give up a signal, it shouldn't be such extensive operation, SIG_IGN is only applicable to the crash signal of the active raise of the program, such as SIG_PIPE.
OK, then you can apply the latest improvement: "try catch finally" for processing, but exceptions may potentially lead to memory leakage.
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!");
The above code can be executed correctly to OK! Part, and there will be no memory leakage problem, but once we operate something related to memory in lambda, exceptions may occur, such as copying a shared in the function_ PTR and an exception occurs before destruct, then shared_ptr references cannot be reduced, resulting in memory resource leakage, so a similar good way is not to use shared_ptr, or use it when you can be sure that no abnormality will occur. If you're really not sure you have to use it, there's a way: weak_ptr may be right for you.
The lambda function with potential exceptions can capture shared_ptr and other things, they do not cause the capture shared cannot be called_ PTR has a destructor, so it is online memory safe, but lambda internal operation is not memory safe. This must be made clear!
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 #endif namespace 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 ANDROID static void PrintStackTrace(); #endif private: 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 #endif namespace 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 ANDROID Watch(35, 0); // FDSCAN(SI_QUEUE) #endif Watch(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 ANDROID Unwatch(35, 0); // FDSCAN(SI_QUEUE) #endif Unwatch(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 ANDROID Try::PrintStackTrace(); #endif signal(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 ANDROID void 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 }