강연소개 exception handler 를통한 에러검출및수정 file강연소개– exception handler...

Post on 06-Nov-2019

6 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

강연 소개 – Exception Handler 를 통한에러 검출 및 수정

디버깅을 즐겨 하십니까..?

에러를 만나면 반갑습니까..?

전화로 버그 보고를 받았나요..?

잡히지 않는 버그 !!!!

따분한 강의 – 졸아도 좋습니다 !!!!

강연자 소개

테스터

온라인 게임 클라이언트 개발

로컬라이즈 및 해외지원 업무

디버깅, 최적화, 호환성 향상에 관심

강연 대상

x86 환경에서 Windows 프로그래밍– 디버깅/에러 추적은 CPU/OS에 따라 다르다

– 소개할 기술은 x86/Windows 에서 사용됨.

C/C++, Assembler에 대한 구조적인 지식– Stack 이나 Register 등의 접근을 요구함

– Listing File 을 참고해야 하는 경우가 있다.

디버깅의 난해함

작은 개발 팀에서의 프로그램 출시– 테스팅/QA 팀이 없거나 기술적으로 미숙함

– 부족한 시간에 따른 충분하지 못한 테스팅

에러에 대한 부족한 정보– 유저들에게 충실한 에러 보고를 요구 할 수 없다.

– 에러의 존재 유무조차 알 수 없는 경우도 많다.

온라인 서비스– 자주 출시되는 실행 파일로 테스트 부족

– 서비스 중단에 따른 금전적 손실

C/C++ 버그의 대표적인 예

포인터, 포인터, 포인터 !!!– 기술적인 에러의 98% 는 포인터 문제다.

간혹 일어나는 divide by zero– 발생 확률이 적어서 디버깅이 더 어렵다.

Debug Code 에선 에러가 나지 않을 수 있다.– Release 로 컴파일 해서 디버깅은 가능하다.

방어적인 코딩으로는 해결되지 않는다.– 방어적인 코딩은 에러를 감출 뿐이다.

SHE의 소개

SEH - Structured Exception Handling– Windows 에서 제공해주는 예외 처리 기능

– User Mode 에서의 S/W, H/W 예외의 처리

예외의 종류– STATUS_ACCESS_VIOLATION

– EXCEPTION_INT_DIVIDE_BY_ZERO

– 기타 에러는 GetExceptionCode() 함수 참고

각종 예외 – Windows 98

각종 예외 – Windows XP

각종 예외 – 라그나로크

SEH의 사용 - __try ~ __except

void main(){

int* p = 0x00000000; // pointer to NULLputs("hello");try{

puts("in try");try{

puts("in try");*p = 13; // causes an access violation exception;

}__finally{puts("in finally");

}}__except(puts("in filter"), 1){

puts("in except");}puts("world");

}

SHE의 사용 -SetUnhandledExceptionFilter

LPTOP_LEVEL_EXCEPTION_FILTER

SetUnhandledExceptionFilter( LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter );

– 기본 구조는 __try ~ __except 와 같음

– 미 설정시 윈도우의 오류 화면 출력

– __try ~ __except 가 준비되지 않은 코드에서 자동적으로 호출됨

UnhandledExceptionFilter

LONG UnhandledExceptionFilter( STRUCT _EXCEPTION_POINTERS *ExceptionInfo );

typedef struct _EXCEPTION_POINTERS {

PEXCEPTION_RECORD ExceptionRecord;

PCONTEXT ContextRecord;

} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;

– SetUnhandledExceptionFilter 의 인자

– 일반적인 CallBack 함수와 같은 형태

– ExceptionInfo에서 CONTEXT 정보를 얻을 수 있다.

CONTEXT 란..?

Thread Context– CPU의 각종 레지스터 정보

– CPU 에 따라 다른 정보가 들어있을 수 있음

디버깅에서의 Thread Context– 실행중인 쓰레드의 정보를 알 수 있다.

– 메모리의 정보와 조합하여 프로그램의 실행 상태를 모두 파악할 수 있다.

X86의 CONTEXT 구조체

