9. 버그 찾기 dump 로 Crash 난 위치 확인 최근에 고친 파일부터 diff 좋은 diff 툴, 일일빌드 이상현상이 생긴 시간대 근처 에러로그 확인 내가 고친 코드를 먼저 의심 다른 사람과 얘기해보자(Bug Talk) 디버깅은 근성이다 도저히 모르겠다 싶을 때 1시간만 더 보자 어셈블리는 항상 가장 마지막에 서버라면 실제 서버를 터미널 서비스로 들어가 작업관리자를 보자
10. Everybody lies false positive(거짓양성) 예전 바이너리를 쓰고 있다던가 false negative(거짓음성) QA 가 고쳐지지도 않은 걸 고쳐졌다고 하거나 플라시보 효과 예전부터 있던 코드도 100% 믿지 말자
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 는 절대 큰 용량이 아님 결국 일본 찾아가서 해결
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; }
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% 의 차이가 없음
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);
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)
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); } }
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 실제 크기 데이터로 테스트 해 보자
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