Two small helper functions (wrapper around the write function) and a panic handler for both Linux and Windows.
What the panic handler does is to print out the values in the 16 GPRs + the RIP register whenever a crash occurs.
The print occurs to stderr, while an equivalent stdout function is present as well.
Just drag and drop the include/panic_handler directory in your project, include include/panic_handler/panic_handler.hpp in your source file (ideally, the same file where your main() is located), add "include/panic_handler/panic_handler.cpp" in your CMakeLists.txt file and call it a day!
Your main() must be of the following way to reasonably use the provided functions:
// Other includes...
#include "include/panic_handler/panic_handler.hpp"
// ...
int main(int argc, char* argv[]) { // or int main()
std::signal(SIGINT, [](const int signum_) {
stderr_write("SIGINT (external interrupt) sent to program.");
std::_Exit(signum_);
});
std::signal(SIGTERM, [](const int signum_) {
stderr_write("SIGTERM (termination request) sent to program.");
std::_Exit(signum_);
});
#ifdef _WIN32
LPTOP_LEVEL_EXCEPTION_FILTER prev_handler_{ SetUnhandledExceptionFilter(panic_handler) };
#elifdef __linux__ // ^^^ _WIN32 || __linux__ vvv
struct sigaction signal_action_{};
signal_action_.sa_sigaction = panic_handler;
signal_action_.sa_flags = SA_SIGINFO;
sigemptyset(&signal_action_.sa_mask);
sigaction(SIGSEGV, &signal_action_, nullptr);
sigaction(SIGABRT, &signal_action_, nullptr);
sigaction(SIGILL, &signal_action_, nullptr);
sigaction(SIGFPE, &signal_action_, nullptr);
// Ignore SIGINT and SIGTERM
// SIGINT is initiated by user
// SIGTERM is sent to the program
// Neither of the two are caused by the program itself
#endif // ^^^ _WIN32 ^^^
// ... Your own logic
}As is evident, the panic handlers must be the first thing to be setup in main. You could also follow the main.cpp provided in this repo to know more.
An example is provided in the repo itself, via the src/main.cpp:
// ... in main()...
int* ptr{ nullptr };
*ptr = 2;I deliberately dereference the null pointer for this example.
On Windows, the example will write to stderr something similar to:
Process ID: 26408
Exception at address 0x00007FF7E6E11037: EXCEPTION_ACCESS_VIOLATION - a thread attempted to read from an address it doesn't have access to.
Registers:
RAX: 0x0000000000000000 RCX: 0xFFFFFFFFFFFFFFFF
RDX: 0x0000000000000000 RBX: 0x000001E1E09AF2B0
RSP: 0x00000030412FFA70 RBP: 0x0000000000000000
RSI: 0x0000000000000000 RDI: 0x000001E1E09B6510
RIP: 0x00007FF7E6E11037
R8: 0x00000030412FF748 R9: 0x0000000000000000
R10: 0x0000000000000000 R11: 0x00000030412FFA60
R12: 0x0000000000000000 R13: 0x0000000000000000
R14: 0x0000000000000000 R15: 0x0000000000000000On a Linux system, the example will write to stderr something similar to:
Process ID: 444
Exception at address 0x0000000000000000: SIGSEGV (segment violation).
Registers:
RAX: 0x0000000000000000 RCX: 0x0000779148C3E3B6
RDX: 0x0000000000000000 RBX: 0x0000000000000000
RSP: 0x00007FFF8839A640 RBP: 0x00007FFF8839A6F0
RSI: 0x00007FFF8839A4F0 RDI: 0x0000000000000008
RIP: 0x0000625730CEA178
R8: 0x0000000000000000 R9: 0x0000000000000000
R10: 0x0000000000000008 R11: 0x0000000000000246
R12: 0x0000000000000000 R13: 0x0000000000000001
R14: 0x000077914935E000 R15: 0x0000625730CECDA8Now, let us do...
std::cout << 10 / 0;On Windows, this writes something similar to stderr:
Process ID: 2728
Exception at address 0x00007FF62A451509: EXCEPTION_INT_DIVIDE_BY_ZERO - division by zero.
Registers:
RAX: 0x000000000000000A RCX: 0x0000000000000000
RDX: 0x0000000000000000 RBX: 0x0000025F9222DB10
RSP: 0x00000074B6BCF990 RBP: 0x0000000000000000
RSI: 0x0000000000000000 RDI: 0x0000025F9222CDD0
RIP: 0x00007FF62A451509
R8: 0x7FFFFFFFFFFFFFFC R9: 0x00000074B6BCF814
R10: 0x00000FFF3B0FCD80 R11: 0x00000074B6BCF950
R12: 0x0000000000000000 R13: 0x0000000000000000
R14: 0x0000000000000000 R15: 0x0000000000000000On Linux, it writes the following to stderr:
Process ID: 526
Exception at address 0x000056C5D1CF628C: SIGFPE (floating point exception - erroneous arithmatic operation, maybe division by zero?).
Registers:
RAX: 0x000000000000000A RCX: 0x0000000000000000
RDX: 0x0000000000000000 RBX: 0x0000000000000000
RSP: 0x00007FFFD7FECEF0 RBP: 0x00007FFFD7FECFA0
RSI: 0x00007FFFD7FECDA0 RDI: 0x0000000000000008
RIP: 0x000056C5D1CF628C
R8: 0x0000000000000000 R9: 0x0000000000000000
R10: 0x0000000000000008 R11: 0x0000000000000246
R12: 0x00007FFFD7FED0D8 R13: 0x0000000000000001
R14: 0x00007946C68D4000 R15: 0x000056C5D1CF8DA8