Thanks to visit codestin.com
Credit goes to github.com

Skip to content

自制一个c++ log库-part4 #77

@icehoo

Description

@icehoo

自制一个c++ log库-part4

  1. 支持打印异常堆栈

    为了能捕获到异常,考虑使用

    __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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions