Files
klubhaus-doorbell/libraries/FastLED/tests/crash_handler_win.h
2026-02-12 00:45:31 -08:00

432 lines
15 KiB
C++

#ifndef CRASH_HANDLER_WIN_H
#define CRASH_HANDLER_WIN_H
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <dbghelp.h>
#include <psapi.h>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <string>
#ifdef _MSC_VER
#pragma comment(lib, "dbghelp.lib")
#pragma comment(lib, "psapi.lib")
#endif
namespace crash_handler_win {
// Global flag to track if symbols are initialized
static bool g_symbols_initialized = false;
// Helper function to get module name from address
inline std::string get_module_name(DWORD64 address) {
HMODULE hModule;
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCTSTR)address, &hModule)) {
char moduleName[MAX_PATH];
if (GetModuleFileNameA(hModule, moduleName, MAX_PATH)) {
// Extract just the filename
char* fileName = strrchr(moduleName, '\\');
if (fileName) fileName++;
else fileName = moduleName;
return std::string(fileName);
}
}
return "unknown";
}
// Helper function to demangle C++ symbols
inline std::string demangle_symbol(const char* symbol_name) {
if (!symbol_name) return "unknown";
// For now, return as-is. In a full implementation, you might want to use
// a C++ demangler library or call the Windows undecorate function
return std::string(symbol_name);
}
inline std::string get_symbol_with_gdb(DWORD64 address) {
printf("[DEBUG] get_symbol_with_gdb called with address: 0x%llx\n", address);
// Get the module base address to calculate file offset
HMODULE hModule = nullptr;
if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(LPCSTR)address, &hModule)) {
printf("[DEBUG] GetModuleHandleExA failed\n");
return "-- module not found";
}
// Calculate file offset by subtracting module base
DWORD64 fileOffset = address - (DWORD64)hModule;
// Get module filename
char modulePath[MAX_PATH];
if (!GetModuleFileNameA(hModule, modulePath, MAX_PATH)) {
printf("[DEBUG] GetModuleFileNameA failed\n");
return "-- module path not found";
}
printf("[DEBUG] Module path: %s\n", modulePath);
// Use addr2line for all test executables (anything starting with "test_")
char* fileName = strrchr(modulePath, '\\');
if (!fileName) fileName = modulePath;
else fileName++;
printf("[DEBUG] File name: %s\n", fileName);
if (strncmp(fileName, "test_", 5) != 0) {
printf("[DEBUG] Not a test executable, filename doesn't start with 'test_'\n");
return "-- not a test executable";
}
printf("[DEBUG] Test executable detected, proceeding with GDB\n");
// Build gdb command for symbol resolution (much better with PE+DWARF than addr2line)
// Use a temporary script file to avoid quoting issues
static int script_counter = 0;
char script_name[256];
snprintf(script_name, sizeof(script_name), "gdb_temp_%d.gdb", ++script_counter);
// Create temporary GDB script
FILE* script = fopen(script_name, "w");
if (!script) {
printf("[DEBUG] Failed to create GDB script file\n");
return "-- gdb script creation failed";
}
fprintf(script, "file %s\n", modulePath);
fprintf(script, "info symbol 0x%llx\n", address);
fprintf(script, "info line *0x%llx\n", address);
fprintf(script, "quit\n");
fclose(script);
printf("[DEBUG] Created GDB script: %s\n", script_name);
// Build command using script file
char command[1024];
snprintf(command, sizeof(command), "gdb -batch -x %s 2>nul", script_name);
printf("[DEBUG] Executing command: %s\n", command);
// Execute gdb
FILE* pipe = _popen(command, "r");
if (!pipe) {
printf("[DEBUG] _popen failed\n");
return "-- gdb failed";
}
char output[512] = {0};
std::string symbol_result;
std::string line_result;
// Read gdb output
while (fgets(output, sizeof(output), pipe)) {
std::string line(output);
// Remove newline
if (!line.empty() && line.back() == '\n') {
line.pop_back();
}
// Skip copyright and other non-symbol lines
if (line.find("Copyright") != std::string::npos ||
line.find("This GDB") != std::string::npos ||
line.find("License") != std::string::npos ||
line.empty()) {
continue;
}
// Look for symbol information
if (line.find(" in section ") != std::string::npos) {
// Parse gdb "info symbol" output format: "symbol_name in section .text"
size_t in_pos = line.find(" in section ");
if (in_pos != std::string::npos) {
symbol_result = line.substr(0, in_pos);
}
} else if (line.find("No symbol matches") != std::string::npos) {
symbol_result = "-- symbol not found";
} else if (line.find("Line ") != std::string::npos && line.find(" of ") != std::string::npos) {
// Parse gdb "info line" output format: "Line 123 of \"file.cpp\" starts at address 0x..."
line_result = line;
} else if (line.find("No line number information") != std::string::npos) {
line_result = "-- no line info";
}
}
_pclose(pipe);
// Clean up temporary script file
remove(script_name);
printf("[DEBUG] GDB symbol_result: '%s'\n", symbol_result.c_str());
printf("[DEBUG] GDB line_result: '%s'\n", line_result.c_str());
// Combine symbol and line information for comprehensive debugging
std::string result;
if (!symbol_result.empty() && symbol_result != "-- symbol not found") {
result = symbol_result;
if (!line_result.empty() && line_result != "-- no line info") {
result += " (" + line_result + ")";
}
} else if (!line_result.empty() && line_result != "-- no line info") {
result = line_result;
} else {
result = "-- no debug information available";
}
printf("[DEBUG] Final result: '%s'\n", result.c_str());
return result;
}
inline void print_stacktrace_windows() {
HANDLE process = GetCurrentProcess();
// Initialize symbol handler if not already done
if (!g_symbols_initialized) {
// Set symbol options for better debugging with Clang symbols
SymSetOptions(SYMOPT_LOAD_LINES |
SYMOPT_DEFERRED_LOADS |
SYMOPT_UNDNAME |
SYMOPT_DEBUG |
SYMOPT_LOAD_ANYTHING | // Load any kind of debug info
SYMOPT_CASE_INSENSITIVE | // Case insensitive symbol lookup
SYMOPT_FAVOR_COMPRESSED | // Prefer compressed symbols
SYMOPT_INCLUDE_32BIT_MODULES | // Include 32-bit modules
SYMOPT_AUTO_PUBLICS); // Automatically load public symbols
// Try to initialize with symbol search path including current directory
char currentPath[MAX_PATH];
GetCurrentDirectoryA(MAX_PATH, currentPath);
if (!SymInitialize(process, currentPath, TRUE)) {
DWORD error = GetLastError();
printf("SymInitialize failed with error %lu (0x%lx)\n", error, error);
printf("This may be due to missing debug symbols or insufficient permissions.\n");
printf("Try running as administrator or ensure debug symbols are available.\n\n");
} else {
g_symbols_initialized = true;
printf("Symbol handler initialized successfully.\n");
}
}
// Get stack trace
void* stack[100];
WORD numberOfFrames = CaptureStackBackTrace(0, 100, stack, nullptr);
printf("Stack trace (Windows):\n");
printf("Captured %d frames:\n\n", numberOfFrames);
// Buffer for symbol information
char symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
// Line information
IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
for (WORD i = 0; i < numberOfFrames; i++) {
DWORD64 address = (DWORD64)(stack[i]);
printf("#%-2d 0x%016llx", i, address);
// Get module name first
std::string moduleName = get_module_name(address);
printf(" [%s]", moduleName.c_str());
// Try to get symbol information - prioritize gdb for DWARF symbols in PE files
std::string gdb_result = get_symbol_with_gdb(address);
if (gdb_result.find("--") != 0) {
printf(" %s", gdb_result.c_str());
} else if (g_symbols_initialized) {
// Fallback to Windows SymFromAddr API
DWORD64 displacement = 0;
if (SymFromAddr(process, address, &displacement, pSymbol)) {
std::string demangled = demangle_symbol(pSymbol->Name);
printf(" %s+0x%llx (via Windows API)", demangled.c_str(), displacement);
// Try to get line number
DWORD lineDisplacement = 0;
if (SymGetLineFromAddr64(process, address, &lineDisplacement, &line)) {
// Extract just the filename from the full path
char* fileName = strrchr(line.FileName, '\\');
if (fileName) fileName++;
else fileName = line.FileName;
printf(" [%s:%lu]", fileName, line.LineNumber);
}
} else {
DWORD error = GetLastError();
if (error != ERROR_MOD_NOT_FOUND) {
printf(" -- symbol lookup failed (error %lu)", error);
} else {
printf(" -- no debug symbols available");
}
}
} else {
printf(" -- no symbol resolution available");
}
printf("\n");
}
printf("\nDebug Information:\n");
printf("- Symbol handler initialized: %s\n", g_symbols_initialized ? "Yes" : "No");
printf("- Process ID: %lu\n", GetCurrentProcessId());
printf("- Thread ID: %lu\n", GetCurrentThreadId());
// Show loaded modules for debugging
printf("\nLoaded modules:\n");
HMODULE hModules[1024];
DWORD cbNeeded;
if (EnumProcessModules(process, hModules, sizeof(hModules), &cbNeeded)) {
for (unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
char moduleName[MAX_PATH];
if (GetModuleFileNameA(hModules[i], moduleName, MAX_PATH)) {
char* fileName = strrchr(moduleName, '\\');
if (fileName) fileName++;
else fileName = moduleName;
printf(" %s\n", fileName);
}
}
}
printf("\n");
}
inline LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS* ExceptionInfo) {
printf("\n=== WINDOWS EXCEPTION HANDLER ===\n");
printf("Exception caught: 0x%08lx at address 0x%p\n",
ExceptionInfo->ExceptionRecord->ExceptionCode,
ExceptionInfo->ExceptionRecord->ExceptionAddress);
// Print exception details
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION:
printf("Exception type: Access Violation\n");
printf("Attempted to %s at address 0x%p\n",
ExceptionInfo->ExceptionRecord->ExceptionInformation[0] ? "write" : "read",
(void*)ExceptionInfo->ExceptionRecord->ExceptionInformation[1]);
break;
case EXCEPTION_STACK_OVERFLOW:
printf("Exception type: Stack Overflow\n");
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
printf("Exception type: Illegal Instruction\n");
break;
case EXCEPTION_PRIV_INSTRUCTION:
printf("Exception type: Privileged Instruction\n");
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
printf("Exception type: Non-continuable Exception\n");
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
printf("Exception type: Array Bounds Exceeded\n");
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
printf("Exception type: Floating Point Denormal Operand\n");
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
printf("Exception type: Floating Point Divide by Zero\n");
break;
case EXCEPTION_FLT_INEXACT_RESULT:
printf("Exception type: Floating Point Inexact Result\n");
break;
case EXCEPTION_FLT_INVALID_OPERATION:
printf("Exception type: Floating Point Invalid Operation\n");
break;
case EXCEPTION_FLT_OVERFLOW:
printf("Exception type: Floating Point Overflow\n");
break;
case EXCEPTION_FLT_STACK_CHECK:
printf("Exception type: Floating Point Stack Check\n");
break;
case EXCEPTION_FLT_UNDERFLOW:
printf("Exception type: Floating Point Underflow\n");
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
printf("Exception type: Integer Divide by Zero\n");
break;
case EXCEPTION_INT_OVERFLOW:
printf("Exception type: Integer Overflow\n");
break;
default:
printf("Exception type: Unknown (0x%08lx)\n",
ExceptionInfo->ExceptionRecord->ExceptionCode);
break;
}
print_stacktrace_windows();
printf("=== END EXCEPTION HANDLER ===\n\n");
// Return EXCEPTION_EXECUTE_HANDLER to terminate the process
return EXCEPTION_EXECUTE_HANDLER;
}
inline void crash_handler(int sig) {
printf("\n=== SIGNAL HANDLER ===\n");
fprintf(stderr, "Error: signal %d:\n", sig);
// Print signal details
switch (sig) {
case SIGABRT:
printf("Signal: SIGABRT (Abort)\n");
break;
case SIGFPE:
printf("Signal: SIGFPE (Floating Point Exception)\n");
break;
case SIGILL:
printf("Signal: SIGILL (Illegal Instruction)\n");
break;
case SIGINT:
printf("Signal: SIGINT (Interrupt)\n");
break;
case SIGSEGV:
printf("Signal: SIGSEGV (Segmentation Fault)\n");
break;
case SIGTERM:
printf("Signal: SIGTERM (Termination)\n");
break;
default:
printf("Signal: Unknown (%d)\n", sig);
break;
}
print_stacktrace_windows();
printf("=== END SIGNAL HANDLER ===\n\n");
exit(1);
}
inline void setup_crash_handler() {
printf("Setting up Windows crash handler...\n");
// Set up Windows structured exception handling
SetUnhandledExceptionFilter(windows_exception_handler);
// Also handle standard C signals on Windows
signal(SIGABRT, crash_handler);
signal(SIGFPE, crash_handler);
signal(SIGILL, crash_handler);
signal(SIGINT, crash_handler);
signal(SIGSEGV, crash_handler);
signal(SIGTERM, crash_handler);
printf("Windows crash handler setup complete.\n");
}
inline void print_stacktrace() {
print_stacktrace_windows();
}
} // namespace crash_handler_win
#endif // _WIN32
#endif // CRASH_HANDLER_WIN_H