파일 비교 프로그램은 WinMerge 라는 걸출한 프로그램 툴이 있다. WinMerge 와 비슷하게 diffchecker 웹페이지에서 간단한 텍스트 및 코드 비교 체크 할수 있다.
Computed Diff - Diff Checker Compare text Diffchecker will compare text to find the difference between two text files. Just paste your files and click Find Difference
# 개요 : 프로그램을 실행하다 보면 예상하지 못한 곳에서 충돌이 발생해 프로그램 다운되는 현상이 발생한다. 이런 충돌을 해결하는 방법에는 여러가지가 있겠지만 Minidump 기능에 대해 알아 보자.
# 장점 : 1. 정확한 충돌 위치를 확인 할 수 있다. Minidump기능을 작성한 응용 프로그램에 넣어두면 프로그램이 크래쉬를 일으킨다면 정확한 충돌 위치를 찾을 수 있다. 추가적으로 CallStack 정보 까지 알아 볼 수 있다.
2. 충돌시 변수 값들이 확인 가능하다. 프로그램이 크래쉬가 났다면 보통 무엇으로 인해서 Kill 되었는지 매우 궁금 할 것이다. 보통 충돌 위치만 알고 어떤 값으로 인해서 죽었는지 궁금한 경우가 많은데 Minidump를 사용하면 모든 메모리 정보를 저장할 수도 있고 그 함수 안에 있는 로컬 변수에 값들을 확인 할 수 있다.
3. 소스가 간결해진다. 보통 크래쉬 위치를 알기 위해서 Try Catch를 사용하는데 이것 또한 여러 소스에 코딩을 할려면 여간 귀찮은 일이 아니다. Minidump는 제일 처음에 한번만 등록을 하면 더이상 소스코드 부분에 신경을 쓸 필요가 없다.
# 단계진행방향 : 1. SetUnhandledExceptionFilter로 충돌 위치 정보얻기
2. MiniDumpWirteDump로 덤프 파일(dmp) 생성
3. 덤프 파일의 종류
4. 생성된 덤프파일 디버깅
5. Minidump기능 확장 1) FTP로 덤프파일 관리 2) Memory Snapshot 3) 추가 내용 기록
# 단계세부설명 :
1. SetUnhandledExceptionFilter로 충돌 위치 정보얻기 SetUnhandledExceptionFilter 함수는 예외가 발생할 때 호출되어 CallBack 함수를 지정(등록)한다. 쉽게 말해 예외처리를 하지 않은 부분에서 예외가 발생했을 경우 이 함수가 지정한 곳으로 크래쉬 정보들이 들어오게 되는 것을 의미한다. 원래 설정된 값을 담아 프로그램 종료 시 복구 용도로 쓰인다.
사용법 SetUnhandledExceptionFilter(TopLevelFilter);
2. MiniDumpWirteDump로 덤프 파일(dmp) 생성 먼저 MiniDump의 기능을 사용하기 위해서는 DBGHELP.DLL 파일이 필요하다. Debugging Tools for Windows에 포함되어 있다. MiniDumpWriteDump기능을 사용하기 위해선 DBGHELP.DLL 5.1 이후 부터 지원을 받을 수 있다. MiniDumpWirteDump를 실행시키면 MiniDump파일을 생성하게 된다.
3. 덤프 파일 종류 덤프 파일을 만들때에도 여러 가지 종류로 나누어져 생성 할 수 있다. 위의 사용법에서 4번째 인자에 그 종류가 들어가게 된다.
주로 2가지를 자주 사용하는데 MiniDumpNormal : 함수의 로컬레지스터 값을 포함해서 덤프를 한다. MiniDumpWithFullMemory : 전체 메모리 값을 포함하여 덤프를 하게 된다.
4. 생성된 덤프 파일 디버깅 덤프 파일(dmp)이 생성이 되면 더블 클릭만으로 덤프파일을 VS .NET로 열수 있다. 덤프파일을 열고 실행(F5)을 하면 정확한 충돌 위치의 코드로 이동하여 크래쉬를 알 수 있다.
조사식에 죽었을 때 당시 로컬(지역) 변수를 찍어 값도 확인 할 수 있다. 하지만 전역 변수값은 확인이 어려울 수도 있는데 그건 minidump를 Normal 형식으로 덤프 했기 때문에 로컬 변수에 대해 값을 저장하기 때문이다. FullMemory 방식으로 메모리를 저장하면 덤프 용량은 커지지만 예외처리가 발생했을 때 당시의 메모리 값들을 전부 저장하게 된다. 그리고 오른쪽에 호출 스택에서 어떤 경로를 통해 프로그램이 크래쉬 되었는지 확인 할 수 있다.
5. Minidump기능 확장 1) FTP로 덤프파일 관리 덤프 파일이 생성이 되면 보통 사용자 컴퓨터에 정보가 남게 된다. 이 정보를 받아야 디버그를 할 수 있기 때문에 어떤 수단을 통해서든 받아야 할 것이다. 메일을 통해서든 이동식 디크스를 통해서든 받을 수 있지만 가장 사용자들에게 쉬운 방법은 Window에서 사용 하는 방식처럼 사용자의 동의를 구하고 파일을 FTP로 전송을 하면 되겠다. 2) Memory Snapshot 가짜 충돌 상황을 발생시켜 덤프파일을 생성시키는 기능이다.
예를들어 서버 프로그램 서비스 도중 메모리 값에 문제가 생겼을 경우 파일 로그나 코드나 유저 정보들을 보고 문제점을 해결 해야 한다. 하지만 찾기가 매우 어렵다면 디버깅을 하고 싶은 충동을 느끼게 되는데 Memory Snapshot을 활용 가짜 예외처리 상황(문제점 재현 시나리오)을 만들어 덤프 파일을 생성하게 한다. 그러면 서버는 브레이크를 걸지 않고 서버 상태를 점검 할 수 있게 된다. 단점은 서버가 사용하는 메모리 용량이 파일 행태로 다 복사를 해야 하기 때문에 덤프 생성 시간이 소요되게 될 것이다. 3) 추가 내용 기록 pMiniDumpWriteDump의 6번째 인자를 보면 MINIDUMP_USER_STREAM_INFORMATION 이라는 정보를 입력 할 수 있다. 구조체 이름 그대로 유저정보를 입력 할 수 있다. 여기에 사용자 동의를 구하고 사용자 컴퓨터의 메모리 상태라던지 그래픽카드 정보를 추가적으로 입력하면 하드웨어에 따른 컴퓨터 오류를 보다 쉽게 해결 할 수 있을 것이다.
여기에 화면 리스토어로 리소스 복구가 일어났을 경우와 사용자의 각각의 이동경로 히스토리로 기억해서 어떤 경로를 통해서 이동하다가 프로그램 오류를 일으켰는지와 마지막 받은 패킷 메세지 번호를 기억하여 어떤 패킷을 받고 그 패킷 처리 후 죽었다는 정보를 추가적으로 남겨서 사용자의 여러 정황에 대해서 상세하게 파악하는데 많은 도움을 받는다.
// 덤프 파일 이름 설정 _sntprintf_s(DumpPath, MAX_PATH, _TRUNCATE, _T("%d-%d-%d %d_%d_%d.dmp"), SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond);
// 덤프 파일 생성 HANDLE FileHandle = CreateFile(DumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == FileHandle) { printLastError(); return EXCEPTION_CONTINUE_SEARCH; }
// MiniDump 예외 정보 저장 구조체 _MINIDUMP_EXCEPTION_INFORMATION MiniDumpExceptionInfo; MiniDumpExceptionInfo.ThreadId = GetCurrentThreadId(); MiniDumpExceptionInfo.ExceptionPointers = (PEXCEPTION_POINTERS)exceptionInfo; MiniDumpExceptionInfo.ClientPointers = NULL;
// typedef BOOL(WINAPI *MINIDUMPWRITEDUMP)(HANDLE, DWORD, HANDLE, TYPE, Param, Param, Param) // 현재 프로세스에 대한 덤프 기록을 실행 BOOL bSuccess = Dump(GetCurrentProcess(), GetCurrentProcessId(), FileHandle, // 덤프를 기록할 파일 핸들 MiniDumpNormal, // MINIDUMP_TYPE &MiniDumpExceptionInfo, // MiniDump 예외 정보 NULL, NULL);
CloseHandle(FileHandle);
// 덤프 기록 성공시 수행 return (TRUE == bSuccess)? EXCEPTION_EXECUTE_HANDLER :EXCEPTION_CONTINUE_SEARCH; }
// UnHandled Exception 발생시 덤프 만들기 위해 사용 정의한 UnHandledExceptionFilter() 호출되도록 설정 // 프로그램 시작 부분에서 호출하도록 한다. BOOL CMiniDump::BeginDump() { // 잘못된 연산 메시지 창이 나오지 않도록 설정 SetErrorMode(SEM_FAILCRITICALERRORS);
// Unhandled Exception 발생시 호출될 CallBack 함수 등록 // PreviousExceptionFilter는 원래 설정을 담아 프로그램 종료시 복구 용도로 쓰인다. PreviousExceptionFilter = SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)UnHandledExceptionFilter);
return TRUE; }
// 원래 설정으로 복구하여 덤프가 종료 되도록 한다. // 프로그램 종료 부분에서 호출 하도록 한다. BOOL CMiniDump::EndDump() { // 프로그램 종료 전에 원래 설정으로 복구 SetUnhandledExceptionFilter(PreviousExceptionFilter); return TRUE; }
//>>>>> 3. 사용 예제 class CTestCls { public: BOOL isOpened; };
/* 덤프 파일 분석 방법 : 0. 덤프 파일이 생기면 1. 덤프파일을 Visual Studio로 연다. 2. F5 로 디버그를 시작하면 덤프 파일 솔루션을 저장하라고 나온다. 3. 덤프 파일 솔루션을 저장하면 덤프가 발생한 지점의 소스가 표시된다. ※ 주의 : 실행 파일을 만들때 생성된 PDB 파일과 같이 두어야 분석 가능합니다. */
Window 경우 stl_critical_section_win7이라는 클래스의 생성자를 호출하도록 구현되어 있음. Windows Vista와 그 이전 버전에서는 크리티컬 섹션을 사용해서 구현. SRW Lock 자체는 Vista 때 추가 했는데, try_lock에 대응되는 기능이 Windows 7에 추가됐기 때문.
VS2013까지는 std::mutex가 크리티컬 섹션보다 느리다가, VS2015부터는 더 빨라진 것을 보면 VS2013까지는 다른 구현 방식 (크리티컬 섹션일 수도 있고.. 아니면 다른 방법)을 사용하다가 VS2015부터 SRW Lock을 사용하기 시작한 듯.
# SRW(Slim Reader/Writer) LOCK
SRW(Slim Reader/Writer) Lock을 사용하면 단일 프로세스의 스레드가 공유 리소스에 액세스할 수 있다. 속도에 최적화되어 있으며 메모리를 거의 차지하지 않는다. Slim reader-writer Lock은 프로세스 간에 공유할 수 없다.
Reader 스레드는 공유 리소스에서 데이터를 읽는 반면 Writer 스레드는 공유 리소스에 데이터를 쓴다.
여러 스레드가 공유 리소스를 사용하여 읽고 쓸 때 Reader 스레드가 계속 실행되지만, 쓰기 작업이 드물면 Critical Section 또는 Mutex와 같은 배타적 잠금이 병목 현상이 될 수 있다.