typedef struct _CONTEXT {

DWORD ContextFlags;

DWORD Dr0;DWORD Dr1;DWORD Dr2;DWORD Dr3;DWORD Dr6;DWORD Dr7;

FLOATING_SAVE_AREA FloatSave;

DWORD SegGs;DWORD SegFs;DWORD SegEs;DWORD SegDs;

DWORD Edi;

DWORD Esi;

DWORD Ebx;

DWORD Edx;

DWORD Ecx;

DWORD Eax;

DWORD Ebp;

DWORD Eip;

DWORD SegCs; // MUST BE SANITIZED

DWORD EFlags; // MUST BE SANITIZED

DWORD Esp;

DWORD SegSs;

BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];

} CONTEXT;

예제 프로그램 (1)

UnahdledExceptionFilter 예제

ExceptionFilter 에서 정보를 얻자.

레지스터의 값에서 얻을 수 있는 정보– EIP 를 통한 현재 실행 명령의 주소

– ESP/EBP를 통한 Stack 의 범위/내용

– 범용 레지스터로 부터 현재 실행상태

메모리에서 얻을 수 있는 정보– ExceptionFilter도 같은 프로세스이다.

– ESP/EBP의 범위는 항상 유효하다.

– Static/Global 한 정보도 항상 유효하다.

Debug Help Library 의 간단한 소개

Debug Help Library 의 기능– Symbol 의 접근 및 정보 제공

– MiniDump 의 접근 및 정보 제공

DbgHelp.dll 을 통해 제공– Windows 2000 이후에는 OS 기본 제공

– 프로그래머가 직접 배포할 수 있다.

Debugging Symbol – 컴파일시 함수명/변수명 정보가 기록

– Debug 모드에선 EXE 에 포함

SymFromAddr

The SymFromAddr function retrieves symbol information for the specified address.

BOOL SymFromAddr(

HANDLE hProcess,

DWORD64 Address,

PDWORD64 Displacement,

PSYMBOL_INFO Symbol

);

SymFromAddr (2)

실행중인 주소의 SYMBOL_INFO 를 읽어옴

CONTEXT 의 EIP 에 해당함

프로그램은 항상 같은 주소에서 실행된다.

SYMBOL_INFO 에서 클래스 함수나 현재 실행중인 함수를 얻어올 수 있다.

SymGetLineFromAddr

The SymGetLineFromAddr function locates the source line for the specified address.

BOOL SymGetLineFromAddr(

HANDLE hProcess,

DWORD dwAddr,

PDWORD pdwDisplacement,

PIMAGEHLP_LINE Line );

SymGetLineFromAddr (2)

실행중인 주소의 IMAGEHLP_LINE 을 읽어옴

CONTEXT의 EIP 에 해당함

IMAGEHLP_LINE 에서 에러가 발생한 소스코드의 파일명, 행수를 얻어올 수 있다.

StackWalk

The StackWalk function provides a portable method for obtaining a stack trace.

BOOL StackWalk(

DWORD MachineType,

HANDLE hProcess,

HANDLE hThread,

LPSTACKFRAME StackFrame,

PVOID ContextRecord,

PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,

PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,

PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,

PTRANSLATE_ADDRESS_ROUTINE TranslateAddress );

StackWalk (2)

Stack을 뒤져서 Return 주소를 찾아낸다.

계속해서 찾으면 CallStack이 된다.

CONTEXT의 EIP와 ESP/EBP로 찾는다.

Symbol 없을 경우 ESP/EBP를 기본으로 찾아간다.

Debugging Application 서적을 참고하자.

Release 에서 PDB 만들기

Visual C++ 의 설정– Release 옵션에서 기본 설정으로 PDB 생성 않음

– Project Setting->Link->[Debug]->Debug Info

주의 사항– PDB는 실행파일과 같은 디렉토리나 Symbol 디렉

토리에 존재해야한다.

– Visual C++ 6.0 에서는 ‘Run’ 명령으로는 PDB를정상적으로 찾지 못한다.

예제 프로그램 (2)

ExceptionFilter 를 통한 에러 로그 출력

PDB의 배포와 문제점

PDB의 배포– 용량이 크다.

– 프로그램의 각종 세 한 정보가 들어 있다.

PDB가 없을 경우– Symbol이 없으므로 CallStack의 에러를 내용을

알 수 없다.

