-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
Description
自制一个c++ log库-part4
-
支持打印异常堆栈
为了能捕获到异常,考虑使用
__try {} __except {}为了防止异常信息丢失,考虑直接写入文件,而不是存入缓存队列等待另一个线程提取再写日志。
__except里可以拿到exception的结构体,看起来试这样__except (GetExceptionInformation())GetExceptionInformation是个宏,能获取_EXCEPTION_POINTERS*,声明如下:typedef struct _EXCEPTION_POINTERS { PEXCEPTION_RECORD ExceptionRecord; PCONTEXT ContextRecord; } EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
其中
PEXCEPTION_RECORD声明如下:typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; DWORD ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; DWORD NumberParameters; ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD;
可以看到里面可以获取到
ExceptionCode,类型是DWORD,通过这个code可以获取到简单的错误信息:const char* Log4Cpp::LogFile::getExcepMsg(const DWORD& code) { switch (code) { case EXCEPTION_ACCESS_VIOLATION: return "EXCEPTION_ACCESS_VIOLATION"; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; case EXCEPTION_BREAKPOINT: return "EXCEPTION_BREAKPOINT"; case EXCEPTION_DATATYPE_MISALIGNMENT: return "EXCEPTION_DATATYPE_MISALIGNMENT"; case EXCEPTION_FLT_DENORMAL_OPERAND: return "EXCEPTION_FLT_DENORMAL_OPERAND"; case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; case EXCEPTION_FLT_INEXACT_RESULT: return "EXCEPTION_FLT_INEXACT_RESULT"; case EXCEPTION_FLT_INVALID_OPERATION: return "EXCEPTION_FLT_INVALID_OPERATION"; case EXCEPTION_FLT_OVERFLOW: return "EXCEPTION_FLT_OVERFLOW"; case EXCEPTION_FLT_STACK_CHECK: return "EXCEPTION_FLT_STACK_CHECK"; case EXCEPTION_FLT_UNDERFLOW: return "EXCEPTION_FLT_UNDERFLOW"; case EXCEPTION_ILLEGAL_INSTRUCTION: return "EXCEPTION_ILLEGAL_INSTRUCTION"; case EXCEPTION_IN_PAGE_ERROR: return "EXCEPTION_IN_PAGE_ERROR"; case EXCEPTION_INT_DIVIDE_BY_ZERO: return "EXCEPTION_INT_DIVIDE_BY_ZERO"; case EXCEPTION_INT_OVERFLOW: return "EXCEPTION_INT_OVERFLOW"; case EXCEPTION_INVALID_DISPOSITION: return "EXCEPTION_INVALID_DISPOSITION"; case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "EXCEPTION_NONCONTINUABLE_EXCEPTION"; case EXCEPTION_PRIV_INSTRUCTION: return "EXCEPTION_PRIV_INSTRUCTION"; case EXCEPTION_SINGLE_STEP: return "EXCEPTION_SINGLE_STEP"; case EXCEPTION_STACK_OVERFLOW: return "EXCEPTION_STACK_OVERFLOW"; default: return "UNKNOWN EXCEPTION"; } }
另外
_EXCEPTION_POINTER还有个上下文:ContextRecord,堆栈就主要从这个上下文来获取:LONG __stdcall Log4Cpp::LogFile::LogException(_EXCEPTION_POINTERS* exceptionInfo) { FILE* pFile = fopen(LOG_ERROR_FILE_Path, "at"); if (!pFile) return EXCEPTION_EXECUTE_HANDLER; //write log's head SYSTEMTIME systime; GetLocalTime(&systime); fprintf(pFile, "%hu-%02hu-%02hu %02hu:%02hu:%02hu.%03hu Exception occured, code: [0x%x], info: %s\n", systime.wYear, systime.wMonth, systime.wDay, systime.wHour, systime.wMinute, systime.wSecond, systime.wMilliseconds, exceptionInfo->ExceptionRecord->ExceptionCode, getExcepMsg(exceptionInfo->ExceptionRecord->ExceptionCode)); CONTEXT* ctx = exceptionInfo->ContextRecord; BOOL result; HANDLE process; HANDLE thread; HMODULE hModule; STACKFRAME64 stack; ULONG frame; DWORD64 displacement; DWORD disp; IMAGEHLP_LINE64* line; char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; char name[MAX_NAME_LEN]; char module[MAX_NAME_LEN]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; // On x64, StackWalk64 modifies the context record, that could // cause crashes, so we create a copy to prevent it CONTEXT ctxCopy; memcpy(&ctxCopy, ctx, sizeof(CONTEXT)); memset(&stack, 0, sizeof(STACKFRAME64)); process = GetCurrentProcess(); thread = GetCurrentThread(); displacement = 0; #if !defined(_M_AMD64) stack.AddrPC.Offset = (*ctx).Eip; stack.AddrPC.Mode = AddrModeFlat; stack.AddrStack.Offset = (*ctx).Esp; stack.AddrStack.Mode = AddrModeFlat; stack.AddrFrame.Offset = (*ctx).Ebp; stack.AddrFrame.Mode = AddrModeFlat; #endif SymInitialize(process, NULL, TRUE); //load symbols for (frame = 0; frame < 10; frame++) { //get next call from stack result = StackWalk64 ( #if defined(_M_AMD64) IMAGE_FILE_MACHINE_AMD64 #else IMAGE_FILE_MACHINE_I386 #endif , process, thread, &stack, &ctxCopy, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL ); if (!result) break; //get symbol name for address pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; SymFromAddr(process, (ULONG64)stack.AddrPC.Offset, &displacement, pSymbol); line = (IMAGEHLP_LINE64*)malloc(sizeof(IMAGEHLP_LINE64)); if (!line) return EXCEPTION_EXECUTE_HANDLER; line->SizeOfStruct = sizeof(IMAGEHLP_LINE64); //try to get line if (SymGetLineFromAddr64(process, stack.AddrPC.Offset, &disp, line)) { fprintf(pFile, "\tat %s():%lu\n", pSymbol->Name, line->LineNumber); } else { //failed to get line hModule = NULL; lstrcpyA(module, ""); GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)(stack.AddrPC.Offset), &hModule); //at least print module name if (hModule != NULL) GetModuleFileNameA(hModule, module, MAX_NAME_LEN); if (strcmp(module, "") != 0) fprintf(pFile, "\tat %s %s()\n", module, pSymbol->Name); } free(line); line = NULL; } fflush(pFile); fclose(pFile); return EXCEPTION_EXECUTE_HANDLER; }
最后,只要把
Log4Cpp::LogFile::LogException传入__except,即可在捕获后调用处理函数:__try { ... } __except (Log4Cpp::LogFile::LogException(GetExceptionInformation())) { ... }
完整的代码:
Log4Cpp.zip