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

4,241 views

Published on

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

Published in: Technology
0 Comments
8 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,241
On SlideShare
0
From Embeds
0
Number of Embeds
744
Actions
Shares
0
Downloads
0
Comments
0
Likes
8
Embeds 0
No embeds

No notes for slide

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

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

×