– 최적화된 경우 PDB 없이 StackWalk가 CallStack을 정상적으로 찾을 수 없다.

Frame-Pointer

Frame-Pointer 란..?– 함수마다 Frame 정보를 Stack에 남긴다.

– Frame 정보는 EBP/ESP 정보들이다.

– Frame-Pointer-Omission 된 상태에서는FPO_DATA 데이터가 Symbol 에 저장된다.

Frame-Pointer 의 설정– 최적화시 Frame-Pointer-Omission이 된다.

– CL.EXE 컴파일러의 /Oy- 옵션으로 강제 설정

PDB 없이 Exception 데이터 생성

실행파일 설정– 긱종 디버깅 정보를 남긴다.

– 실행파일은 FP를 함수에서 남기도록 설정

– Exception Filter 는 StackWalk로 CallStack의 주소만을 알아내서 로그로 남기게 된다.

Symbol Reader 제작– PDB를 뒤져서 CallStack의 정보를 읽을 수 있는

버전으로 해독한다.

주의 사항 – Time Stamp, 실행파일 이름

여러 버전의 실행파일의 출시– TimeStamp를 찍는다.

– 유저의 시스템은 가능한 믿지 말자.

어느 실행파일의 에러인가..? – 여러 개의 실행파일을 출시하는 경우 실행파일 이

름이 없으면 어떤 에러인지 알 수 없다.

예제 프로그램 (3)

PDB 없이 Exception 데이터 생성

PDB,EXE 파일로 CallStack 정보를 읽어내기

Call Stack 으로 알아보기 힘든 버그

한줄에서 여러가지 작업을 하는 경우– Line을 알아도 에러를 특정할 수 없다.

최적화시에는 순서가 바뀔 수 있다.– 최적화가 되어 있을 경우 실행 순서가 바뀐다.

– PDB에 잘못된 행수가 적힐 수 있다.

포인터 에러는 구분하기 힘들 수 있다.– 포인터의 포인터 연산

– 포인터가 한줄에 여러 개 있다.

Listing File 의 생성 및 사용

Listing File 이란..?– C/C++의 컴파일 결과물을 보여주는 텍스트 파일

– Source Code/Assembler/Machine Code

Listing File 의 생성– Project Settings->C/C++->Listing Files

Listing File의 용도– SymFromAddr의 Displacement로 에러 확인

– 에러 추적을 위해서 Assembler 지식이 필요함

Memory Dump의 유용성

Memory Dump 란..?– ESP가 가르키는 Stack의 일정 영역을 기록

– 프로그래머가 미리 지정한 특정 주소를 기록

Memory Dump 의 사용법– Stack의 내용은 Listing File 참조

– Listing File에 어셈블러 코드만으로 디버깅이 안될때 사용한다.

예제 프로그램 (4)

Listing 파일에서 에러 찾기

메모리 덤프 참고하기

CallStack 이 비정상적으로 보이는 경우

Template– Symbol 정보가 정상적으로 남아있지 않는 경우

– iterator 가 end를 넘어간 경우

inline 된 함수들의 디버깅– inline은 호출 함수에 녹아들어간다.

– CallStack은 inline의 위치만 표시한다.

NULL Function Pointer 의 호출– StackWalk가 비정상적인 작동

디버깅이 매우 힘든 에러

다른 Thread 에서 에러– DirectX 내부에서 호출되는 에러가 검출된다.

– 멀티 쓰레드의 경우 해당 Thread 정보만 남는다.

ExceptionFilter 에 걸리지 않는 종료– Windows NT 계열에서 발생

– Handle을 너무 많이 쓰거나 지속적으로 증가시발생하는 경우가 많음.

– 에러 추적이 매우 어려움

비정상적인 Data, System 문제도 고려한다

일어날 수 없는 에러– 0.000001%의 에러도 있다.

– 프로그램에서 절대 발생할 수 없는가..?

– 일단 지나가자 !!!

데이터에는 문제가 없는가..?– 데이터의 의존하는 코드의 문제

– 각종 유저 데이터도 확인한다.

에러는 어떻게 모을 것인가..?

유저들의 직접 전달에 의한 접수

네트워크를 통한 간단한 툴을 제작

모두 자동화 시킨다.

질문 / 답변 받기

top related