Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
온라인 게임에서사례로 살펴보는 DebuggingNDC 2010NCsoft 박일http://parkpd.egloos.comtwitter:rigmania
왜 디버깅인가?
디버깅과 섹스의 공통점잘하면 재미있지만, 잘못하면 짜증난다.하면 할 수록 늘지만, 너무 자주 하는 건 싫다.밤늦게 할 때가 많다.대놓고 얘기하진 않지만 남들은 어떻게 하는지 궁금하다.교제가 거의 없다.과정은 신경쓰지 않고, 어서 결론에 도달하려고 한다.도구를 사용하면 도움은 되지만, 결국 사람이 해야 한다.술을 많이 마시고 하면 다음날 어떻게 했는지 기억이 나지 않는다.끝나고 나면 허무하고 담배가 땡긴다.급할 때는 더 안 된다.
재현진단수정 및 반영
왜 재현부터?버그가 나타날 때의 프로그램 상태를 볼 수 있다.버그를 수정했을 때, 정말 수정했는지를 증명할 수 있다.QA팀이 재현 가능해야 한다.
환경 만들기재현할 때는 문제가 되는 환경을 전부 똑같게설정 파일, 이벤트 파일 확인, 하드웨어, OS 버전, 보안 프로그램 동작 여부택배로 컴퓨터를 받아오기해외에 설정 파일을 암호화해서 보내기(3개월 전의) 해외 버전을 국내에도 유지하면 회귀 테스트 하기가 쉽다이번 업데이트 이후 바뀐 것인지 테스트 가능
사례 - OpenMP한 대의 컴퓨터에서만 에러 발생알고 보니 특정 머신(하퍼타운4 cores)에서만 에러 발생. 이유는?실제로 코드가 thread-safe 하지 않았음제대로 MT 를 지원하는 머신몰랐던 이유는?Core2 Duo 에서는 멀쩡함. 버그?하퍼타운을 쓰는 다른 개발자는 설정에서 omp사용을 빼 놨기 때문에 에러 없었음
사례 보고!현상은 원인이 있기 때문에 나타난다정상적인 재현이 불가능하면, 억지로라도 재현한다버그 report 를 그대로 믿지 마라QA 에서도 아는 선에서 리포트를 쓴다QA 분께 개인용 개발서버로 접속해서재현해 달라고 부탁한다msn 과 사내전화 활용
사례 – 희귀 아이템 경매라이브 서버가 아닌 테스트 서버, 그 중 한 npc에서만 발생재현도안 되어서, 별 문제 없겠지 생각하고 무시업데이트 후에 전 라이브 서버에서 문제 발생 이유 : 라이브 서버에서는 처음에 개발자가 직접 seed 값을 넣어줘서 경매에 문제 없었지만, 테스트 서버의 경우 개발자가 여러 npc중 한 npc만 까먹고 seed 값을 넣어주지 않았기 때문에 계속 문제가 있었음처음부터 문제가 있었는데도, QA 에서는 ‘잘 되다가 저번 주부터 문제가 생겼음’ 이라고 보고했고, 개발팀도 QA 만 믿고특이한 사례보고라고 생각함대규모 업데이트를 위해 모든 seed 값을 초기화했더니, 테스트 서버와 동일한 문제가 모든 npc에서 발생[사례보고] 라는 제목 때문에 선입관 생김
사례보고를무시하면한 방에훅 간다
사례 – 공성과 결자해지voidLoadData(){// do something¡¦CCastlec(nCastleNum);// dead code???return;}
사례 – 공성과 결자해지voidLoadData(){// do something¡¦CCastlec(nCastleNum);// dead code? NO!!!return;}classCCastle{public:CCastle(intn){g_CastleDB.Register(this);}};
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
사례 – 기계는 믿을 수 있는가특정 머신, CPU, 그래픽카드, NIC
재현진단수정 및 반영
버그 찾기dump 로 Crash 난 위치 확인최근에 고친 파일부터 diff이상현상이 생긴 시간대 근처 에러로그 확인내가 고친 코드를 먼저 의심다른 사람과 얘기해보자피규어 디버깅어셈블리는 마지막에서버라면 실제 라이브 서버를 터미널 서비스로 들어가 작업관리자로 보기
Everybody liesfalse positive(거짓양성)예전 바이너리를 쓰고 있다던가false negative(거짓음성)QA가 고쳐지지 않은 걸 고쳐졌다고 한다플라시보 효과예전부터 있던 코드도 100% 믿지 말자모든 걸 의심하자
Dump
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
minidump출력Crash 이메일 전송개발망에서도개발망email 로 전송소스 컨트롤러에 날짜_Label 로 폴더 만든 후 바이너리, pdb를 저장Symbol 서버 구축하기장,단점
Stack 이 깨졌다면?
Base Line
필승교의 교훈
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
Base Line지금 상태는 정상인가?평소 CPU 는 몇 % 정도였나?의도대로 최적화가 되었나?
Performance CountersCounter 를 log 나 DB 에 주기적으로 저장해 base line 를 만든다counter 예시SQL Server: SQL Statistics: Batch Requests/sec초당 요청 받은 SQL 배치 요청 수SQL Server: Buffer Manager: Buffer Cache Hit Ratio90 이상이면 좋음SQL Server: Locks: Lock Waits/sec잠금대기요청수Process: Page Faults/sec프로세스가 Cache Hit하지 않은 페이지수
볼 수 있으면고칠 수 있다
Visualizer (Visual Studio 2005)
Visualizer in C++autoexp.dat으로 watch 창 변경C:rogram Filesicrosoft Visual Studio 8ommon7ackagesebuggerutoexp.dat[AutoExpand]CMyData =head=<m_Head> m_Tail=<m_Tail,x> name=<m_Name,su>classCMyData{public:CMyData(inth,intt):m_Head(h),m_Tail(t){}intm_Head;charm_Name[1024];intm_Tail;};int_tmain(intargc,_TCHAR*argv[]){typedefvector<CMyData*>DataList;DataListdata;for(inti=0;i<10;++i){data.push_back(newCMyData(i,i+1));}return0;}
Debug Windows 1{[function],[source],[module]} 뒤에location, variable_name, expression특정 함수의 static local 변수 보기void Test() { static intsLocalNum = 0; } : {Test,,}sLocalNum특정 dll의 전역변수 : {,,foobar.dll}g_pMyStructBP 걸어놓고 함수 실행하기classCTest{public:staticCTest&Inst(){staticCTests;returns;}voidCheckValidate(){OutputDebugString(L"T");m_Test.push_back(1);}std::vector<int>m_Test;};int_tmain(){CTest::Inst().CheckValidate();return0;}
Debug Windows 2Pseudo Register@eax : 리턴값@err : GetLastError@HANDLES현재 프로세스의핸들 갯수@ebp : 지역 변수시작 지점@esp : 최상위 스택
사례 – BotvoidCPlayer::OnItemExchange(intnItemCount1,BYTE*pData1,intnItemCount2,BYTE*pData2) {for(inti=0;i<nItemCount1;++i){// do something}for(inti=0;i<nItemCount2;++i){// do something}}
Fuzzing Test임의의 입력을 만들어 주는 Fuzzer 사용Peach Fuzzing Platformhttp://peachfuzzer.com/generation 과 mutation 으로 입력값 생성RFuzzhttp://rfuzz.rubyforge.org일종의 monkey test
Bug?(1.0<a.hP)=false(1.0>=a.hP)=false
Bug?(1.0<a.hP)=false(1.0>=a.hP)=falseNaN(Not a Number)double answer = sqrt(-1.0);
structCData{static const intHALF_NUM=0x7fffffff;static const intMAX_NUM=0xffffffff;CData():m_dPoint(1.0){m_dData[0]=1.3;     m_dData[1]=1.5;m_nData[0]=HALF_NUM;  m_nData[1]=MAX_NUM;}voidApplyBonus(intnBonusIndex){m_dPoint*=m_dData[nBonusIndex];}doublem_dPoint;doublem_dData[2];unsignedlongm_nData[2];};int_tmain(intargc,_TCHAR*argv[]){CDataa;a.ApplyBonus(2);wcout<<L"isnan : "<<_isnan(a.m_dPoint)<<L'';wcout<<L"(1.0 < a.Point) : "<<(1.0<a.m_dPoint)<<L'';wcout<<L"(1.0 >= a.Point) : "<<(1.0>=a.m_dPoint)<<L'';return0;}// outputisnan:1(1.0<a.Point):0(1.0>=a.Point):0-1.#R, -1.#QO, -1.#QNB, -1.#QNAN (%.2~5f 로 출력했을 때의 결과)
사례 – 메모리 침범classCTest{public:conststaticintMAX_DATA=10;intm_Data[MAX_DATA];vector<int>m_Nums;};int_tmain(intargc,_TCHAR*argv[]){CTestt;t.m_Nums.push_back(1);ZeroMemory(t.m_Data,sizeof(int)*CTest::MAX_DATA+10);t.m_Nums.push_back(1);// Crashreturn0;}#include <boost/array.hpp>int_tmain(intargc,_TCHAR*argv[]){intcArray[256];boost::array<int,5>aTest={1,2,3,4 ,5};boost::array<int,256>aArray;cArray[256]=0;// not always dieaArray[256]=0;// always die// Assertion failed: i < N && "out of range", file , line 91return0;}
여러 가지가 정보다에러 로그가 찍히는 주기CPU 가 튀는 주기로그에 찍힌 캐릭터의 위치와 시간이렇게 빨리 이동했다는 것은?개발자에게 GM 용 캐릭터를!
사례 – 언제 문제가 발생했나?특정 시각? 특정 요일? CPU 가 튀는 주기는?게임 서버가 새벽 2시만 되면 죽었던 이유는?Windows Update 가 새벽 2시에 실행바이러스 update 창이 200 개 이상 오픈같은 port 를 bind 계속하면 port 차단서버관리툴이 실행될 때마다 특정 port 를 bind 하게 설계일본 IDC 에서 꼭 설치하게 하는 패킷 필터링 시스템의 DDOS, Garbage Attack 탐지기가 해당 port 를 차단얼마만에 죽는가특정 tick 에 죽는 잘못된 코드와 memory leakPrintf대신 사용한 로그 파일이 엄청나게 커짐Memory leak은 아니지만, 너무 많은 메모리 할당결국 일본 찾아가서 해결
재현진단수정 및 반영
가와사키(Kawasaki) 이야기Kawasaki 의 제트 스키
가와사키(Kawasaki) 이야기Kawasaki 의 제트 스키
가와사키(Kawasaki) 이야기Kawasaki 의 제트 스키새로 나온 제트 스키
해결책에는 여러 가지가 있다(있어야 한다)펫, 소환수 자동 소환09.09.09 라이브 업데이트펫인벤 아이템을 언제 주인에게 옮겨줄 것인가?진짜 옮겨야 하나?
로그신중하게 남긴다너무 많은 로그는 노이즈+ 에러다[NO_ERROR] 교훈모든 로그는 unique 해야 한다하다못해 __FILE__, __LINE__ 만이라도필요한 정보를 전부 남긴다필요하면 id 와 item 클래스 아이디,심지어 캐릭터 이름 같은 경우 문자열로 남긴다해외의 경우, 로그 접근을 직접 할 수 없거나 대응이 너무 느릴 수 있으므로 특히 주의
버그 기록 및 공유버그 트래킹 툴에 FIX 할 때, 실제로 어떤 코드를 왜, 어떻게 고쳤는지 정보를 남긴다누구보다 내가 나중에 그 정보를 필요로 하게 된다일일회의 할 때 실수를 용기있게 공유한다고참 개발자일수록 먼저일일회의 내용을 각자 위키에 써 놓으면, 코드 변경 히스토리에서 버그를 만든 날짜에 내가 뭘 하려고 했는지를 알 수 있다해결한 문제에 대해, 원인-해결책을 시간, 문제별로 정리해 놓으면, 몇 달 후 해외에서 같은 문제가 생겼을 때 빨리 해결할 수 있다.
좋은 코드 작성하기voidTest1(CTest*p){// do something}voidTest2(CTest&t){// do something}
voidTest1(CTest*p){if(p){Test1_1(p);}else{// do something}}voidTest1_1(CTest*p){if(p){// do something}}voidTest2(CTest&t){Test2_1(t);}voidTest2_1(CTest&t){// do something}
Source:  Coverity White Paper
버그의 비용Source:  Coverity White Paper
버그 미리 찾기_ASSERT !!Magic bit 사용int 를 int64 로 바꾸면서 magic bit 끼워넣기특정 bit 가 1 이 아니면 crash정기적인 코드 리뷰코드리뷰 별 거 아님정적 분석툴과 CI 연동일일빌드(TeamCity, CruiseControl.Net)Code Analysispc-lintPMD Copy/Paste Detector
Code Analysis Buffer Overrun 경고, 배열 인덱스 체크 기능
 포인터에 대한 사용 주의 경고, 중복된 변수 선언
 묵시적 Type Casting, sprintf 의 인자 개수 체크
단 x64 는지원하지 않으므로 편법을 좀 써야 함longlData[10];sValue.Format("%d",lData[10]);warningC6201:Index'10'isoutofvalidindexrange'0'to'9'forpossiblystackallocatedbuffer'lData¡¯voidCTest::DrawData(CDC*pDC){ASSERT(pDC);pDC->FillSolidRect(rect,RGB(255,0,0));}CRectrcTmp;for(...){CRectrcTmp;}
PMD Copy/Paste Detector
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
단위 테스트
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
단위 테스트 적용 사례사례 - 활 쏘기활은 레이저 빔이 아니다활은 포물선을 그리며 날아간다그럼 이런 경우는?
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
해결방법structTestBowData {	Pos from; Pos to; bool expect;};TestBowData data[] = {	{ {10, 20, 10}, {100, 40, 50}, true},  // 성공해야 함	{ {334, 13, 314}, {525, 11, 42}, false},  // 실패해야 함	….};for (inti = 0; i < dataSize; ++i) {TestBowData& d = data[i];	_ASSERT(d.expect == TestCanShoot(d.from, d.to));}
단위테스트간단한 팁
간단한 Mock 만들기classCTest{protected:intm_Test;voidTest(){}};structCMockTest:publicCTest{public:usingCTest::m_Test;// 부모 클래스의 멤버를 public 으로 쓰겠다.usingCTest::Test;};int_tmain(intargc,_TCHAR*argv[]){CTesta;//a.m_Test = 1; // protected 멤버 변수 접근할 수 없음.//a.Test();	// protected 멤버 함수 접근할 수 없음.CMockTest*pMockTest=(CMockTest*)(&a);pMockTest->m_Test=1;// CMockTest로 강제 캐스팅하면 접근할 수 있음.pMockTest->Test();// a 가 CMockTest객체가 아니어도 이렇게 쓸 수 있다는 점에 주의return0;}
Memory Leak Detector#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)FILE*g_hfileLog=NULL;intAllocHook(intnAllocType,void*pvData,size_tnSize,intnBlockUse,longlRequest,constunsignedchar*szFileName,intnLine){staticsize_tsizeAlloc=0;_CrtMemBlockHeader*pHead;if(nBlockUse==_CRT_BLOCK)// alloced by c libreturntrue;switch(nAllocType){case_HOOK_ALLOC:sizeAlloc+=nSize;fprintf(g_hfileLog,"ALLOC%d",sizeAlloc);break;case_HOOK_REALLOC:break;case_HOOK_FREE:pHead=pHdr(pvData);sizeAlloc-=pHead->nDataSize;fprintf(g_hfileLog,"FREE%d",sizeAlloc);break;}returntrue;}int_tmain(intargc,_TCHAR*argv[]){g_hfileLog=fopen("log.txt","w+");fprintf(g_hfileLog,"Start");_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);_CrtSetAllocHook(AllocHook);fclose(g_hfileLog);return0;}#include <crtdbg.h>#define nNoMansLandSize 4typedefstruct_CrtMemBlockHeader{struct_CrtMemBlockHeader*pBlockHeaderNext;struct_CrtMemBlockHeader*pBlockHeaderPrev;char*szFileName;intnLine;#ifdef _WIN64/* These items are reversed on Win64 to eliminate gaps       * in the struct and ensure that sizeof(struct)%16 == 0, * so 16-byte alignment is maintained in the debug heap.       */intnBlockUse;size_tnDataSize;#else  /* _WIN64 */size_tnDataSize;intnBlockUse;#endif/* _WIN64 */longlRequest;unsignedchargap[nNoMansLandSize];// followed by:// unsigned char           data[nDataSize];// unsigned char           anotherGap[nNoMansLandSize]; }_CrtMemBlockHeader;
Memory Leak DetectorMemory Pool 대신 new, delete 를 쓰게 만드는 flag 를 하나 둘 것이러면 LeakDiag, gflags, UMDH 로 버그 찾기도 훨씬 쉽다
단위테스트와업무 효율
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
개발자측정에는쓰지 말 것
결론버그 찾는 법을 수련하자어떻게든 재현하자작은 버그도 무시하지 말자쓰고 있는 툴을 최대한 활용하자해결책을 최소 2 개 이상 생각해보자버그를 미리 막을 방법을 만들어보자단위테스트를 활용하자서로의 경험을 공유하자
Q/A
Reference

More Related Content

온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010

  • 1. 온라인 게임에서사례로 살펴보는 DebuggingNDC 2010NCsoft 박일http://parkpd.egloos.comtwitter:rigmania
  • 3. 디버깅과 섹스의 공통점잘하면 재미있지만, 잘못하면 짜증난다.하면 할 수록 늘지만, 너무 자주 하는 건 싫다.밤늦게 할 때가 많다.대놓고 얘기하진 않지만 남들은 어떻게 하는지 궁금하다.교제가 거의 없다.과정은 신경쓰지 않고, 어서 결론에 도달하려고 한다.도구를 사용하면 도움은 되지만, 결국 사람이 해야 한다.술을 많이 마시고 하면 다음날 어떻게 했는지 기억이 나지 않는다.끝나고 나면 허무하고 담배가 땡긴다.급할 때는 더 안 된다.
  • 5. 왜 재현부터?버그가 나타날 때의 프로그램 상태를 볼 수 있다.버그를 수정했을 때, 정말 수정했는지를 증명할 수 있다.QA팀이 재현 가능해야 한다.
  • 6. 환경 만들기재현할 때는 문제가 되는 환경을 전부 똑같게설정 파일, 이벤트 파일 확인, 하드웨어, OS 버전, 보안 프로그램 동작 여부택배로 컴퓨터를 받아오기해외에 설정 파일을 암호화해서 보내기(3개월 전의) 해외 버전을 국내에도 유지하면 회귀 테스트 하기가 쉽다이번 업데이트 이후 바뀐 것인지 테스트 가능
  • 7. 사례 - OpenMP한 대의 컴퓨터에서만 에러 발생알고 보니 특정 머신(하퍼타운4 cores)에서만 에러 발생. 이유는?실제로 코드가 thread-safe 하지 않았음제대로 MT 를 지원하는 머신몰랐던 이유는?Core2 Duo 에서는 멀쩡함. 버그?하퍼타운을 쓰는 다른 개발자는 설정에서 omp사용을 빼 놨기 때문에 에러 없었음
  • 8. 사례 보고!현상은 원인이 있기 때문에 나타난다정상적인 재현이 불가능하면, 억지로라도 재현한다버그 report 를 그대로 믿지 마라QA 에서도 아는 선에서 리포트를 쓴다QA 분께 개인용 개발서버로 접속해서재현해 달라고 부탁한다msn 과 사내전화 활용
  • 9. 사례 – 희귀 아이템 경매라이브 서버가 아닌 테스트 서버, 그 중 한 npc에서만 발생재현도안 되어서, 별 문제 없겠지 생각하고 무시업데이트 후에 전 라이브 서버에서 문제 발생 이유 : 라이브 서버에서는 처음에 개발자가 직접 seed 값을 넣어줘서 경매에 문제 없었지만, 테스트 서버의 경우 개발자가 여러 npc중 한 npc만 까먹고 seed 값을 넣어주지 않았기 때문에 계속 문제가 있었음처음부터 문제가 있었는데도, QA 에서는 ‘잘 되다가 저번 주부터 문제가 생겼음’ 이라고 보고했고, 개발팀도 QA 만 믿고특이한 사례보고라고 생각함대규모 업데이트를 위해 모든 seed 값을 초기화했더니, 테스트 서버와 동일한 문제가 모든 npc에서 발생[사례보고] 라는 제목 때문에 선입관 생김
  • 11. 사례 – 공성과 결자해지voidLoadData(){// do something¡¦CCastlec(nCastleNum);// dead code???return;}
  • 12. 사례 – 공성과 결자해지voidLoadData(){// do something¡¦CCastlec(nCastleNum);// dead code? NO!!!return;}classCCastle{public:CCastle(intn){g_CastleDB.Register(this);}};
  • 14. 사례 – 기계는 믿을 수 있는가특정 머신, CPU, 그래픽카드, NIC
  • 16. 버그 찾기dump 로 Crash 난 위치 확인최근에 고친 파일부터 diff이상현상이 생긴 시간대 근처 에러로그 확인내가 고친 코드를 먼저 의심다른 사람과 얘기해보자피규어 디버깅어셈블리는 마지막에서버라면 실제 라이브 서버를 터미널 서비스로 들어가 작업관리자로 보기
  • 17. Everybody liesfalse positive(거짓양성)예전 바이너리를 쓰고 있다던가false negative(거짓음성)QA가 고쳐지지 않은 걸 고쳐졌다고 한다플라시보 효과예전부터 있던 코드도 100% 믿지 말자모든 걸 의심하자
  • 18. Dump
  • 20. minidump출력Crash 이메일 전송개발망에서도개발망email 로 전송소스 컨트롤러에 날짜_Label 로 폴더 만든 후 바이너리, pdb를 저장Symbol 서버 구축하기장,단점
  • 25. Base Line지금 상태는 정상인가?평소 CPU 는 몇 % 정도였나?의도대로 최적화가 되었나?
  • 26. Performance CountersCounter 를 log 나 DB 에 주기적으로 저장해 base line 를 만든다counter 예시SQL Server: SQL Statistics: Batch Requests/sec초당 요청 받은 SQL 배치 요청 수SQL Server: Buffer Manager: Buffer Cache Hit Ratio90 이상이면 좋음SQL Server: Locks: Lock Waits/sec잠금대기요청수Process: Page Faults/sec프로세스가 Cache Hit하지 않은 페이지수
  • 29. Visualizer in C++autoexp.dat으로 watch 창 변경C:rogram Filesicrosoft Visual Studio 8ommon7ackagesebuggerutoexp.dat[AutoExpand]CMyData =head=<m_Head> m_Tail=<m_Tail,x> name=<m_Name,su>classCMyData{public:CMyData(inth,intt):m_Head(h),m_Tail(t){}intm_Head;charm_Name[1024];intm_Tail;};int_tmain(intargc,_TCHAR*argv[]){typedefvector<CMyData*>DataList;DataListdata;for(inti=0;i<10;++i){data.push_back(newCMyData(i,i+1));}return0;}
  • 30. Debug Windows 1{[function],[source],[module]} 뒤에location, variable_name, expression특정 함수의 static local 변수 보기void Test() { static intsLocalNum = 0; } : {Test,,}sLocalNum특정 dll의 전역변수 : {,,foobar.dll}g_pMyStructBP 걸어놓고 함수 실행하기classCTest{public:staticCTest&Inst(){staticCTests;returns;}voidCheckValidate(){OutputDebugString(L"T");m_Test.push_back(1);}std::vector<int>m_Test;};int_tmain(){CTest::Inst().CheckValidate();return0;}
  • 31. Debug Windows 2Pseudo Register@eax : 리턴값@err : GetLastError@HANDLES현재 프로세스의핸들 갯수@ebp : 지역 변수시작 지점@esp : 최상위 스택
  • 32. 사례 – BotvoidCPlayer::OnItemExchange(intnItemCount1,BYTE*pData1,intnItemCount2,BYTE*pData2) {for(inti=0;i<nItemCount1;++i){// do something}for(inti=0;i<nItemCount2;++i){// do something}}
  • 33. Fuzzing Test임의의 입력을 만들어 주는 Fuzzer 사용Peach Fuzzing Platformhttp://peachfuzzer.com/generation 과 mutation 으로 입력값 생성RFuzzhttp://rfuzz.rubyforge.org일종의 monkey test
  • 36. structCData{static const intHALF_NUM=0x7fffffff;static const intMAX_NUM=0xffffffff;CData():m_dPoint(1.0){m_dData[0]=1.3; m_dData[1]=1.5;m_nData[0]=HALF_NUM; m_nData[1]=MAX_NUM;}voidApplyBonus(intnBonusIndex){m_dPoint*=m_dData[nBonusIndex];}doublem_dPoint;doublem_dData[2];unsignedlongm_nData[2];};int_tmain(intargc,_TCHAR*argv[]){CDataa;a.ApplyBonus(2);wcout<<L"isnan : "<<_isnan(a.m_dPoint)<<L'';wcout<<L"(1.0 < a.Point) : "<<(1.0<a.m_dPoint)<<L'';wcout<<L"(1.0 >= a.Point) : "<<(1.0>=a.m_dPoint)<<L'';return0;}// outputisnan:1(1.0<a.Point):0(1.0>=a.Point):0-1.#R, -1.#QO, -1.#QNB, -1.#QNAN (%.2~5f 로 출력했을 때의 결과)
  • 37. 사례 – 메모리 침범classCTest{public:conststaticintMAX_DATA=10;intm_Data[MAX_DATA];vector<int>m_Nums;};int_tmain(intargc,_TCHAR*argv[]){CTestt;t.m_Nums.push_back(1);ZeroMemory(t.m_Data,sizeof(int)*CTest::MAX_DATA+10);t.m_Nums.push_back(1);// Crashreturn0;}#include <boost/array.hpp>int_tmain(intargc,_TCHAR*argv[]){intcArray[256];boost::array<int,5>aTest={1,2,3,4 ,5};boost::array<int,256>aArray;cArray[256]=0;// not always dieaArray[256]=0;// always die// Assertion failed: i < N && "out of range", file , line 91return0;}
  • 38. 여러 가지가 정보다에러 로그가 찍히는 주기CPU 가 튀는 주기로그에 찍힌 캐릭터의 위치와 시간이렇게 빨리 이동했다는 것은?개발자에게 GM 용 캐릭터를!
  • 39. 사례 – 언제 문제가 발생했나?특정 시각? 특정 요일? CPU 가 튀는 주기는?게임 서버가 새벽 2시만 되면 죽었던 이유는?Windows Update 가 새벽 2시에 실행바이러스 update 창이 200 개 이상 오픈같은 port 를 bind 계속하면 port 차단서버관리툴이 실행될 때마다 특정 port 를 bind 하게 설계일본 IDC 에서 꼭 설치하게 하는 패킷 필터링 시스템의 DDOS, Garbage Attack 탐지기가 해당 port 를 차단얼마만에 죽는가특정 tick 에 죽는 잘못된 코드와 memory leakPrintf대신 사용한 로그 파일이 엄청나게 커짐Memory leak은 아니지만, 너무 많은 메모리 할당결국 일본 찾아가서 해결
  • 43. 가와사키(Kawasaki) 이야기Kawasaki 의 제트 스키새로 나온 제트 스키
  • 44. 해결책에는 여러 가지가 있다(있어야 한다)펫, 소환수 자동 소환09.09.09 라이브 업데이트펫인벤 아이템을 언제 주인에게 옮겨줄 것인가?진짜 옮겨야 하나?
  • 45. 로그신중하게 남긴다너무 많은 로그는 노이즈+ 에러다[NO_ERROR] 교훈모든 로그는 unique 해야 한다하다못해 __FILE__, __LINE__ 만이라도필요한 정보를 전부 남긴다필요하면 id 와 item 클래스 아이디,심지어 캐릭터 이름 같은 경우 문자열로 남긴다해외의 경우, 로그 접근을 직접 할 수 없거나 대응이 너무 느릴 수 있으므로 특히 주의
  • 46. 버그 기록 및 공유버그 트래킹 툴에 FIX 할 때, 실제로 어떤 코드를 왜, 어떻게 고쳤는지 정보를 남긴다누구보다 내가 나중에 그 정보를 필요로 하게 된다일일회의 할 때 실수를 용기있게 공유한다고참 개발자일수록 먼저일일회의 내용을 각자 위키에 써 놓으면, 코드 변경 히스토리에서 버그를 만든 날짜에 내가 뭘 하려고 했는지를 알 수 있다해결한 문제에 대해, 원인-해결책을 시간, 문제별로 정리해 놓으면, 몇 달 후 해외에서 같은 문제가 생겼을 때 빨리 해결할 수 있다.
  • 47. 좋은 코드 작성하기voidTest1(CTest*p){// do something}voidTest2(CTest&t){// do something}
  • 48. voidTest1(CTest*p){if(p){Test1_1(p);}else{// do something}}voidTest1_1(CTest*p){if(p){// do something}}voidTest2(CTest&t){Test2_1(t);}voidTest2_1(CTest&t){// do something}
  • 49. Source: Coverity White Paper
  • 50. 버그의 비용Source: Coverity White Paper
  • 51. 버그 미리 찾기_ASSERT !!Magic bit 사용int 를 int64 로 바꾸면서 magic bit 끼워넣기특정 bit 가 1 이 아니면 crash정기적인 코드 리뷰코드리뷰 별 거 아님정적 분석툴과 CI 연동일일빌드(TeamCity, CruiseControl.Net)Code Analysispc-lintPMD Copy/Paste Detector
  • 52. Code Analysis Buffer Overrun 경고, 배열 인덱스 체크 기능
  • 53. 포인터에 대한 사용 주의 경고, 중복된 변수 선언
  • 54. 묵시적 Type Casting, sprintf 의 인자 개수 체크
  • 55. 단 x64 는지원하지 않으므로 편법을 좀 써야 함longlData[10];sValue.Format("%d",lData[10]);warningC6201:Index'10'isoutofvalidindexrange'0'to'9'forpossiblystackallocatedbuffer'lData¡¯voidCTest::DrawData(CDC*pDC){ASSERT(pDC);pDC->FillSolidRect(rect,RGB(255,0,0));}CRectrcTmp;for(...){CRectrcTmp;}
  • 60. 단위 테스트 적용 사례사례 - 활 쏘기활은 레이저 빔이 아니다활은 포물선을 그리며 날아간다그럼 이런 경우는?
  • 65. 해결방법structTestBowData { Pos from; Pos to; bool expect;};TestBowData data[] = { { {10, 20, 10}, {100, 40, 50}, true}, // 성공해야 함 { {334, 13, 314}, {525, 11, 42}, false}, // 실패해야 함 ….};for (inti = 0; i < dataSize; ++i) {TestBowData& d = data[i]; _ASSERT(d.expect == TestCanShoot(d.from, d.to));}
  • 67. 간단한 Mock 만들기classCTest{protected:intm_Test;voidTest(){}};structCMockTest:publicCTest{public:usingCTest::m_Test;// 부모 클래스의 멤버를 public 으로 쓰겠다.usingCTest::Test;};int_tmain(intargc,_TCHAR*argv[]){CTesta;//a.m_Test = 1; // protected 멤버 변수 접근할 수 없음.//a.Test(); // protected 멤버 함수 접근할 수 없음.CMockTest*pMockTest=(CMockTest*)(&a);pMockTest->m_Test=1;// CMockTest로 강제 캐스팅하면 접근할 수 있음.pMockTest->Test();// a 가 CMockTest객체가 아니어도 이렇게 쓸 수 있다는 점에 주의return0;}
  • 68. Memory Leak Detector#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)FILE*g_hfileLog=NULL;intAllocHook(intnAllocType,void*pvData,size_tnSize,intnBlockUse,longlRequest,constunsignedchar*szFileName,intnLine){staticsize_tsizeAlloc=0;_CrtMemBlockHeader*pHead;if(nBlockUse==_CRT_BLOCK)// alloced by c libreturntrue;switch(nAllocType){case_HOOK_ALLOC:sizeAlloc+=nSize;fprintf(g_hfileLog,"ALLOC%d",sizeAlloc);break;case_HOOK_REALLOC:break;case_HOOK_FREE:pHead=pHdr(pvData);sizeAlloc-=pHead->nDataSize;fprintf(g_hfileLog,"FREE%d",sizeAlloc);break;}returntrue;}int_tmain(intargc,_TCHAR*argv[]){g_hfileLog=fopen("log.txt","w+");fprintf(g_hfileLog,"Start");_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);_CrtSetAllocHook(AllocHook);fclose(g_hfileLog);return0;}#include <crtdbg.h>#define nNoMansLandSize 4typedefstruct_CrtMemBlockHeader{struct_CrtMemBlockHeader*pBlockHeaderNext;struct_CrtMemBlockHeader*pBlockHeaderPrev;char*szFileName;intnLine;#ifdef _WIN64/* These items are reversed on Win64 to eliminate gaps * in the struct and ensure that sizeof(struct)%16 == 0, * so 16-byte alignment is maintained in the debug heap. */intnBlockUse;size_tnDataSize;#else /* _WIN64 */size_tnDataSize;intnBlockUse;#endif/* _WIN64 */longlRequest;unsignedchargap[nNoMansLandSize];// followed by:// unsigned char data[nDataSize];// unsigned char anotherGap[nNoMansLandSize]; }_CrtMemBlockHeader;
  • 69. Memory Leak DetectorMemory Pool 대신 new, delete 를 쓰게 만드는 flag 를 하나 둘 것이러면 LeakDiag, gflags, UMDH 로 버그 찾기도 훨씬 쉽다
  • 73. 결론버그 찾는 법을 수련하자어떻게든 재현하자작은 버그도 무시하지 말자쓰고 있는 툴을 최대한 활용하자해결책을 최소 2 개 이상 생각해보자버그를 미리 막을 방법을 만들어보자단위테스트를 활용하자서로의 경험을 공유하자
  • 74. Q/A
  • 76. 버그 정의디버깅 .NET 응용 프로그램가와사키애자일의 미래(http://agile.egloos.com/3182427)미니 덤프http://hhko.egloos.com/891853크래시 덤프 분석기http://blog.maiet.net/xe/4596성능 카운터http://serious-code.net/moin.cgi/WindowsPerformanceMonitoringVisualizerhttp://minjang.egloos.com/468834Stack 깨졌다면실전 윈도우 디버깅 5장 p.235NaN(Not a Number)http://msdn.microsoft.com/en-us/library/w22adx1s%28VS.80%29.aspx
  • 77. 편작 이야기http://www.m-letter.or.krhttp://blog.daum.net/suknyangdari/1810Code Analysishttp://eslife.tistory.com/entry/Visual-Studio-2005%EC%9D%98-Code-Analysis-%EA%B8%B0%EB%8A%A5http://whiteapple.textcube.com/224펫, 소환수 자동 소환http://www.playforum.net/lineage2/board.comm?action=read&iid=10032291&pageNo=0&num=16102http://www.playforum.net/lineage2/board.comm?action=read&iid=10032298&pageNo=0&num=14443Memory Leak Detectorhttp://cozyhouse.tistory.com/entry/Win32%EC%97%90%EC%84%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%82%AC%EC%9A%A9%EB%9F%89-%EC%B8%A1%EC%A0%95%EB%B0%A9%EB%B2%95버그 종류http://mschnlnine.vo.llnwd.net/d1/pdc08/PPTX/PRECONF/PRE02.pptx