SlideShare uma empresa Scribd logo
1 de 91
온라인 게임에서사례로 살펴보는디버깅 KGC 2009 박일 리니지2 서버팀 parkpd.egloos.com
저를 소개합니다 박일 2006 년 초 리니지2 서버팀으로 이동 The Chaotic Chronicle – Oath of Blood (Chronicle 5) 부터 credit 에 제 이름을 볼 수 있습니다 블로그 : http://parkpd.egloos.com
경고:발표에서 사용된 사례들은대부분 실제사례를근거로 하여재구성된 것들입니다.
왜 디버깅인가?
버그? 충돌과 응답없음, 성능과 측정 가능성의 저하 부정확한 결과, 보안 취약점, 일관성 없는 사용자 인터페이스 요구 사항 불충족
디버깅을 잘 하는 방법 배우기 나와 다른 사람의 문제 해결 과정을 관찰 내성법 “문제가 무엇이었나” 보다,“어떻게 문제를 찾았나” 가 더 중요 의사보다 유리한 점 프로세스를 여러 번 다양하게 죽여 볼 수 있다 웃기는 점 디버깅이 오래 걸릴 수록, 고마워한다
버그 찾기 버그 재현 버그 분석 해결책 결정 및 예방
버그 찾기 dump 로 Crash 난 위치 확인 최근에 고친 파일부터 diff 좋은 diff 툴, 일일빌드 이상현상이 생긴 시간대 근처 에러로그 확인 내가 고친 코드를 먼저 의심 다른 사람과 얘기해보자(Bug Talk) 디버깅은 근성이다 도저히 모르겠다 싶을 때 1시간만 더 보자 어셈블리는 항상 가장 마지막에 서버라면 실제 서버를 터미널 서비스로 들어가 작업관리자를 보자
Everybody lies false positive(거짓양성) 예전 바이너리를 쓰고 있다던가 false negative(거짓음성) QA 가 고쳐지지도 않은 걸 고쳐졌다고 하거나 플라시보 효과 예전부터 있던 코드도 100% 믿지 말자
// Dump 보기 실습 typedefBOOL(WINAPI*MINIDUMPWRITEDUMP)(HANDLEhProcess,...); LONGTopExceptionFilter(LPEXCEPTION_POINTERS pExp){ LONGretval=EXCEPTION_CONTINUE_SEARCH; HMODULEhDll=NULL; hDll=::LoadLibrary(_T("DBGHELP.DLL")); if(hDll){ MINIDUMPWRITEDUMPpDump= ::GetProcAddress(hDll,"MiniDumpWriteDump"); if(pDump){ HANDLEhFile=::CreateFile(_T("Crash.dmp"),..); if(INVALID_HANDLE_VALUE!=hFile){ _MINIDUMP_EXCEPTION_INFORMATIONExInfo; ExInfo.ThreadId=::GetCurrentThreadId(); ExInfo.ExceptionPointers=pExp; ExInfo.ClientPointers=NULL; BOOLbOK=pDump( GetCurrentProcess(),GetCurrentProcessId(), hFile,MiniDumpNormal,&ExInfo,NULL,NULL); if(bOK){ tcout<<" dump to Crash.dmp"<<endl; retval=EXCEPTION_EXECUTE_HANDLER; } ::CloseHandle(hFile); } } ::FreeLibrary(hDll); } returnretval; } int_tmain(intargc,_TCHAR*argv[]){ SetUnhandledExceptionFilter(TopExceptionFilter); TCHAR*p=NULL; p[1]=_T('M'); return0; }
minidump를 출력하기 .dmp파일을 만드는 것 보다 장점은? 이메일로 전송 Label, 빌드별 바이너리, pdb를 SVC 에 저장 라이브용 바이너리 만든 후, 이메일로 label 공유
Base Line
Base Line 지금 상태는 정상인가? 평소 CPU 는 몇 % 정도였나? 의도대로 최적화가 되었나?
Performance Counters void start(constchar*pszCounter){ PdhOpenQuery(NULL,0,&hQuery); PdhValidatePath(pszCounter); PdhAddCounter(hQuery,pszCounter,0,&hCounter); } longcurrent()const{ PdhCollectQueryData(hQuery); PDH_FMT_COUNTERVALUEvalue; PdhGetFormattedCounterValue(hCounter,PDH_FMT_LONG,NULL,&value); returnvalue.longValue; } int_tmain(intargc,_TCHAR*argv[]){ Perfmoncpu("cpu.csv"); Perfmonmem("mem.csv"); cpu.start("Processor(_Total)% Processor Time"); mem.start("MemoryPool Nonpaged Bytes"); for(inti=0;i<3;++i){ Sleep(1000); printf("CPU load: %d%, Nonpaged bytes: %d KB",cpu.current(),mem.current()/1024); } } CPUload:99,Nonpagedbytes:22408KB CPUload:0,Nonpagedbytes:22500KB CPUload:3,Nonpagedbytes:22400KB
Performance Counters base line 만들기 Counter 를 log 나 DB 에 주기적으로 저장 counter 예시 SQL Server: SQL Statistics: Batch Requests/sec 초당 요청 받은 SQL 배치 요청 수 SQL Server: Buffer Manager: Buffer Cache Hit Ratio 90 이상이어야 함 SQL Server: Locks: Lock Waits/sec 잠금대기요청수 Process: Page Faults/sec 프로세스가 Cache Hit하지 않은 페이지수
사례 – 특정 머신 Tomcat 서버가 죽었음 JVM의 다운으로 인해서 발생되었으며죽은 시점은 GC 과정 중이었음 Log4j를 통한 로그 모니터링 결과에는 죽기 전 후 특이 사항 발견되지 않음
JVM 에러로그 # An unexpected error has been detected by HotSpot Virtual Machine: #  Internal Error (50532D41524B33574545502445434F5241544F520E4350500024), pid=27607, tid=1828866976 # Java VM: Java HotSpot(TM) Server VM (1.5.0_12-b04 mixed mode) ---------------  T H R E A D  --------------- Current thread (0x080ffe48):  VMThread [id=27611] Stack: [0x6cfa4000,0x6d025000),  sp=0x6d023ae0,  free space=510k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) V  [libjvm.so+0x512633] V  [libjvm.so+0x1c862c] V  [libjvm.so+0x51a3f0] V  [libjvm.so+0x438228] C  [libpthread.so.0+0x53cc] VM_Operation (0x66f6ae40): parallel gc failed allocation,mode: safepoint, requested by thread 0x6bbcf7d0 Heap PSYoungGen      total 17216K, used 696K [0xaa370000, 0xabc90000, 0xb1530000) eden space 16512K, 0% used [0xaa370000,0xaa370000,0xab390000)   from space 704K, 98% used [0xab3d0000,0xab47e1d0,0xab480000)   to   space 4608K, 0% used [0xab810000,0xab810000,0xabc90000) PSOldGen        total 629440K, used 627294K [0x71530000, 0x97be0000, 0xaa370000)   object space 629440K, 99% used [0x71530000,0x979c7b48,0x97be0000) PSPermGen       total 37888K, used 37271K [0x6d530000, 0x6fa30000, 0x71530000)   object space 37888K, 98% used [0x6d530000,0x6f995ed0,0x6fa30000)
다행인지 불행인지 동일한 문제가 터짐 ,[object Object]
하드웨어에 빨간 불이 들어오고 있었음,[object Object]
사례 – 언제 문제가 발생했나? 특정 시각? 특정 요일? CPU 가 튀는 주기는? 게임 서버가 새벽 2시만 되면 죽었던 이유는? Windows Update 가 새벽 2시에 실행 바이러스 update 창이 200 개 이상 오픈 같은 port 를 bind 계속하면 port 차단 서버관리툴이 실행될 때마다 특정 port 를 bind 하게 설계 일본 IDC 에서 꼭 설치하게 하는 패킷 필터링 시스템의 DDOS, Garbage Attack 탐지기가 해당 port 를 차단 얼마만에 죽는가 특정 tick 에 죽는 잘못된 코드와 memory leak Printf대신 사용한 로그 파일이 엄청나게 커짐 Memory leak은 아니지만, 너무 많은 메모리 할당 2G 는 절대 큰 용량이 아님 결국 일본 찾아가서 해결
버그 찾기 버그 재현 버그 분석 해결책 결정 및 예방
무조건 재현! 재현! 현상은 원인이 있기 때문에 나타난다.  정상적인 재현이 불가능하면, 억지로라도 재현해라. 재현 할 때는 BP 걸어놓고 한 step 씩 이동하면서 확인 재현할 때는 문제가 되는 현상을 데이터까지 똑같게 택배로 컴퓨터를 아예 받아와라.  고친 게 하나도 없어도, 환경만 바뀌면 버그 발생 가능 설정 파일, 하드웨어, OS 버전, 보안 프로그램 동작 여부 버그 report 를 그대로 믿지 마라  QA 에서도 아는 선 안에서만 리포트를 쓴다 QA 분들에게 내 개발서버로 접속해서 재현해 달라고 부탁 해외 지사에서 보낸 버그 리포트는 특히 심하다
사례 - 경매 라이브 서버가 아닌 테스트 서버, 그 중 한 npc에서만 발생 재현도안 되어서, 별 문제 없겠지 생각하고 무시 업데이트 후에 전 라이브 서버에서 문제 발생  이유 : 라이브 서버에서는 처음에 개발자가 직접 seed 값을 넣어줘서 경매에 문제 없었지만, 테스트 서버의 경우 개발자가 여러 npc중 한 npc만 까먹고 seed 값을 넣어주지 않았기 때문에 계속 문제가 있었음 처음부터 문제가 있었는데도, QA 에서는 ‘잘 되다가 저번 주부터 문제가 생겼음’ 이라고 보고했고, 개발팀도 QA 만 믿고특이한 사례보고라고 생각함 대규모 업데이트를 위해 모든 seed 값을 초기화했더니, 테스트 서버와 동일한 문제가 모든 npc에서 발생 결론 증상이 있다면 무시하지 말자 [사례보고] 라는 제목 때문에 선입관 생김
사례보고를무시하면한 방에훅 간다
사례 - OpenMP 수 십대의 개발 서버 중 한 서버에서만 에러 발생 알고 보니 특정 머신(하퍼타운)에서만 에러 발생. 이유는? 제대로 MT 를 지원하는 머신에서 테스트 물리 core 가 여러 대인 CPU 머신 사용하자 하퍼타운을 쓰는 다른 개발자는 설정에서 omp사용을 빼 놨기 때문에 에러나는 줄 몰랐음
사례 – 공성과 결자해지 voidLoadData(){ // do something CCastlec(nCastleNum);// dead code??? return; }
사례 – 공성과 결자해지 voidLoadData(){ // do something CCastlec(nCastleNum);// dead code? NO!!! return; } classCCastle{ public: CCastle(intnId){ g_CastleDB.Register(nId, this); } };
사례 – 토요일에만 실패하는단위테스트 요일 index 문제 GetLocalTime wDayOfWeek : The day of the week.  Sunday = 0, Monday = 1 해외 : 특정 요일에만 실패
버그 찾기 버그 재현 버그 분석 해결책 결정 및 예방
보여주기만 해도버그를찾을 수 있다
Visualizer in 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_pMyStruct 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 2 Pseudo Register @eax : 리턴값 @err : GetLastError @HANDLES 현재 프로세스의 핸들 갯수 @ebp : 지역 변수 시작 지점 @esp : 최상위 스택
사례 – Bot voidCPlayer::OnItemExchange( intnItemCount1,BYTE*pData1, intnItemCount2,BYTE*pData2) { for(inti=0;i<nItemCount1;++i){ // do something } for(inti=0;i<nItemCount2;++i){ // do something } }
사례 – hp 회복 문제 classCPlayer{ public: CPlayer():m_HP(1.0){} voidOnRegenTick(); doublem_HP; doublem_Buff[3]; }; voidCPlayer::OnRegenTick(double delta){ m_HP=m_HP * 1,05+(0.5*delta) +(10.0*0.3)/(52+m_Buff[1]) +(m_HP/m_Buff[2]*3.0)+2; }
사례 - MSVC2005 최적화 오류 classVec{ intx,y,z; Vec&operator=(constVec&v){ if(this!=&v){ x=v.x; y=v.y; z=v.z; } return*this; } }; Vecv1,v2; v1=v2; ,[object Object],Visual Studio 2005 sp1 에서 수정
BUG? (x != x) : true (x == x) : false (y > x) : false (y < x) : false
BUG? (x != x) : true (x == x) : false (y > x) : false (y < x) : false NaN(Not a Number) double answer = sqrt(-1.0);
// http://msdn.microsoft.com/en-us/library/w22adx1s%28VS.80%29.aspx classCData{ public: enum{ HALF_NUM=0x7fffffff, MAX_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; } voidGetBonus(intnBonusIndex){ m_dPoint*=m_dData[nBonusIndex]; } doublem_dPoint; doublem_dData[2]; unsignedlongm_nData[2]; }; int_tmain(intargc,_TCHAR*argv[]){ CDataa; a.GetBonus(2); wcout<<L"isnan : "<<_isnan(a.m_dPoint)<<L''; wcout<<"(1.0 < a.m_dPoint) : "<<(1.0<a.m_dPoint)<<L''; wcout<<"(1.0 >= a.m_dPoint) : "<<(1.0>=a.m_dPoint)<<L''; return0; } // output isnan:1 (1.0<a.m_dPoint):0 (1.0>=a.m_dPoint):0
사례 – 메모리 침범 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);// Crash return0; } #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 die aArray[256]=0;// always die // Assertion failed: i < N && "out of range", file , line 91 return0; }
사례 – goto 변수 선언문을 건너뛰기 #define goto WhatTheHell int_tmain(intargc,_TCHAR*argv[]){ gotoJumpToHere; intnTestValue; JumpToHere: nTestValue=4; wcout<<nTestValue; return0; }
사례 - random // ……………………….. intGetRand1(intnMin,intnMax){ intnOffset=rand()%(nMax-nMin); returnnMin+nOffset; } doubleGetRand2(doubledMin,doubledMax){ return((double)rand()/(double)RAND_MAX)*(dMax-dMin); }
사례 - random // RAND_MAX : 32767 intGetRand1(intnMin,intnMax){ intnOffset=rand()%(nMax-nMin); returnnMin+nOffset; } doubleGetRand2(doubledMin,doubledMax){ return((double)rand()/(double)RAND_MAX)*(dMax-dMin); } 0 ~ 2767 확률이 2768 ~ 9999 보다 1/32767 높다. if (Rand(1000000) <= 1) // 0.0001% 과if (Rand(100000) <= 1)  // 0.001% 의차이는 rand 를 RAND_MAX 번 호출했을 때 1이 나올 확률은 1/32767 = 0.00305% > (0.001% 과 0.0001%) 0.001% 와 0.003% 의 차이가 없음
예제 - std::sort structData{ Data(intn):m_Num(n){} intm_Num; }; boolIsLessThan(Data*a,Data*b){ returna->m_Num>b->m_Num; } typedefvector<Data*>DataList; voidtest(DataList& d){ for(inti=0;i<10;++i) d.push_back(newData(i)); sort(d.begin(),d.end(),IsLessThan);
예제 - std::sort sort(m_data.begin() …) 하는 도중에 다른 thread 에서 Num 값을 바꾸면 무한 루프가 발생할 수 있음 structData{ Data(intn):m_Num(n){} intm_Num; }; boolIsLessThan(Data*a,Data*b){ returna->m_Num>b->m_Num; } typedefvector<Data*>DataList; voidtest(DataList& d){ for(inti=0;i<10;++i) d.push_back(newData(i)); sort(d.begin(),d.end(),IsLessThan);
Symbol Server
Symbol Server
Symbol Server
버그 찾기 버그 재현 버그 분석 해결책 결정 및 예방
좋은 코드 작성하기 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 }
Example of bugs repartition Open source code Source:  Coverity White Paper
Example of bugs repartition Open source code 버그의 비용 Source:  Coverity White Paper
진짜 해결책 펫, 소환수 자동 소환 09.09.09 라이브 업데이트 펫인벤 아이템을 언제 주인에게 옮겨줄 것인가? 진짜 옮겨야 하나?
에러 로그를 적절하게 남긴다 잘남겨야 한다 copy & paste 하면서 에러 로그도 똑같이 남기면 어디에서 생긴 에러인지 알 수 없다 DDiba! __FILE__, __LINE__ 캐릭터 이름, 아이템 아이디(해외 에러 사례) 너무 많이 남기면 느려진다, 정보 노이즈 사례 : [NO_ERROR] 로그 너무 조금 남기면 필요한 정보를 찾을 수 없다
버그 미리 찾기 _ASSERT !! 정기적인 코드 리뷰 코드리뷰 별 거 아님 정적 분석툴과CI 연동 Code Analysis (자료 추가할 것) pc-lint 단위테스트 Magic bit 사용 int를 int64 로 바꾸면서 magic bit 끼워넣기(특정 bit 가 1 이 아니면 crash)
Code Analysis ,[object Object]
 포인터에 대한 사용 주의 경고, 중복된 변수 선언
 묵시적 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; }
단위 테스트활용법
단위테스트 단위테스트로 버그 재현 후 해결책을 적용해 버그가 사라지는 걸 확인 가장 많이 물어보는 질문 무엇을 단위테스트로 만들 것인가? 버그가 발생했던 문제부터 단위테스트로 재현하라 가장 쉬운 것 부터 해라 테스트를 만들기 쉽게 해라
Memory Leak Detector 1 structCItem{ CItem(){g_ItemCount++;} ~CItem(){g_ItemCount--;} }; structFixtureBase{ FixtureBase(){g_ItemCount=0;} virtual~FixtureBase(){_ASSERT(0==g_ItemCount);} }; structFixtureTest:publicFixtureBase{ FixtureTest(){ m_pPlayer=CPlayer::Create(); m_pItem=CItem::Create(); } ~FixtureTest(){ CItem::Delete(m_pItem); CPlayer::Delete(m_pPlayer); } CPlayer*m_pPlayer; CItem*m_pItem; }; TEST_FIXTURE(FixtureTest,UseItem){ m_pPlayer->Use(m_pItem); }
Memory Leak Detector 2 #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 lib returntrue; 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 4 typedefstruct_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 Detector Memory Pool 대신 new, delete 를 쓰게 만드는 flag 를 하나 둘 것 이러면 gflag 나 UMDH 로 버그 찾기도 훨씬 쉽다
간단한 Mock 만들기 classCTest{ protected: intm_Test; voidTest(){} }; classCMockTest: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; }
NewTest SUITE 특수한 조건에서의 함수 테스트에 Break Point 를 걸고 싶을 때 RunAllTests(… “NewTest”); RunAllTests(… “DefaultSuite”); TEST_FIXTURE(FixtureTest,UseItem) { m_pPlayer->Use(m_pItem); } SUITE(NewTest) { 	TEST_FIXTURE(FixtureTest,UseSpecialItem) { m_pPlayer->Use(m_pItem); 	} }
개발자측정에는쓰지 말 것
결론 버그 찾는 법을 수련하자 어떻게든 재현하자 작은 버그도 무시하지 말자 쓰고 있는 툴을 최대한 활용하자 해결책을 최소 2 개 이상 생각해보자 버그를 미리 막을 방법을 만들어보자 단위테스트를 활용하자
Q/A
그 외 사례들
사례 classCConnector{ public: ~CConnector(){Close();} voidOpen(){/*...*/} voidClose(){/*...*/} vector<int>m_TestData;// new added member }; classCVideo:publicCObj{ public: CVideo(intnId):m_nId(nId){} intm_nId; }; int_tmain(intargc,_TCHAR*argv[]){ // create CComicBook and do something { CConnectorc; CVideo*p1=CVideoManager::CreateVideo(1); if(p1){ wcout<<p1->m_nId; p1->Release(); } } } ,[object Object]
 Crash 위치 :CConnector 소멸자 -> 멤버변수 vector,[object Object]
 재현은안 되나 꾸준히 발생
 원인을 찾지 못해, 가능한 원인을 하나씩 제거하기로 결정
새로 추가된 m_TestData 제거
 Crash 는 없어진 듯 했으나1주일 후 다시 Crash 발생
 CVideo 의 멤버변수값이 이상하게 변경되는 다른 현상도 발생
CVideo 과 관련 있을까?,[object Object]
typedefCSmartPtr<CVideo>CVideoSP; classCVideoManager{ staticCVideoSPCreateVideo(intnId){ returnnewCVideo(nId); } }; int_tmain(intargc,_TCHAR*argv[]){ CVideo* p1=CVideoManager::CreateVideo(1); return0; } classCObj{ public: CObj():m_nRef(0){} virtual~CObj(){} intAddRef(){return++m_nRef;} boolRelease(){ --m_nRef; if(0==m_nRef){ //return_to_pool(this); // reason 3 deletethis; returntrue; }elseif(m_nRef<0){ //_ASSERT_EXPR(0, L"over release!");                             // reason 4 } returnfalse; } staticvoid*operatornew(size_tsize){ //void *p=allocate_from_pool(size); void*p=malloc(size); returnp; } staticvoidoperatordelete(void*p){ //_ASSERT_EXPR(0, L"don't delete CObj"); free(p); } intm_nRef; }; template<typenameT> classCSmartPtr{ public: typedefCSmartPtr<T>this_type; CSmartPtr():m_p(NULL){} //explicit CSmartPtr(T* p) : m_p(p) {} CSmartPtr(T*p):m_p(p){}// reason 1 ~CSmartPtr(){ if(m_p){ m_p->Release(); } } T&operator*()const{ return*m_p; } T*operator->()const{ returnm_p; } operatorT*(){// reason 2 returnm_p; } private: T*m_p; };
사례 int_tmain(intargc,_TCHAR*argv[]){ intnInput=0; constfloatfail=1.0f; while(1){ wcin>>nInput; for(inti=0;i<5;++i){ DWORDt=GetTickCount(); DWORDnRet=(nInput==0)?fail:t; wcout<<nRet<<L''<<t<<L''; Sleep((rand()%10)+20); } } return0; }
사례 int_tmain(intargc,_TCHAR*argv[]){ intnInput=0; constfloatfail=1.0f; while(1){ wcin>>nInput; for(inti=0;i<5;++i){ DWORDt=GetTickCount(); DWORDnRet=(nInput==0)?fail:t; wcout<<nRet<<L''<<t<<L''; Sleep((rand()%10)+20); } } return0; } 426468224   426468234 426468256   426468250 426468288   426468281 426468320   426468312 426468352   426468343
사례 – 설정 파일 분명 그 파일을 고쳤어요! 설정 파일을 암호화하고, 개발팀에서 관리
사례 : 해외 설정 Country Code : tailand
사례 : 해외 설정 Country Code : tailand > Country Code : thailand
팀 디버깅 왜 서버가 뜨는 도중에 죽을까? 각자 disassemble, 코드 히스토리 비교, 로그 비교 원인 : 운영팀에서 DB 에 직접 데이터를 insert 하는 바람에 특정 데이터의 갯수가 max 값을 넘어버려서, pass by index 문제 발생 DBA 가 발견 교훈 :  디버깅할 때는 모든 가정을 버리자 각자 전문분야를 동원해 문제를 바라보면, 같은 문제를 다양한 시각에서 바라볼 수 있다 업무를 돌아가면서 맡기
사례 – DB update pc_data set c_value = 127 delete from event_data 실제 크기 데이터로 테스트 해 보자

Mais conteúdo relacionado

Mais procurados

메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템
메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템 메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템
메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템 ByungTak Kang
 
[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규
[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규
[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규ChangKyu Song
 
[NEXT] Android Profiler
[NEXT] Android Profiler[NEXT] Android Profiler
[NEXT] Android ProfilerYoungSu Son
 
예외처리가이드
예외처리가이드예외처리가이드
예외처리가이드도형 임
 
Io t에서의 소프트웨어단위테스트_접근사례
Io t에서의 소프트웨어단위테스트_접근사례Io t에서의 소프트웨어단위테스트_접근사례
Io t에서의 소프트웨어단위테스트_접근사례SangIn Choung
 
Ryan Dahl의 Node.js 소개 동영상 해설 by doortts
Ryan Dahl의 Node.js 소개 동영상 해설 by doorttsRyan Dahl의 Node.js 소개 동영상 해설 by doortts
Ryan Dahl의 Node.js 소개 동영상 해설 by doorttsSuwon Chae
 
스프링 코어 강의 1부 - 봄 맞이 준비 운동
스프링 코어 강의 1부 - 봄 맞이 준비 운동스프링 코어 강의 1부 - 봄 맞이 준비 운동
스프링 코어 강의 1부 - 봄 맞이 준비 운동Sungchul Park
 

Mais procurados (7)

메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템
메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템 메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템
메이플스토리 사례를 통해 살펴보는 서버사이드 봇/핵 탐지 시스템
 
[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규
[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규
[NDC10] Unity Build 로 빌드타임 반토막내기 - 송창규
 
[NEXT] Android Profiler
[NEXT] Android Profiler[NEXT] Android Profiler
[NEXT] Android Profiler
 
예외처리가이드
예외처리가이드예외처리가이드
예외처리가이드
 
Io t에서의 소프트웨어단위테스트_접근사례
Io t에서의 소프트웨어단위테스트_접근사례Io t에서의 소프트웨어단위테스트_접근사례
Io t에서의 소프트웨어단위테스트_접근사례
 
Ryan Dahl의 Node.js 소개 동영상 해설 by doortts
Ryan Dahl의 Node.js 소개 동영상 해설 by doorttsRyan Dahl의 Node.js 소개 동영상 해설 by doortts
Ryan Dahl의 Node.js 소개 동영상 해설 by doortts
 
스프링 코어 강의 1부 - 봄 맞이 준비 운동
스프링 코어 강의 1부 - 봄 맞이 준비 운동스프링 코어 강의 1부 - 봄 맞이 준비 운동
스프링 코어 강의 1부 - 봄 맞이 준비 운동
 

Destaque

Programming Game AI by Example. Ch7. Raven
Programming Game AI by Example. Ch7. RavenProgramming Game AI by Example. Ch7. Raven
Programming Game AI by Example. Ch7. RavenRyan Park
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10Ryan Park
 
즉흥연기와프로그래밍
즉흥연기와프로그래밍즉흥연기와프로그래밍
즉흥연기와프로그래밍Ryan Park
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.Ryan Park
 
AIbyExample - Ch7 raven. version 0.8
AIbyExample - Ch7 raven. version 0.8AIbyExample - Ch7 raven. version 0.8
AIbyExample - Ch7 raven. version 0.8Ryan Park
 
나도기술서번역한번해볼까 in NDC10
나도기술서번역한번해볼까 in NDC10나도기술서번역한번해볼까 in NDC10
나도기술서번역한번해볼까 in NDC10Ryan Park
 
나도(기술서)번역한번해볼까
나도(기술서)번역한번해볼까나도(기술서)번역한번해볼까
나도(기술서)번역한번해볼까Ryan Park
 
Unicode 이해하기
Unicode 이해하기Unicode 이해하기
Unicode 이해하기Ryan Park
 
위대한 게임개발팀의 공통점
위대한 게임개발팀의 공통점위대한 게임개발팀의 공통점
위대한 게임개발팀의 공통점Ryan Park
 
문자셋과 인코딩
문자셋과 인코딩문자셋과 인코딩
문자셋과 인코딩Jaehoon Jung
 
Domain Driven Design Ch7
Domain Driven Design Ch7Domain Driven Design Ch7
Domain Driven Design Ch7Ryan Park
 
Oop design principle SOLID
Oop design principle SOLIDOop design principle SOLID
Oop design principle SOLIDRyan Park
 
Oop design principle
Oop design principleOop design principle
Oop design principleRyan Park
 
NDC14 - 사례로 배우는 디스어셈블리 디버깅
NDC14 - 사례로 배우는 디스어셈블리 디버깅NDC14 - 사례로 배우는 디스어셈블리 디버깅
NDC14 - 사례로 배우는 디스어셈블리 디버깅Seungjae Lee
 
온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기Seungjae Lee
 
KGC2010 - 낡은 코드에 단위테스트 넣기
KGC2010 - 낡은 코드에 단위테스트 넣기KGC2010 - 낡은 코드에 단위테스트 넣기
KGC2010 - 낡은 코드에 단위테스트 넣기Ryan Park
 

Destaque (20)

Programming Game AI by Example. Ch7. Raven
Programming Game AI by Example. Ch7. RavenProgramming Game AI by Example. Ch7. Raven
Programming Game AI by Example. Ch7. Raven
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
 
Taocp1 2 4
Taocp1 2 4Taocp1 2 4
Taocp1 2 4
 
즉흥연기와프로그래밍
즉흥연기와프로그래밍즉흥연기와프로그래밍
즉흥연기와프로그래밍
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.
 
Unicode
UnicodeUnicode
Unicode
 
AIbyExample - Ch7 raven. version 0.8
AIbyExample - Ch7 raven. version 0.8AIbyExample - Ch7 raven. version 0.8
AIbyExample - Ch7 raven. version 0.8
 
Unicode
UnicodeUnicode
Unicode
 
나도기술서번역한번해볼까 in NDC10
나도기술서번역한번해볼까 in NDC10나도기술서번역한번해볼까 in NDC10
나도기술서번역한번해볼까 in NDC10
 
나도(기술서)번역한번해볼까
나도(기술서)번역한번해볼까나도(기술서)번역한번해볼까
나도(기술서)번역한번해볼까
 
Unicode 이해하기
Unicode 이해하기Unicode 이해하기
Unicode 이해하기
 
위대한 게임개발팀의 공통점
위대한 게임개발팀의 공통점위대한 게임개발팀의 공통점
위대한 게임개발팀의 공통점
 
문자셋과 인코딩
문자셋과 인코딩문자셋과 인코딩
문자셋과 인코딩
 
Domain Driven Design Ch7
Domain Driven Design Ch7Domain Driven Design Ch7
Domain Driven Design Ch7
 
Unicode100
Unicode100Unicode100
Unicode100
 
Oop design principle SOLID
Oop design principle SOLIDOop design principle SOLID
Oop design principle SOLID
 
Oop design principle
Oop design principleOop design principle
Oop design principle
 
NDC14 - 사례로 배우는 디스어셈블리 디버깅
NDC14 - 사례로 배우는 디스어셈블리 디버깅NDC14 - 사례로 배우는 디스어셈블리 디버깅
NDC14 - 사례로 배우는 디스어셈블리 디버깅
 
온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기
 
KGC2010 - 낡은 코드에 단위테스트 넣기
KGC2010 - 낡은 코드에 단위테스트 넣기KGC2010 - 낡은 코드에 단위테스트 넣기
KGC2010 - 낡은 코드에 단위테스트 넣기
 

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

클라우드 & 모바일 환경에서 알아야 할 성능 품질 이야기
클라우드 & 모바일 환경에서 알아야 할 성능 품질 이야기클라우드 & 모바일 환경에서 알아야 할 성능 품질 이야기
클라우드 & 모바일 환경에서 알아야 할 성능 품질 이야기YoungSu Son
 
클라우드 환경에서 알아야할 성능 이야기
클라우드 환경에서 알아야할 성능 이야기클라우드 환경에서 알아야할 성능 이야기
클라우드 환경에서 알아야할 성능 이야기YoungSu Son
 
[D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint
[D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint [D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint
[D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint NAVER D2
 
레가시 프로젝트의 빌드 자동화
레가시 프로젝트의 빌드 자동화레가시 프로젝트의 빌드 자동화
레가시 프로젝트의 빌드 자동화Jaehoon Choi
 
소셜게임 서버 개발 관점에서 본 Node.js의 장단점과 대안
소셜게임 서버 개발 관점에서 본 Node.js의 장단점과 대안소셜게임 서버 개발 관점에서 본 Node.js의 장단점과 대안
소셜게임 서버 개발 관점에서 본 Node.js의 장단점과 대안Jeongsang Baek
 
웨일브라우저 성능 및 메모리 최적화
웨일브라우저 성능 및 메모리 최적화웨일브라우저 성능 및 메모리 최적화
웨일브라우저 성능 및 메모리 최적화NAVER D2
 
안드로이드 빌드: 설탕없는 세계
안드로이드 빌드: 설탕없는 세계안드로이드 빌드: 설탕없는 세계
안드로이드 빌드: 설탕없는 세계Leonardo YongUk Kim
 
Rapid Development
Rapid DevelopmentRapid Development
Rapid Development기룡 남
 
Multithread & shared_ptr
Multithread & shared_ptrMultithread & shared_ptr
Multithread & shared_ptr내훈 정
 
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드cranbe95
 
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기Jaeseung Ha
 
Pgday bdr gt1000
Pgday bdr gt1000Pgday bdr gt1000
Pgday bdr gt1000정대 천
 
Pgday bdr 천정대
Pgday bdr 천정대Pgday bdr 천정대
Pgday bdr 천정대PgDay.Seoul
 
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 TestOkjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 Testbeom kyun choi
 
비동기와 이벤트큐 수업자료
비동기와 이벤트큐 수업자료비동기와 이벤트큐 수업자료
비동기와 이벤트큐 수업자료지수 윤
 
[NDC08] 최적화와 프로파일링 - 송창규
[NDC08] 최적화와 프로파일링 - 송창규[NDC08] 최적화와 프로파일링 - 송창규
[NDC08] 최적화와 프로파일링 - 송창규ChangKyu Song
 
안드로이드 Oreo의 변화와 모바일 앱/플랫폼의 적합한 성능 측정 방법
안드로이드 Oreo의 변화와  모바일 앱/플랫폼의 적합한 성능 측정 방법안드로이드 Oreo의 변화와  모바일 앱/플랫폼의 적합한 성능 측정 방법
안드로이드 Oreo의 변화와 모바일 앱/플랫폼의 적합한 성능 측정 방법YoungSu Son
 
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018Kenneth Ceyer
 
빌드관리 및 디버깅 (2010년 자료)
빌드관리 및 디버깅 (2010년 자료)빌드관리 및 디버깅 (2010년 자료)
빌드관리 및 디버깅 (2010년 자료)YEONG-CHEON YOU
 
프로그래밍 패러다임의 진화 및 Spring의 금융권 적용
프로그래밍 패러다임의 진화 및 Spring의 금융권 적용프로그래밍 패러다임의 진화 및 Spring의 금융권 적용
프로그래밍 패러다임의 진화 및 Spring의 금융권 적용중선 곽
 

Semelhante a 온라인 게임에서 사례로 살펴보는 디버깅 (20)

클라우드 & 모바일 환경에서 알아야 할 성능 품질 이야기
클라우드 & 모바일 환경에서 알아야 할 성능 품질 이야기클라우드 & 모바일 환경에서 알아야 할 성능 품질 이야기
클라우드 & 모바일 환경에서 알아야 할 성능 품질 이야기
 
클라우드 환경에서 알아야할 성능 이야기
클라우드 환경에서 알아야할 성능 이야기클라우드 환경에서 알아야할 성능 이야기
클라우드 환경에서 알아야할 성능 이야기
 
[D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint
[D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint [D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint
[D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint
 
레가시 프로젝트의 빌드 자동화
레가시 프로젝트의 빌드 자동화레가시 프로젝트의 빌드 자동화
레가시 프로젝트의 빌드 자동화
 
소셜게임 서버 개발 관점에서 본 Node.js의 장단점과 대안
소셜게임 서버 개발 관점에서 본 Node.js의 장단점과 대안소셜게임 서버 개발 관점에서 본 Node.js의 장단점과 대안
소셜게임 서버 개발 관점에서 본 Node.js의 장단점과 대안
 
웨일브라우저 성능 및 메모리 최적화
웨일브라우저 성능 및 메모리 최적화웨일브라우저 성능 및 메모리 최적화
웨일브라우저 성능 및 메모리 최적화
 
안드로이드 빌드: 설탕없는 세계
안드로이드 빌드: 설탕없는 세계안드로이드 빌드: 설탕없는 세계
안드로이드 빌드: 설탕없는 세계
 
Rapid Development
Rapid DevelopmentRapid Development
Rapid Development
 
Multithread & shared_ptr
Multithread & shared_ptrMultithread & shared_ptr
Multithread & shared_ptr
 
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
 
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기
 
Pgday bdr gt1000
Pgday bdr gt1000Pgday bdr gt1000
Pgday bdr gt1000
 
Pgday bdr 천정대
Pgday bdr 천정대Pgday bdr 천정대
Pgday bdr 천정대
 
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 TestOkjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
 
비동기와 이벤트큐 수업자료
비동기와 이벤트큐 수업자료비동기와 이벤트큐 수업자료
비동기와 이벤트큐 수업자료
 
[NDC08] 최적화와 프로파일링 - 송창규
[NDC08] 최적화와 프로파일링 - 송창규[NDC08] 최적화와 프로파일링 - 송창규
[NDC08] 최적화와 프로파일링 - 송창규
 
안드로이드 Oreo의 변화와 모바일 앱/플랫폼의 적합한 성능 측정 방법
안드로이드 Oreo의 변화와  모바일 앱/플랫폼의 적합한 성능 측정 방법안드로이드 Oreo의 변화와  모바일 앱/플랫폼의 적합한 성능 측정 방법
안드로이드 Oreo의 변화와 모바일 앱/플랫폼의 적합한 성능 측정 방법
 
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
 
빌드관리 및 디버깅 (2010년 자료)
빌드관리 및 디버깅 (2010년 자료)빌드관리 및 디버깅 (2010년 자료)
빌드관리 및 디버깅 (2010년 자료)
 
프로그래밍 패러다임의 진화 및 Spring의 금융권 적용
프로그래밍 패러다임의 진화 및 Spring의 금융권 적용프로그래밍 패러다임의 진화 및 Spring의 금융권 적용
프로그래밍 패러다임의 진화 및 Spring의 금융권 적용
 

Mais de Ryan Park

OOP 설계 원칙 S.O.L.I.D.
OOP 설계 원칙 S.O.L.I.D.OOP 설계 원칙 S.O.L.I.D.
OOP 설계 원칙 S.O.L.I.D.Ryan Park
 
프로그램은 왜 실패하는가 1장
프로그램은 왜 실패하는가 1장프로그램은 왜 실패하는가 1장
프로그램은 왜 실패하는가 1장Ryan Park
 
Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005Ryan Park
 
UnitTest, Tdd For Games Kgc2007 ParkPD
UnitTest, Tdd For Games Kgc2007 ParkPDUnitTest, Tdd For Games Kgc2007 ParkPD
UnitTest, Tdd For Games Kgc2007 ParkPDRyan Park
 
Agile Test Driven Development For Games What, Why, And How
Agile Test Driven Development For Games What, Why, And HowAgile Test Driven Development For Games What, Why, And How
Agile Test Driven Development For Games What, Why, And HowRyan Park
 
Agd Test Driven Development For Games What, Why, And How)(Game Connect 2006...
Agd   Test Driven Development For Games What, Why, And How)(Game Connect 2006...Agd   Test Driven Development For Games What, Why, And How)(Game Connect 2006...
Agd Test Driven Development For Games What, Why, And How)(Game Connect 2006...Ryan Park
 

Mais de Ryan Park (7)

OOP 설계 원칙 S.O.L.I.D.
OOP 설계 원칙 S.O.L.I.D.OOP 설계 원칙 S.O.L.I.D.
OOP 설계 원칙 S.O.L.I.D.
 
Unicode
UnicodeUnicode
Unicode
 
프로그램은 왜 실패하는가 1장
프로그램은 왜 실패하는가 1장프로그램은 왜 실패하는가 1장
프로그램은 왜 실패하는가 1장
 
Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005
 
UnitTest, Tdd For Games Kgc2007 ParkPD
UnitTest, Tdd For Games Kgc2007 ParkPDUnitTest, Tdd For Games Kgc2007 ParkPD
UnitTest, Tdd For Games Kgc2007 ParkPD
 
Agile Test Driven Development For Games What, Why, And How
Agile Test Driven Development For Games What, Why, And HowAgile Test Driven Development For Games What, Why, And How
Agile Test Driven Development For Games What, Why, And How
 
Agd Test Driven Development For Games What, Why, And How)(Game Connect 2006...
Agd   Test Driven Development For Games What, Why, And How)(Game Connect 2006...Agd   Test Driven Development For Games What, Why, And How)(Game Connect 2006...
Agd Test Driven Development For Games What, Why, And How)(Game Connect 2006...
 

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

  • 1. 온라인 게임에서사례로 살펴보는디버깅 KGC 2009 박일 리니지2 서버팀 parkpd.egloos.com
  • 2. 저를 소개합니다 박일 2006 년 초 리니지2 서버팀으로 이동 The Chaotic Chronicle – Oath of Blood (Chronicle 5) 부터 credit 에 제 이름을 볼 수 있습니다 블로그 : http://parkpd.egloos.com
  • 3. 경고:발표에서 사용된 사례들은대부분 실제사례를근거로 하여재구성된 것들입니다.
  • 5.
  • 6. 버그? 충돌과 응답없음, 성능과 측정 가능성의 저하 부정확한 결과, 보안 취약점, 일관성 없는 사용자 인터페이스 요구 사항 불충족
  • 7. 디버깅을 잘 하는 방법 배우기 나와 다른 사람의 문제 해결 과정을 관찰 내성법 “문제가 무엇이었나” 보다,“어떻게 문제를 찾았나” 가 더 중요 의사보다 유리한 점 프로세스를 여러 번 다양하게 죽여 볼 수 있다 웃기는 점 디버깅이 오래 걸릴 수록, 고마워한다
  • 8. 버그 찾기 버그 재현 버그 분석 해결책 결정 및 예방
  • 9. 버그 찾기 dump 로 Crash 난 위치 확인 최근에 고친 파일부터 diff 좋은 diff 툴, 일일빌드 이상현상이 생긴 시간대 근처 에러로그 확인 내가 고친 코드를 먼저 의심 다른 사람과 얘기해보자(Bug Talk) 디버깅은 근성이다 도저히 모르겠다 싶을 때 1시간만 더 보자 어셈블리는 항상 가장 마지막에 서버라면 실제 서버를 터미널 서비스로 들어가 작업관리자를 보자
  • 10. Everybody lies false positive(거짓양성) 예전 바이너리를 쓰고 있다던가 false negative(거짓음성) QA 가 고쳐지지도 않은 걸 고쳐졌다고 하거나 플라시보 효과 예전부터 있던 코드도 100% 믿지 말자
  • 11. // Dump 보기 실습 typedefBOOL(WINAPI*MINIDUMPWRITEDUMP)(HANDLEhProcess,...); LONGTopExceptionFilter(LPEXCEPTION_POINTERS pExp){ LONGretval=EXCEPTION_CONTINUE_SEARCH; HMODULEhDll=NULL; hDll=::LoadLibrary(_T("DBGHELP.DLL")); if(hDll){ MINIDUMPWRITEDUMPpDump= ::GetProcAddress(hDll,"MiniDumpWriteDump"); if(pDump){ HANDLEhFile=::CreateFile(_T("Crash.dmp"),..); if(INVALID_HANDLE_VALUE!=hFile){ _MINIDUMP_EXCEPTION_INFORMATIONExInfo; ExInfo.ThreadId=::GetCurrentThreadId(); ExInfo.ExceptionPointers=pExp; ExInfo.ClientPointers=NULL; BOOLbOK=pDump( GetCurrentProcess(),GetCurrentProcessId(), hFile,MiniDumpNormal,&ExInfo,NULL,NULL); if(bOK){ tcout<<" dump to Crash.dmp"<<endl; retval=EXCEPTION_EXECUTE_HANDLER; } ::CloseHandle(hFile); } } ::FreeLibrary(hDll); } returnretval; } int_tmain(intargc,_TCHAR*argv[]){ SetUnhandledExceptionFilter(TopExceptionFilter); TCHAR*p=NULL; p[1]=_T('M'); return0; }
  • 12.
  • 13. minidump를 출력하기 .dmp파일을 만드는 것 보다 장점은? 이메일로 전송 Label, 빌드별 바이너리, pdb를 SVC 에 저장 라이브용 바이너리 만든 후, 이메일로 label 공유
  • 15. Base Line 지금 상태는 정상인가? 평소 CPU 는 몇 % 정도였나? 의도대로 최적화가 되었나?
  • 16. Performance Counters void start(constchar*pszCounter){ PdhOpenQuery(NULL,0,&hQuery); PdhValidatePath(pszCounter); PdhAddCounter(hQuery,pszCounter,0,&hCounter); } longcurrent()const{ PdhCollectQueryData(hQuery); PDH_FMT_COUNTERVALUEvalue; PdhGetFormattedCounterValue(hCounter,PDH_FMT_LONG,NULL,&value); returnvalue.longValue; } int_tmain(intargc,_TCHAR*argv[]){ Perfmoncpu("cpu.csv"); Perfmonmem("mem.csv"); cpu.start("Processor(_Total)% Processor Time"); mem.start("MemoryPool Nonpaged Bytes"); for(inti=0;i<3;++i){ Sleep(1000); printf("CPU load: %d%, Nonpaged bytes: %d KB",cpu.current(),mem.current()/1024); } } CPUload:99,Nonpagedbytes:22408KB CPUload:0,Nonpagedbytes:22500KB CPUload:3,Nonpagedbytes:22400KB
  • 17. Performance Counters base line 만들기 Counter 를 log 나 DB 에 주기적으로 저장 counter 예시 SQL Server: SQL Statistics: Batch Requests/sec 초당 요청 받은 SQL 배치 요청 수 SQL Server: Buffer Manager: Buffer Cache Hit Ratio 90 이상이어야 함 SQL Server: Locks: Lock Waits/sec 잠금대기요청수 Process: Page Faults/sec 프로세스가 Cache Hit하지 않은 페이지수
  • 18. 사례 – 특정 머신 Tomcat 서버가 죽었음 JVM의 다운으로 인해서 발생되었으며죽은 시점은 GC 과정 중이었음 Log4j를 통한 로그 모니터링 결과에는 죽기 전 후 특이 사항 발견되지 않음
  • 19. JVM 에러로그 # An unexpected error has been detected by HotSpot Virtual Machine: # Internal Error (50532D41524B33574545502445434F5241544F520E4350500024), pid=27607, tid=1828866976 # Java VM: Java HotSpot(TM) Server VM (1.5.0_12-b04 mixed mode) --------------- T H R E A D --------------- Current thread (0x080ffe48): VMThread [id=27611] Stack: [0x6cfa4000,0x6d025000), sp=0x6d023ae0, free space=510k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) V [libjvm.so+0x512633] V [libjvm.so+0x1c862c] V [libjvm.so+0x51a3f0] V [libjvm.so+0x438228] C [libpthread.so.0+0x53cc] VM_Operation (0x66f6ae40): parallel gc failed allocation,mode: safepoint, requested by thread 0x6bbcf7d0 Heap PSYoungGen total 17216K, used 696K [0xaa370000, 0xabc90000, 0xb1530000) eden space 16512K, 0% used [0xaa370000,0xaa370000,0xab390000) from space 704K, 98% used [0xab3d0000,0xab47e1d0,0xab480000) to space 4608K, 0% used [0xab810000,0xab810000,0xabc90000) PSOldGen total 629440K, used 627294K [0x71530000, 0x97be0000, 0xaa370000) object space 629440K, 99% used [0x71530000,0x979c7b48,0x97be0000) PSPermGen total 37888K, used 37271K [0x6d530000, 0x6fa30000, 0x71530000) object space 37888K, 98% used [0x6d530000,0x6f995ed0,0x6fa30000)
  • 20.
  • 21.
  • 22. 사례 – 언제 문제가 발생했나? 특정 시각? 특정 요일? CPU 가 튀는 주기는? 게임 서버가 새벽 2시만 되면 죽었던 이유는? Windows Update 가 새벽 2시에 실행 바이러스 update 창이 200 개 이상 오픈 같은 port 를 bind 계속하면 port 차단 서버관리툴이 실행될 때마다 특정 port 를 bind 하게 설계 일본 IDC 에서 꼭 설치하게 하는 패킷 필터링 시스템의 DDOS, Garbage Attack 탐지기가 해당 port 를 차단 얼마만에 죽는가 특정 tick 에 죽는 잘못된 코드와 memory leak Printf대신 사용한 로그 파일이 엄청나게 커짐 Memory leak은 아니지만, 너무 많은 메모리 할당 2G 는 절대 큰 용량이 아님 결국 일본 찾아가서 해결
  • 23. 버그 찾기 버그 재현 버그 분석 해결책 결정 및 예방
  • 24. 무조건 재현! 재현! 현상은 원인이 있기 때문에 나타난다. 정상적인 재현이 불가능하면, 억지로라도 재현해라. 재현 할 때는 BP 걸어놓고 한 step 씩 이동하면서 확인 재현할 때는 문제가 되는 현상을 데이터까지 똑같게 택배로 컴퓨터를 아예 받아와라. 고친 게 하나도 없어도, 환경만 바뀌면 버그 발생 가능 설정 파일, 하드웨어, OS 버전, 보안 프로그램 동작 여부 버그 report 를 그대로 믿지 마라 QA 에서도 아는 선 안에서만 리포트를 쓴다 QA 분들에게 내 개발서버로 접속해서 재현해 달라고 부탁 해외 지사에서 보낸 버그 리포트는 특히 심하다
  • 25. 사례 - 경매 라이브 서버가 아닌 테스트 서버, 그 중 한 npc에서만 발생 재현도안 되어서, 별 문제 없겠지 생각하고 무시 업데이트 후에 전 라이브 서버에서 문제 발생 이유 : 라이브 서버에서는 처음에 개발자가 직접 seed 값을 넣어줘서 경매에 문제 없었지만, 테스트 서버의 경우 개발자가 여러 npc중 한 npc만 까먹고 seed 값을 넣어주지 않았기 때문에 계속 문제가 있었음 처음부터 문제가 있었는데도, QA 에서는 ‘잘 되다가 저번 주부터 문제가 생겼음’ 이라고 보고했고, 개발팀도 QA 만 믿고특이한 사례보고라고 생각함 대규모 업데이트를 위해 모든 seed 값을 초기화했더니, 테스트 서버와 동일한 문제가 모든 npc에서 발생 결론 증상이 있다면 무시하지 말자 [사례보고] 라는 제목 때문에 선입관 생김
  • 27. 사례 - OpenMP 수 십대의 개발 서버 중 한 서버에서만 에러 발생 알고 보니 특정 머신(하퍼타운)에서만 에러 발생. 이유는? 제대로 MT 를 지원하는 머신에서 테스트 물리 core 가 여러 대인 CPU 머신 사용하자 하퍼타운을 쓰는 다른 개발자는 설정에서 omp사용을 빼 놨기 때문에 에러나는 줄 몰랐음
  • 28. 사례 – 공성과 결자해지 voidLoadData(){ // do something CCastlec(nCastleNum);// dead code??? return; }
  • 29. 사례 – 공성과 결자해지 voidLoadData(){ // do something CCastlec(nCastleNum);// dead code? NO!!! return; } classCCastle{ public: CCastle(intnId){ g_CastleDB.Register(nId, this); } };
  • 30. 사례 – 토요일에만 실패하는단위테스트 요일 index 문제 GetLocalTime wDayOfWeek : The day of the week. Sunday = 0, Monday = 1 해외 : 특정 요일에만 실패
  • 31. 버그 찾기 버그 재현 버그 분석 해결책 결정 및 예방
  • 33. Visualizer in Visual Studio 2005
  • 34. 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; }
  • 35. Debug Windows 1 {[function],[source],[module]} 뒤에location, variable_name, expression 특정 함수의 static local 변수 보기 void Test() { static intsLocalNum = 0; } {Test,,}sLocalNum 특정 dll의 전역변수 : {,,foobar.dll}g_pMyStruct 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; }
  • 36. Debug Windows 2 Pseudo Register @eax : 리턴값 @err : GetLastError @HANDLES 현재 프로세스의 핸들 갯수 @ebp : 지역 변수 시작 지점 @esp : 최상위 스택
  • 37.
  • 38. 사례 – Bot voidCPlayer::OnItemExchange( intnItemCount1,BYTE*pData1, intnItemCount2,BYTE*pData2) { for(inti=0;i<nItemCount1;++i){ // do something } for(inti=0;i<nItemCount2;++i){ // do something } }
  • 39. 사례 – hp 회복 문제 classCPlayer{ public: CPlayer():m_HP(1.0){} voidOnRegenTick(); doublem_HP; doublem_Buff[3]; }; voidCPlayer::OnRegenTick(double delta){ m_HP=m_HP * 1,05+(0.5*delta) +(10.0*0.3)/(52+m_Buff[1]) +(m_HP/m_Buff[2]*3.0)+2; }
  • 40.
  • 41. BUG? (x != x) : true (x == x) : false (y > x) : false (y < x) : false
  • 42. BUG? (x != x) : true (x == x) : false (y > x) : false (y < x) : false NaN(Not a Number) double answer = sqrt(-1.0);
  • 43. // http://msdn.microsoft.com/en-us/library/w22adx1s%28VS.80%29.aspx classCData{ public: enum{ HALF_NUM=0x7fffffff, MAX_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; } voidGetBonus(intnBonusIndex){ m_dPoint*=m_dData[nBonusIndex]; } doublem_dPoint; doublem_dData[2]; unsignedlongm_nData[2]; }; int_tmain(intargc,_TCHAR*argv[]){ CDataa; a.GetBonus(2); wcout<<L"isnan : "<<_isnan(a.m_dPoint)<<L''; wcout<<"(1.0 < a.m_dPoint) : "<<(1.0<a.m_dPoint)<<L''; wcout<<"(1.0 >= a.m_dPoint) : "<<(1.0>=a.m_dPoint)<<L''; return0; } // output isnan:1 (1.0<a.m_dPoint):0 (1.0>=a.m_dPoint):0
  • 44. 사례 – 메모리 침범 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);// Crash return0; } #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 die aArray[256]=0;// always die // Assertion failed: i < N && "out of range", file , line 91 return0; }
  • 45. 사례 – goto 변수 선언문을 건너뛰기 #define goto WhatTheHell int_tmain(intargc,_TCHAR*argv[]){ gotoJumpToHere; intnTestValue; JumpToHere: nTestValue=4; wcout<<nTestValue; return0; }
  • 46. 사례 - random // ……………………….. intGetRand1(intnMin,intnMax){ intnOffset=rand()%(nMax-nMin); returnnMin+nOffset; } doubleGetRand2(doubledMin,doubledMax){ return((double)rand()/(double)RAND_MAX)*(dMax-dMin); }
  • 47. 사례 - random // RAND_MAX : 32767 intGetRand1(intnMin,intnMax){ intnOffset=rand()%(nMax-nMin); returnnMin+nOffset; } doubleGetRand2(doubledMin,doubledMax){ return((double)rand()/(double)RAND_MAX)*(dMax-dMin); } 0 ~ 2767 확률이 2768 ~ 9999 보다 1/32767 높다. if (Rand(1000000) <= 1) // 0.0001% 과if (Rand(100000) <= 1) // 0.001% 의차이는 rand 를 RAND_MAX 번 호출했을 때 1이 나올 확률은 1/32767 = 0.00305% > (0.001% 과 0.0001%) 0.001% 와 0.003% 의 차이가 없음
  • 48. 예제 - std::sort structData{ Data(intn):m_Num(n){} intm_Num; }; boolIsLessThan(Data*a,Data*b){ returna->m_Num>b->m_Num; } typedefvector<Data*>DataList; voidtest(DataList& d){ for(inti=0;i<10;++i) d.push_back(newData(i)); sort(d.begin(),d.end(),IsLessThan);
  • 49. 예제 - std::sort sort(m_data.begin() …) 하는 도중에 다른 thread 에서 Num 값을 바꾸면 무한 루프가 발생할 수 있음 structData{ Data(intn):m_Num(n){} intm_Num; }; boolIsLessThan(Data*a,Data*b){ returna->m_Num>b->m_Num; } typedefvector<Data*>DataList; voidtest(DataList& d){ for(inti=0;i<10;++i) d.push_back(newData(i)); sort(d.begin(),d.end(),IsLessThan);
  • 53. 버그 찾기 버그 재현 버그 분석 해결책 결정 및 예방
  • 54. 좋은 코드 작성하기 voidTest1(CTest*p){ // do something } voidTest2(CTest&t){ // do something }
  • 55. 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 }
  • 56. Example of bugs repartition Open source code Source: Coverity White Paper
  • 57. Example of bugs repartition Open source code 버그의 비용 Source: Coverity White Paper
  • 58. 진짜 해결책 펫, 소환수 자동 소환 09.09.09 라이브 업데이트 펫인벤 아이템을 언제 주인에게 옮겨줄 것인가? 진짜 옮겨야 하나?
  • 59. 에러 로그를 적절하게 남긴다 잘남겨야 한다 copy & paste 하면서 에러 로그도 똑같이 남기면 어디에서 생긴 에러인지 알 수 없다 DDiba! __FILE__, __LINE__ 캐릭터 이름, 아이템 아이디(해외 에러 사례) 너무 많이 남기면 느려진다, 정보 노이즈 사례 : [NO_ERROR] 로그 너무 조금 남기면 필요한 정보를 찾을 수 없다
  • 60. 버그 미리 찾기 _ASSERT !! 정기적인 코드 리뷰 코드리뷰 별 거 아님 정적 분석툴과CI 연동 Code Analysis (자료 추가할 것) pc-lint 단위테스트 Magic bit 사용 int를 int64 로 바꾸면서 magic bit 끼워넣기(특정 bit 가 1 이 아니면 crash)
  • 61.
  • 62. 포인터에 대한 사용 주의 경고, 중복된 변수 선언
  • 63. 묵시적 Type Casting, sprintf 의 인자 개수 체크
  • 64. 단 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; }
  • 66. 단위테스트 단위테스트로 버그 재현 후 해결책을 적용해 버그가 사라지는 걸 확인 가장 많이 물어보는 질문 무엇을 단위테스트로 만들 것인가? 버그가 발생했던 문제부터 단위테스트로 재현하라 가장 쉬운 것 부터 해라 테스트를 만들기 쉽게 해라
  • 67. Memory Leak Detector 1 structCItem{ CItem(){g_ItemCount++;} ~CItem(){g_ItemCount--;} }; structFixtureBase{ FixtureBase(){g_ItemCount=0;} virtual~FixtureBase(){_ASSERT(0==g_ItemCount);} }; structFixtureTest:publicFixtureBase{ FixtureTest(){ m_pPlayer=CPlayer::Create(); m_pItem=CItem::Create(); } ~FixtureTest(){ CItem::Delete(m_pItem); CPlayer::Delete(m_pPlayer); } CPlayer*m_pPlayer; CItem*m_pItem; }; TEST_FIXTURE(FixtureTest,UseItem){ m_pPlayer->Use(m_pItem); }
  • 68. Memory Leak Detector 2 #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 lib returntrue; 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 4 typedefstruct_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 Detector Memory Pool 대신 new, delete 를 쓰게 만드는 flag 를 하나 둘 것 이러면 gflag 나 UMDH 로 버그 찾기도 훨씬 쉽다
  • 70. 간단한 Mock 만들기 classCTest{ protected: intm_Test; voidTest(){} }; classCMockTest: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; }
  • 71. NewTest SUITE 특수한 조건에서의 함수 테스트에 Break Point 를 걸고 싶을 때 RunAllTests(… “NewTest”); RunAllTests(… “DefaultSuite”); TEST_FIXTURE(FixtureTest,UseItem) { m_pPlayer->Use(m_pItem); } SUITE(NewTest) { TEST_FIXTURE(FixtureTest,UseSpecialItem) { m_pPlayer->Use(m_pItem); } }
  • 73. 결론 버그 찾는 법을 수련하자 어떻게든 재현하자 작은 버그도 무시하지 말자 쓰고 있는 툴을 최대한 활용하자 해결책을 최소 2 개 이상 생각해보자 버그를 미리 막을 방법을 만들어보자 단위테스트를 활용하자
  • 74. Q/A
  • 76.
  • 77.
  • 78. 재현은안 되나 꾸준히 발생
  • 79. 원인을 찾지 못해, 가능한 원인을 하나씩 제거하기로 결정
  • 81. Crash 는 없어진 듯 했으나1주일 후 다시 Crash 발생
  • 82. CVideo 의 멤버변수값이 이상하게 변경되는 다른 현상도 발생
  • 83.
  • 84. typedefCSmartPtr<CVideo>CVideoSP; classCVideoManager{ staticCVideoSPCreateVideo(intnId){ returnnewCVideo(nId); } }; int_tmain(intargc,_TCHAR*argv[]){ CVideo* p1=CVideoManager::CreateVideo(1); return0; } classCObj{ public: CObj():m_nRef(0){} virtual~CObj(){} intAddRef(){return++m_nRef;} boolRelease(){ --m_nRef; if(0==m_nRef){ //return_to_pool(this); // reason 3 deletethis; returntrue; }elseif(m_nRef<0){ //_ASSERT_EXPR(0, L"over release!"); // reason 4 } returnfalse; } staticvoid*operatornew(size_tsize){ //void *p=allocate_from_pool(size); void*p=malloc(size); returnp; } staticvoidoperatordelete(void*p){ //_ASSERT_EXPR(0, L"don't delete CObj"); free(p); } intm_nRef; }; template<typenameT> classCSmartPtr{ public: typedefCSmartPtr<T>this_type; CSmartPtr():m_p(NULL){} //explicit CSmartPtr(T* p) : m_p(p) {} CSmartPtr(T*p):m_p(p){}// reason 1 ~CSmartPtr(){ if(m_p){ m_p->Release(); } } T&operator*()const{ return*m_p; } T*operator->()const{ returnm_p; } operatorT*(){// reason 2 returnm_p; } private: T*m_p; };
  • 85. 사례 int_tmain(intargc,_TCHAR*argv[]){ intnInput=0; constfloatfail=1.0f; while(1){ wcin>>nInput; for(inti=0;i<5;++i){ DWORDt=GetTickCount(); DWORDnRet=(nInput==0)?fail:t; wcout<<nRet<<L''<<t<<L''; Sleep((rand()%10)+20); } } return0; }
  • 86. 사례 int_tmain(intargc,_TCHAR*argv[]){ intnInput=0; constfloatfail=1.0f; while(1){ wcin>>nInput; for(inti=0;i<5;++i){ DWORDt=GetTickCount(); DWORDnRet=(nInput==0)?fail:t; wcout<<nRet<<L''<<t<<L''; Sleep((rand()%10)+20); } } return0; } 426468224 426468234 426468256 426468250 426468288 426468281 426468320 426468312 426468352 426468343
  • 87. 사례 – 설정 파일 분명 그 파일을 고쳤어요! 설정 파일을 암호화하고, 개발팀에서 관리
  • 88. 사례 : 해외 설정 Country Code : tailand
  • 89. 사례 : 해외 설정 Country Code : tailand > Country Code : thailand
  • 90. 팀 디버깅 왜 서버가 뜨는 도중에 죽을까? 각자 disassemble, 코드 히스토리 비교, 로그 비교 원인 : 운영팀에서 DB 에 직접 데이터를 insert 하는 바람에 특정 데이터의 갯수가 max 값을 넘어버려서, pass by index 문제 발생 DBA 가 발견 교훈 : 디버깅할 때는 모든 가정을 버리자 각자 전문분야를 동원해 문제를 바라보면, 같은 문제를 다양한 시각에서 바라볼 수 있다 업무를 돌아가면서 맡기
  • 91. 사례 – DB update pc_data set c_value = 127 delete from event_data 실제 크기 데이터로 테스트 해 보자
  • 92. 사례 – 환형 큐 int_tmain(intargc,_TCHAR*argv[]){ constintMAX_QUEUE=7; volatilelongindex=0xffffffff-8; for(inti=0;i<10;++i){ ::InterlockedIncrement(&index); wcout<<(unsignedlong)index%MAX_QUEUE<<L", "; } return0; }
  • 93. 사례 – 환형 큐 long 범위를 벗어나는 순간, MAX_QUEUE 가 2 의 배승이 아니라면? 3, 4, 5, 6, 0, 1, 2, 3, 0, 1 overflow 되기까지 5일~10일이 걸린다면? int_tmain(intargc,_TCHAR*argv[]){ constintMAX_QUEUE=7; volatilelongindex=0xffffffff-8; for(inti=0;i<10;++i){ ::InterlockedIncrement(&index); wcout<<(unsignedlong)index%MAX_QUEUE<<L", "; } return0; }
  • 94. 사례 – virtual method classA{ public: virtualvoidOnTest(){ wcout<<L"A::OnTest()"; } }; classB:publicA{ public: virtualvoidOnTest(){ A::OnTest(); wcout<<L"B::OnTest()"; } }; voidtest() { A*p=newB(); p->OnTest(); } A::OnTest() B::OnTest() voidtest() { A*p=newB(); p->OnTest(); } A::OnTest() 만 출력되는 이유는? 2가지
  • 95. 사례 – virtual method classA{ public: virtualvoidOnTest(){ wcout<<L"A::OnTest()"; } }; classB:publicA{ public: virtualvoidOnTest(intn=0){ A::OnTest(); wcout<<L"B::OnTest()"; } }; classA{ public: virtualvoidOnTest()const{ wcout<<L"A::OnTest()"; } }; classB:publicA{ public: virtualvoidOnTest(){ A::OnTest(); wcout<<L"B::OnTest()"; } };
  • 96. 기록 버그의 원인과 해결책 기록 및 공유 실천법 버그 트래킹 툴에 FIX 할 때는, 실제로 어떤 코드를 고쳤는지 정도의 간단한 정보를 남긴다 누구보다 내가 나중에 그 정보를 필요하게 된다 일일회의 내용을 위키에 저장해 놓으면, 코드 변경 히스토리에서 버그를 만든 날짜에 내가 뭘 하려고 했는지를 알 수 있다 해결한 문제에 대해, 원인-해결책을 시간, 문제별로 정리해 놓으면, 몇 달 후 해외에서 같은 문제가 생겼을 때 빨리 해결할 수 있다.
  • 98. 버그 정의 디버깅 .NET 응용 프로그램 미니 덤프 http://hhko.egloos.com/891853 크래시 덤프 분석기 http://blog.maiet.net/xe/4596 성능 카운터 http://serious-code.net/moin.cgi/WindowsPerformanceMonitoring Visualizer http://minjang.egloos.com/468834 Visual Studio 2005 최적화 오류 http://support.microsoft.com/kb/925792/ http://support.microsoft.com/kb/918526/ko http://support.microsoft.com/kb/959378/ko Comma operator(C++) http://msdn.microsoft.com/en-us/library/zs06xbxh(VS.80).aspx IE 크래시 http://parkpd.egloos.com/1930129 (파수 닷컴) http://parkpd.egloos.com/1926843 (한글 2007 문제)
  • 99. Magic bit http://btwinuni.egloos.com/1171237 NaN(Not a Number) http://msdn.microsoft.com/en-us/library/w22adx1s%28VS.80%29.aspx Code Analysis http://eslife.tistory.com/entry/Visual-Studio-2005%EC%9D%98-Code-Analysis-%EA%B8%B0%EB%8A%A5 http://whiteapple.textcube.com/224 펫, 소환수 자동 소환 http://www.playforum.net/lineage2/board.comm?action=read&iid=10032291&pageNo=0&num=16102 http://www.playforum.net/lineage2/board.comm?action=read&iid=10032298&pageNo=0&num=14443 Memory Leak Detector 2 http://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
  • 100. 사진 출처 버그! 원사운드 만화 http://oooz.net/tc 닥터 하우스 http://mdy2.tistory.com/110 아이온 쿠폰 http://duke.egloos.com/page/2 측우기 http://www.pureunschool.org/bbs/board.php?bo_table=dmake&wr_id=7 필승교 http://tvpot.daum.net/clip/ClipView.do?clipid=18199953%26q=%EB%8C%80%ED%94%BC%EC%8B%9C%EA%B0%84%26searchType=0%26sort=wtime%26svctype=1%26focus=1 매듭 http://www.opentory.com/index.php/%EB%A7%A4%EB%93%AD 심볼 서버 http://www.codeguru.com/cpp/v-s/debug/debuggers/article.php/c15355__2/ 그래픽 카드 테스트실 http://chulin28ho.egloos.com/5044251