Windbg에 포함된 Adplus를 이용한 덤프 생성 방법입니다.


간략 예제

크래시 덤프 : adplus.vbs -CRASH -p PID -o 출력경로

행 덤프 : adplus.vbs -HANG -p PID -o 출력경로



Adplus

http://support.microsoft.com/kb/286350

ADPlus 명령줄 스위치

Adplus를 사용 하는 일련의 명령줄 스위치나 인수를 사용 하는 스크립트를 지정 해야 합니다. 최소한 ADPlus 두 개의 스위치가 필요 합니다: 하나는 작동 모드를 지정 하 고 작동 대상 프로세스를 지정 합니다.

다음은 가장 자주 사용 하는 스위치입니다. 베이스 ADPlus실행 하 여 스위치의 전체 목록을 보고 하거나 도움말 파일 (Debugger.chm)을 디버거를 볼 수도 있습니다.
  • -중단
    이 스위치는 Adplus를 중지 모드에서 실행 되도록 구성 합니다. 이 스위치는 -iis-pn또는 -p 스위치와 함께 사용 해야 합니다. 사용할 수 없습니다 -중단 에 -크래시 전환 합니다.

    참고 중지 모드에서 Adplus를 실행 하는 경우 ADPlus  프로세스가 응답 하지 않거나 높은 비율의 CPU 사용을 시작 해야 합니다.
  • -크래시
    이 스위치는 Adplus가 충돌 모드에서 실행 되도록 구성 합니다. 이 스위치는 -iis-pn또는 -p 스위치와 함께 사용 해야 합니다. 사용할 수 없습니다 -크래시 에 -중단 전환 합니다.

    참고 충돌 모드에서 Adplus를 실행 하면 프로세스가 예기치 않게 종료 되거나 불안정 해지기 ADPlus 전에 시작 해야 합니다.
  • -pn 프로세스 이름
    -Pn 스위치는 Adplus에서 분석할 프로세스 이름을 지정 하는 데 사용 됩니다. 둘 이상의 프로세스를 지정 하려면 다중 사용-pn 프로세스 이름 전환합니다. 예를 들면 다음과 같습니다.
    -pn process1.exe-pn process2.exe
  • -p 프로세스 ID
    -P 스위치를 사용 하 여 프로세스 ID (PID)는 Adplus에서 분석할 프로세스의 지정 합니다. 둘 이상의 프로세스를 지정 하려면 다중 사용 -p PID 전환합니다. 예를 들면 다음과 같습니다.
    -p 1896 p 1702
  • -sc명령 생성
    -Pn 및 -p 스위치를 하려면 디버거를 이미 실행 중인 지정 프로세스와 달리 -sc 스위치는 응용 프로그램 및 시작을 생성 하거나 될 매개 변수 디버거를 정의 합니다. 예를 들면 다음과 같습니다.
    -sc "c:\windows\system32\notepad.exe
  • -iis
    -Iis 스위치는 인터넷 정보 서버 (IIS) 4.0 이상을 실행 하는 서버 컴퓨터를 디버깅 하는 데 사용 됩니다. ADPlus -iis 스위치와 함께 사용 하면 ADPlus 모든 IIS in-process (Inetinfo.exe)와 독립 프로세스 (Mtx.exe/Dllhost.exe) 응용 프로그램을 모니터링 합니다. -Iis 스위치는 -pn 스위치나 -p 스위치를 사용할 수 있습니다 또는 단독으로 IIS와 실행 중인 모든 MTS를 분석 하는 데 사용할 수 있습니다 / COM + 응용 프로그램에서 충돌 모드 또는 중지 모드.

    IIS 3.0을 실행 하는 서버 컴퓨터를 분석 하는 동안 또는 이전에 -pn 스위치를 사용 하 고 모니터링할 프로세스로 inetinfo.exe를 지정 합니다.
  • -알림 컴퓨터 이름 또는 사용자 이름
    이 스위치는 Adplus가 충돌 모드에서 실행 되는 경우만 유효 합니다. 이 스위치에 지정 된 사용자 이름이 나 컴퓨터 이름 충돌이 adplus. 두 번째 예외로 인해 디버거를 프로세스에서 분리 하거나 디버깅을 중지 하려면 CTRL + C를 누를 때 원격 사용자나 로컬 메신저 서비스를 통해 컴퓨터에 알림 메시지가 보내집니다. 이 알림은 디버깅 되는 컴퓨터에서 로컬 메신저 서비스가 시작만 발생 합니다.
  • -자동
    이 스위치 사용 하면 adplus 모든 모달 대화 상자 표시 안 함. 이 스위치는 Adplus는 사용자가 확인을 클릭 합니다 무한정 기다리려면 모달 대화 상자를 시킬 수 있는 원격 명령 셸에서 Adplus를 실행 하는 경우에 유용 합니다. 최상의 결과 얻으려면 이것이 Adplus.vbs에 전달 되는 첫번째 스위치 인지 해야 합니다.
  • -o 출력 디렉터리
    이 스위치 adplus가 디버그 출력 파일을 저장할 위치입니다. 긴 파일 이름을 사용 하는 경우 큰따옴표로 묶어야 합니다. 또한 UNC 경로 사용할 수 있습니다 (\\서버\공유). UNC 경로 사용 하는 경우 Adplus는 지정한 UNC 경로 바로 아래에 새 폴더를 만듭니다. ADPlus 실행 중인 서버에 대 한 라는 폴더 (예를 들어, \\서버\공유\Web1 또는 \\서버\공유\Web2)입니다. 이 스위치는 모든 출력에는 동일한 네트워크 공유에 저장 하는 웹 팜의 여러 컴퓨터에서 Adplus를 실행 하는 경우 유용 합니다.

Posted by 엘키 엘키

댓글을 달아 주세요


오늘 오후 5시에 코엑스에서 강연한 자료입니다.
첫 외부 강연이라 미숙하지만, 좋게 좋게 봐주시면 감사하겠습니다 ^^


Posted by 엘키 엘키

댓글을 달아 주세요

  1. 안녕하세요 2012.10.16 09:17  댓글주소  수정/삭제  댓글쓰기

    강연 끝난 후 잠시 이야기 나눴던 프로그래머 지망생입니다... 혹시 메일 보냈는데.. 답장이 안와서요... 검색검색해보다가 블로그를 찾았네요... 혹시나 귀찮게 했다면 죄송합니다.ㅠㅠㅠ;

이 글은 게임 데브 포에버 (http://gamedevforever.com) 에 기고하기 위해 작성된 글임을 알려드립니다.


이번 시간에는 예고한 대로 버그 재발 방지에 대해서 알아보겠습니다.

저를 포함한 대부분의 개발자들은 버그를 만듭니다.
인재들이 모였다는 MS의 IE 베타버전이 얼마나 엉망인지 아시죠? QA와 오류 보고 시스템 등을 통한 검수가 정식 버전을 안정성있게 만드는 것이지 MS가 만든 제품도 버그가 많습니다.
그 들이 극찬하는 IOS나 OSX요? 네 그렇게 신중한 애플도 버그를 만듭니다.
특히나 많은 버그가 개발 과정에서 더더욱 많이 튀어나오는데, 이들 대부분이 개발자의 습관에서 비롯됩니다.

흔 히 많이 하는 실수가 초기화 오류(변수값의 초기화 값이 없다거나, 변수가 초기화 되는 시점이 잘못된 경우)라던가, 경계값 오류(임계치에 가까운 값을 잘못 설정한 오류), 연속 시도시 잘못된 결과가 도출되는 오류 (예를들면 결제 버튼 여러번 누르면 돈은 여러번 감소하고 아이템은 지급안된다던가하는 오류), 버그 수정시 다른 버그를 만들어 내는 문제 등이 있습니다.

생각보다 같은 종류의 실수를 자주 하는 사람이 많은데, 모 QA팀에서는 버그 스타일에 따라 담당자를 배정하는 사례도 있었다고 할 정도죠.
물론 어쩌다가 한번 나오는 버그는 개발자의 실수로 모두가 좋게 넘어가지만 같은 종류의 버그가 여러번 나오고, 제대로 된 수정이 이루어지지 않는다면 여러가지 측면에서 문제가 생깁니다.

QA팀에 없는 경우에는 개발팀 테스트 후 바로 서비스가 될 테니 더 말 할것 없고, QA팀이 있는 경우라해도 개발 과정의 버그도 줄여야 합니다.
반드시 그렇게 해야 하는 근거는 다음과 같습니다.

1. QA팀에 신뢰를 심어주기 위해
- 물론 QA팀은 몇번 같은 종류의 버그가 생겼다면 꼼꼼히 챙겨봐주신다. 하지만, 버그가 급격히 줄어든 걸 보면 뿌듯하고 개발팀에 신뢰도가 향상하게 되어있다.

2. 우리의 버그를 검증해주는 QA팀 마저도 놓칠 수 있기에
- 특히나 대규모 패치일 경우 더더욱 그런데, 체크 리스트 목록을 많이 작성하다보면 평소보다 테스트하는 디테일이 떨어질 수 있다.

3. 안좋은 습관은 긴급한 코드 작성시 극도로 심해지기 때문에

- 이건 주로 내가 그런데, 급하게 코드를 작성하다보면 안좋은 습관 (여러가지 측면을 다 고려해야 하는데, 현재 증상만 보고 버그를 수정한다거나 하는 경우)이 나온다.

4. 멍청하단 소리를 듣지 않기 위해서
- 같은 실수 여러번하면 멍청하다는 소리를 듣게 되있다. 듣기 싫다면 고쳐라.


이런 문제는 정상적인 개발자라면 대부분 2~3년차 때는 고치게 되는 편입니다. (안타깝게 비정상적인 개발자가 꽤 많죠...ㅠ_ㅠ 누구라고는 얘기 안하겠습니다.)


여하튼 개인의 습관에서 나오는 문제를 고쳐야 되는 이유에 대해 이야기 해봤습니다.
자 이제, 팀 차원에서의 버그 재발을 막기 위한 노력에 대해서 예를 들어 이야기 해보겠습니다.

개발자가 어느정도 숙련자 위주로 이루어져있는 팀이 있습니다. S프로젝트라고 지칭해보죠.

S프로젝트는 3년의 개발을 거쳐 OBT 직전에 와있는 사내에서도 주목받는 팀입니다. 그런데 이 팀에 인원이 부족하네요.
자 우리 회사는 어느정도 건실하고, 주목받는 팀에서 본보기가 되기 위해 신입도 뽑아보자고 마음을 먹었습니다.
10여명 면접을 보고 그 중 가장 성실하고 센스 있어 보이는 C를 뽑았습니다.

OBT가 시작됐습니다. 예상보다 반응이 좋습니다. 너무 반응이 좋다보니 유저가 폭발적이네요. 로그인이 잘 안된다고 합니다.
하지만 우리가 누군가요? 베타랑들 답게 능숙하게 트러블을 10분만에 빠르게 해결하고 정상화 시켰습니다.
게시판에서도 호평 일색입니다. 안정적인 서비스, 재미, 그래픽 모두가 만족스럽다고 합니다.
딱히 문제도 없다보니 OBT중이지만 이 행복한 팀은 OBT기간임에도 칼퇴근도 합니다.

반응이 좋지만 조금씩 불안해집니다. 미리 만들어놓은 컨텐츠가 1달치뿐이니까요. 슬슬 컨텐츠를 더 만들어야 할 때 입니다.
베테랑 개발자 A가 이 시기에 집안일로 어쩔 수 없이 자리를 비우게 됩니다.
해야 될 일은 많은데 한사람이 비어 어쩔 수 없이 C에게 컨텐츠 개발을 시켜봅니다.

그런데 똘똘해보이고, 센스 있어보이던 C가 실수 연발입니다.
개발과정이 A보다 몇배 오래 걸림은 물론이고, QA에서 발생한 버그를 한번에 수정하지 못하고 계속 여러번 오가게 만들어 QA도 늦게 끝나 야근하는 날이 늘어납니다.
다른 개발자들은 이 신입에게 불만을 토로합니다. 좀 모자란거 아니냐, 잘못 뽑은거 같다, 나 신입땐 안그랬다 등등...
팀 분위기는 조금씩 안좋아지고, 야근이 잦아지다보니 서비스 퀄리티는 조금씩 떨어집니다.
아 뭔가 잘못 되어가는거 같습니다. 우울해집니다.


노파심에서 말씀드리자면 이 이야기는 절대 신입을 뽑지 말자는 의미가 아닙니다.
상황이 이렇게 된데에는 잘 흘러가는 것처럼 보이던 S프로젝트 팀에 잠재적 문제가 있었습니다.


과연 이 팀의 문제가 무엇이었을까요?

1. 개개인의 차이에 맞지 않는 일정 수립
- 베테랑 개발자 A와 신입 개발자 C의 개발 속도 차이를 고려하지 않고 일정이 수립.
- 여기에 + @로 신입 개발자 C는 코드 분석 시간과 업무 방식에 대한 적응 기간도 필요한데 이마저 고려되지 않았음.

2. 개발 검수 과정이 없었음.
- 너무나 숙련된 개발자들로 이루어진 팀이었다보니, 개발 과정에 대해 누구도 관심을 갖지 않음. 각자의 할일에만 포커스가 맞추어져 있었음.
- 개발 검수가 없었다는 것은 코드 검수도 이루어 지지 않았다는 것이고, 신입 개발자 C가 작성한 코드가 지금껏 암묵적으로 지켜져오던 룰을 깼을 가능성도 있음.
- 분명 개발 검수 과정이 제대로 이루어졌다라고 한다면 적절한 로그를 남기도록 작업했을 터인데, 이마저도 이루어지지 않음.

3. 디버깅이란 경험이 많은 역할을 하는데, 이에 대한 리딩이 없었음.
- 디버깅은 정상 동작과 비정상 동작의 차이와 증상을 힌트로 원인을 찾아내는 과정이다.
- 신입 개발자인 C가 디버깅에 익숙할리가 없는데 이에 대한 방치로 인해 업무가 지연되었고, 이는 C가 아닌 개발팀이 만든 문제다.


실제로 이 팀에 내재된 문제는 더 있을 겁니다.
이런 상황을 만들었다는 것 자체가, 개발자 개개인의 역량에 지나치게 의존한다는 증거라고 볼 수 있기 때문입니다.

게다가 업무에 대한 교류와 검토가 이루어지지 않다보니, 베테랑 개발자 A라 할지라도 순간 정줄을 놓는 순간 버그를 양산할 여지가 다분합니다.
문제는 버그가 양산된다해도 우리의 희망 QA팀에서 대부분 잡아주시겠지만 개발 규약이라던지, 로그가 적절하게 취합되고 있는지, 예외 상황은 얼마나 잘 처리되었는지 등에 대한 검토마저 개개인의 역량에 맡기고 있다는 사실입니다.

요 새 PC가 워낙에 빠르다지만 클라이언트는 유저에게 배포되기 때문에, 서버는 많은 유저를 수용하고 그들의 요청을 처리해야 되기 때문에 속도도 중요한데, 검수가 없다는 것은 이 코드가 정상 상황에서 잘 돌아가기만 하면 된다고 여기고 있다는 얘기가 되는 겁니다.

이렇듯 꽤나많은 문제를 갖고 있는 이 팀이 문제들을 해결하기 위해선 몇가지 노력이 필요합니다.

1. 오류 보고 시스템
- M2 프로젝트에서 개발과정 도입되었다고 알려져 있는 이 시스템은, 나는 5명이상의 프로그래머가 함께 일하는 모든 개발팀에서 도입되어야 한다고 생각한다. 오류 보고 시스템을 적극 사용하는 과정중에 코드 검수가 이루어지며, 런타임 오류가 발생한 위치와 작성자, 원인 등을 명확히 파악하기에 유용하기 때문이다.

2. 개발 규약
- 암묵적이 아닌 개발 규약은 어느정도 존재할 필요가 있다. 뭐 띄어쓰기는 어떻게 하고, 네이밍은 어떻게하고 이런거보다는, 예외 처리는 어떻게 해야 하는지, 로그 기록에 대한 기준, 외부 라이브러리 도입 기준, 코드 관리 규약 (만들어져 있는 기능들에 대한 명세 및 관리) 등에 대한 이야기를 정리해놓고 손쉽게 관리하도록 노력하는 것이좋다.

3. 코드 리뷰
- 코드 리뷰를 어렵게 생각하지 마라. 개발 전에 작업 방향에 대해 이야기를 나눈 이후, 개발 테스트 이전이나 개발 테스트중 코드 검수를 요청하면 된다. 작업 방향에 대한 이야기가 선행되어 있기 때문에 전면적 리팩토링을 하게 될일은 없을 터이고, 개발 테스트 도중 발견됐을 버그나, 부분적인 코드 완성도를 끌어올리고 개발 규약에 맞는지, 유저에게 오류 발생시 어떻게 보여지고 어떻게 처리될지에 대한 고려를 중점적으로 보면 된다.
- 다만 지나치게 잦은 코드 리뷰와, 코드의 큰 방향성을 뒤흔드는 코드리뷰는 옳지 않다. 이는 개발 미팅 후 개발자들 간의 조율 과정에서 끝나야 한다.

4. 자동화 된 테스트
- 유닛테스트 + 모듈 테스트로 분리되어 얘기되기도 하는데, 분리 가능한 코드 조각을 테스트 하는 것이 유닛 테스트, 외부 프로그램 내지는 포함된 코드가 실제 동작중인 프로그램을 대상으로 반복된 테스트를 통해 새로 작성된 코드가 이미 작성된 기능을 망가뜨리진 않았는지, 또 새로 작성된 코드에 대한 테스트를 작성함으로써 해당 기능에 대한 이해도를 높이는 순작용도 가지고 있다.
-  또한 자동화된 테스트를 통해 크래시가 얼마나 자주, 또 매번인지 때때로 발생하는 지 등도 알아 낼 수 있다.

5. 자동화된 측정 시스템
- 자동화된 테스트에 함께 붙이면 되는 경우가 많으며, 성능 측정과 잔존물 측정이 함께 이루어져야 한다.
성능 측정은 perfmon과 같은 외부 프로그램을 통한 측정과, 코드 수행속도 측정, 부가 데이터 측정 (로그나 DB 데이터 등...)등으로 분류된다.
측정 대상은 상황마다 다르지만, 보통 퇴근시간에 측정 시스템과 테스트 시스템을 켜놓은 후 다음날 결과를 바탕으로 회의와 업무를 진행하는 식으로 이루어지는 것이 좋다.

이렇게 갖춰놓는다면 아무리 사고뭉치라해도 개발 과정에서 대다수를 캐치 할 수 있습니다.


자 이렇게 좋은 개발팀이 버그를 많이 만들지 않도록 구축해야 할 것들에 대해 이야기해보았습니다.
어느정도 디버깅에 대한 이야기를 마무리 한거 같네요. 아쉬운 부분이 좀 있지만, 디버깅에 대해 좀 더 진지하게 다시 할 얘기가 생겼을 때 연재하도록 하겠습니다.

다음 시간에는 디버깅 주제가 아닌 게임 개발 이야기로 찾아올게요~ 감사합니다 ^^

Posted by 엘키 엘키

댓글을 달아 주세요

이 글은 게임 데브 포에버 (http://gamedevforever.com) 에 기고하기 위해 작성된 글임을 알려드립니다.



안녕하세요~
오랜만에 다시 찾아뵙는 엘키입니다. 개인적으로 몸이 너무 좋지 않아 몸조리를 하고 다시 복귀했습니다. 이어지는 포스팅은 늦지 않도록 노력하겠습니다. (__) 꾸벅~

지난 포스팅에서도 이야기 했다시피 일반적으로 버그에 대한 보고는 자신이 겪은 증상에 대한 보고 입니다.

아주 행복한 시나리오는 보고 받은 대로 시도하면 100% 재현 되는 버그입니다.
이런 종류의 버그는 너무나 해결하기 쉬워 “훗~ 어떻게 고쳐줄까?” 라고 고민하는 것만 집중하면 되지요.

하지만 대다수의 잡기 힘들었던 버그는

1. “그냥 뜬금 없이 프로그램이 종료”
2. “같은 방법으로 재현하려 해도 매번 다른 동작을 보여주는 버그”
3. “보고 받은 대로 해도 재현 되지 않는 버그”

위와 같은 버그들입니다.

이번 포스팅에서는 이런 버그들을 잡는 법에 대해 알아보겠습니다.


기본적으로 제가 생각하는 디버깅은 추리입니다.


이를테면 이 녀석이랑 우리는 비슷한 일은 한다는 거죠~



버그를 잡는 과정은 다음과 같습니다.

1. A라는 오류가 생겼다.
2. 그로 인한 원인은 B이다. (로그 혹은 재현)
3. 해결책은 C다.


여기서 가장 중요한 것은 B를 증명하는 과정입니다.

B를 증명하지 못한다면 그저 우연에 맡기는 프로그래밍(실용주의 프로그래머에서 인용)으로 서비스를 하는 것일테죠.


명확히 로그로 A라는 오류가 생겼다는 로그가 발견됐다면? 별거 아닐겁니다. 아마 그럴겁니다.


물론 실수는 누구나 할 수 있기에 단순 실수였다니 다행이군~ 하고 넘어갑시다.

하지만 대부분의 버그는 부주의에서 나오고, 그런 부주의의 원인이 무엇인지를 알려주는 로그 따위는 기대하지 맙시다.

대부분의 디버깅 상황은 명확한 증상과 원인을 알려주는 로그 따위는 없는 경우가 많습니다. 주로 한정된 로그와 로직의 흐름을 통해서 이루어지는 것이 일반적입니다.

그래서 제한된 정보를 바탕으로 재앙의 원인을 찾아내는 과정을 거치게 되는데 이 과정을 추리라고 보시면 됩니다.

명탐정 코난 보셨죠? 아주 작은 실마리 하나를 놓치지 않고 범인을 찾아내는 명석한 두뇌!!
아, 물론 우리는 그 정도로 똑똑할 필요는 없습니다.

하지만 종종 그에 못지않은 추리와 상상력이 필요할 때가 있습니다.

대부분의 경우 힌트는 조각나있습니다. 조각난 힌트들을 퍼즐 맞추듯 하나 하나 끼워 맞추는 과정이 우리에겐 필요합니다.



예를 들어 볼까요?

게임하다 갑자기 클라이언트가 종료됐다는 보고가 왔습니다. 그런데 전체 유저도 아니고 소수 유저라고 하는군요. (10명 내외)

아! 덤프 서버!! 덤프 분석 페이지를 가 봅시다. 어라? 덤프가 안남았다니...최악이군요.

여기서 보통 2가지 반응을 보입니다.
1. “어? 뭐야? 보고가 잘못된거 아냐??”
2. “헐....뭐지 덤프 안남고 죽는 상황들에는 뭐가 있더라...뭐 여하튼 힌트가 될만한건?”

물론, 우리는 2번 반응을 보여야 합니다. 유저의 보고가 잘못됐다는 것 마저도 우리는 증명해야 합니다. 우리는 엔지니어니까요. ^-^


기술자(技術者, technician)는 어떤 분야에 공학적인 일에 숙련된 사람을 말한다. 반면 공학자(工學者, engineer)는 공학의 일에 자연과학적인 지식과 기술적인 지식을 가지고 과학자와 기술자 사이에 매개체가 되는 사람을 가리킨다. 공학자는 기술, 수학, 과학 지식을 사용하여 실용적인 문제를 해결한다. 공학자로 일하는 사람들은 보통 공학 분야에서 학위를 가지고 있다. 공학자는 자연과학적 지식에 기초하고 있기 때문에 기술자와 구분된다. 15-16세기에는 엔지니어란 군사 분야의 기술을 맡거나, 건축가, 수력학자, 조각가, 화가로서 재능을 빌려주는 사람이었다. [1]


출처 : http://ko.wikipedia.org/wiki/%EA%B8%B0%EC%88%A0%EC%9E%90



자~ 그럼 뭐라도 힌트를 얻어볼까요? 에러 로그 먼저 살펴보죠.


에러 로그를 봐도 별다른게 없습니다. 쳇...쉽지 않네요.

그 렇다면...추가적인 보고되는 상황도 뭔가 조금씩 다릅니다. 대부분 유저의 보고는 감정적으로 이루어지는 경우가 많아, 고의적이 아니더라도 현상을 다르게 보고하는 경향이 있습니다. (상대적으로 이성적으로 판단해주시는 QA 분들 마저 현상을 착각하는 경우도 종종 있습니다.)

이런 경우 공통점을 찾기 시작해야 합니다. 대부분 로그에서 결정적인 힌트는 없더라도 비슷한 점이 발견되는 경우가 종종 있습니다.

문 제가 발생한 모든 유저의 로그에서, 유저가 크래시 발생전에 했던 행동중 우편함을 열어보았다는 로그를 발견했습니다. (저의 경우에는 Disconnect 시점에서 최근 10개 패킷 번호를 로그로 남기는 구현을 했던 적이 있는데, 이 로그가 디버깅에 아주 큰 도움이 되곤했습니다.)

우편함을 뒤져볼까요?


아...뭔가 조금 이상하군요. C++ 구조체에 정의된 최대 크기는 64바이트, DB에서 기록 가능한 최대 메시지가 128바이트였군요.

DB에서 실제 사용된 메시지 크기를 기준으로 검색해봅시다.

아...53명이군요. 53명중 10명만 오류에 대한 보고를 한 것이고요.

아마 버퍼 오버플로우가 났었겠지요?

어라? 어쨰서 유저들의 보고에서는 우편함을 클릭했었다 라는 보고가 없었던 걸까요?

그리고 어째서 서버는 크래시 되지 않았던걸까요?



서버 코드는 다음과 같습니다.

typedef DWORD MAIL_IDX;

typedef DWORD USER_IDX;

class Mail : public ISyncroized
{
public:
   Mail (const MAIL_IDX Idx, const char* szMessage, const USER_IDX UserIdx)
   {
       m_Idx = Idx;
       m_szMessage = szMessage;
       m_UserIdx = UserIdx;
   }

   virtual bool Encode()
   {
       // 인코딩 작업 구현
   }
   

private:

   MAIL_IDX m_Idx;

   USER_IDX m_UserIdx;

   std::string m_strMessage;
};

Mail mail(Idx, szMessage, UserIdx);
pSession->Send(mail); // Send 함수 내부에서 ISyncroized 형 참조자를 통해 Encode된 정보를 유저에게 전달함.


자 그렇다면 클라이언트 코드를 볼까요?


const int MAX_MAIL_MESSAGE = 64;

typedef DWORD MAIL_IDX;
typedef DWORD USER_IDX;

class Mail
{
public:
   Mail(const MAIL_IDX Idx, std::string strMessage, const USER_IDX UserIdx)
   {
       m_Idx = Idx;
       strcpy(m_szMessage, strMessage.c_str());
       m_UserIdx = UserIdx;
   }

private:
   MAIL_IDX m_Idx;

   USER_IDX m_UserIdx;

   char m_szMessage[MAX_MAIL_MESSAGE];

private:
   IUIForm* m_pForm;

private:
   MailBox* m_pMailBox;
};

MAIL_IDX Idx = packet.Decode();

std::string strMessage = packet.Decode();

USER_IDX UserIdx = packet.Decode();

Mail mail(Idx, strMessage, UserIdx);

변수 배치상 우편함을 열자마자 오류가 생긴 것이 아니고, 그로 인한 2차 감염으로 문제가 생겼네요.

자! 찾았습니다.
신난다~~ 신난다~~~


그렇지만 좋아만 할때가 아니죠잉?


자. 어떠한 실수가 이런 문제를 만든 걸까요?
1. DB와 C++ 코드와 텍스트 길이 오차
-> 64 <-> 128
2. non-safe 한 c 표준 함수 사용. (strcpy)
-> 버퍼 길이를 지정하는 함수로 변경. (strncpy)

직접적인 원인은 위 두가지겠지만, 실제론 더 있다고 볼 수 있습니다.
1. 패킷에서 사용하기로한 자료형은 std::string에 Decode되는 자료 구조형과 달리 NULL-Terminated 문자열 사용.
-> 패킷을 데이터로 변환하는 함수에서 std::string을 지원한다면 굳이 NULL-terminated형을 사용할 필요가 없었음.

2. UI에서 제대로 표기가능한 문자열은 실제로 128byte였음.
-> 무언가 기획서의 반영이 안된것인가 찾아보았더니, 최초 UI폼은 64byte만큼 표시가 가능했으나, 이후 UI폼에선 128byte만큼 표기 가능하게 변경됨. C++ 코드에서의 UI변화 코드 수정 누락으로 인한 문제 발생.


이렇듯, 문제가 왜 발생했는가의 경위를 알아내는 것이 매우 중요합니다.
이런 실수들은 꽤나 큰 문제를 일으키곤하는데요, 방금전 버퍼 오버 플로우 관려련 코드가 로그인 시에 매번 수행되는 코드에서 사용되었다면 유저 대부분이 떨어져 나가버려 서비스는 엉망이 될 것이고, 덤프도 안남는 상황에서 (혹은 엉터리 같은 덤프가 남는 상황에서) 그 많은 유저 사이에서의 공통점을 찾기란 서울에서 김서방 찾기만큼 힘들었을겁니다.

“찾았다~~ 문제생긴 코드만 후딱 수정해서 패치하면 되지 뭐~.”

이렇게 대처해서는 안됩니다.


생각보다 많은 팀에서 버그나 크래시가 발생했을 때 이를 너무 크게다뤄 지나치게 혼을 낸다거나, “뭐 이런일도 있는거지” 하며 쉽게 넘어가죠.

실제로 필요한 대처는 버그가 발생한 코드 조각이 아니라, 진짜 단순 실수였는가, 개발자의 무지에서 온 것은 아닌가, 시스템으로 커버 불가능한 오류였는가, 개발 과정에서 캐치 할 순 없었는가 등을 다 고민해보아야 합니다.


이런 과정은 “좋은 서비스”를 하고 있는 개발 팀일 수록 체계화 되어있으며, 개개인의 실수가 팀 차원에서 커버되는 경우가 많습니다.

여하튼 버그를 찾았다면 그런 버그가 “다시는” 나오지 않게 하기 위한 여러가지 고민을 할 차례입니다.

이런 고민에 대한 이야기를 다음 글에서 이어서 하겠습니다.

Posted by 엘키 엘키

댓글을 달아 주세요


이 글은 게임 데브 포에버 (http://gamedevforever.com) 에 기고하기 위해 작성된 글임을 알려드립니다.

안녕하세요. 엘키라고 합니다. (절대 베르트랑 따라한거 아님!!)

제가 즐기는 게임은 제 블로그를 통해서 확인하실수 있으니, 저와 같이 플레이하실분 늘 환영합니다 ^^ (이렇게 홍보해도 캡파말고는 아는척 하는분 한분이 안계시더라는....쿨럭!!!)


이 블로그를 보시는 분들 다들 뛰어나고 다양한 경험을 많이 갖춘 분들이시겠지만, 제 경험담을 바탕으로 디버깅에 대해 알아보는 시간을 가져보고자 이렇게 나섰습니다. ^^

우리는 모두 사고 뭉치입니다. 사람이란 실수할 수도 있다는 것을 전제로 이 글을 작성 하고자 마음 먹었죠. (정확성이 떨어지는 발로 하는 스포츠인 축구계의 명언에서 인용했습니다.)

그래서~ 언제나 실수할 여지를 갖고 있는 우리 모두를 위해, 실수를 했을 때 이를 빨리 수습하기 위해 해야 되는 작업이 디버깅이라는 의미로, 사고뭉치들을 위한 디버깅 방법이라 이름을 지었습니다.


흔히 디버깅이라하면, VS Debugger, Windbg, Ollydbg 등의 디버거를 이용하는 것에만 집중 하시는데요, 이 글에서는 디버깅 툴에 의존하지 않는 범용적인 디버깅 과정에 대해 썰을 풀어볼 계획입니다.

이런 창을 보여주지 않는 코딩에 대한 글은 절대 아닙니다. 이런 상황의 원인을 찾고 해결하기 위한 글입니다.



프로그래머가 디버깅을 하게 되는 상황은 주로 다음과 같습니다.

1. 누군가에서 버그 상황을 전달 받는다. (혹은 자동화된 시스템을 통해)
2. 직접 겪는다.


그렇다면, 디버깅 해야 될 상황은 어떤게 있을까요?


1. 프로그램이 비정상 종료 내지는 멈춘다.
2. 사용자가 기대하지 않은 상황과 맞이한다.


비정상 종료 내지는 멈췄을 땐 어떻게 해결 해야 될까요?

1. 덤프가 남았는지 확인한다.
2. 로그를 확인한다.



덤프가 안남았을땐, 무한 루프에 빠지거나 dead-lock 에 빠지지 않았는지 확인해야 합니다.

dead-lock : 코어 점유는 없고, 데드락이 걸린 스레드 몇개만 잠긴다.
무한 루프 : 코어 1개를 점유 하고 있음.


그래서 dead-lock 을 회피하기 위해 모든 스레드를 잠깐씩 거치는 falling-thread를 놓고, 해당 thread가 잠겼는지 확인하는 방식으로 구현하기도 합니다.

falling-thread가 멈추면 어떠한 특정 thread 인해 dead-lock이 일어났다고 판단할 수 있기 때문입니다.

물론 dead-lock 상황은 userdump와 같은 프로그램을 통해 덤프를 생성함으로써 확인 가능하지만 이보다 빠른 대처와 판단이 가능해지는 장점이 있지요.

한가지 팁을 드리자면, 모든 thread는 생성시 thread의 역할과 thread-id 를 로그로 기록하는 것이, 디버깅시 여러모로 유용합니다.

windbg 등의 툴이나 로그등에서 확인할 때 이 정보는 매우 유용해 지죠. (스레드별 로깅을 하는 방식도 나름 괜찮음)또한 크리티컬섹션(CRITICAL_SECTION)객체에는 Ownering Thread 정보가 존재하기에 이를 바탕으로 한 잠긴 Thread를 확인할수 있기 때문이죠.


어쨋거나 dead-lock, 무한루프 모두 dump를 생성하거나 debugger로 attach해서 원인 파악이 가능합니다.


만약 덤프가 남았고 크래시 내지는 예외 발생 후 스택 되감기(Stack Unwinding)를 통해 정상 동작하고 있을땐 어떻게 해야 될까요?


덤프가 남았지만 정상 동작하는 상황
-> 스택 오버 플로우가 났을 때 (새로운 Thread를 만들어서, 기존 익셉션 정보를 처리해야함. ExceptionProcess를 위해 스택을 구성하는 과정에서 오류가 생기기 때문)
-> 잘못된 함수 포인터 콜
-> 스택 메모리 덮어썼을때 (스택 깨먹었다고도 하죠)
-> Divide Zero
-> NULL 포인터 접근
-> 같은 메모리에 delete 두번


크래시가 나는 상황 (절대적이진 않음)
-> 스택 되감기 도중 예외 발생.
-> 잘못된 포인터 캐스팅을 통한 가상 함수 콜

-> 잘못된 함수 포인터 콜
-> 변수 값 덮어 썼을때 (주로 가상함수 포인터 테이블을 덮어썼을때 크래시가 남.)
-> 잘못된 포인터 접근


덤프가 남았을 경우는 대부분이 단순 실수가 많습니다. NULL 포인터 참조 오류와 같은 경우나 잘못된 포인터 참조는 꽤나 자주 겪게 되는 단순 실수에 해당 되기 때문에, 발견하게 되면 바로 고칠 수 있는 단순 버그에 속합니다.

당연한 얘기겠지만, 스택 되감기란 아주 단순한 동작이 아니기 때문에 이로 인한 무한 루프에 빠지는 경우도 종종 생깁니다.

예외 처리를 위해 감싸는 로직의 단위를 잘 판단할 필요가 있습니다.


애매모호한 동작을 보이지만 잘못된 포인터 캐스팅은 상대적으로 찾기 쉬운 편입니다. 해당 포인터를 강제 캐스팅 해야 될 만큼 잘못된 설계가 되어있는 경우가 대다수지만, 피치 못할 사정 상 그렇게 구현 했다고 해도, 캐스팅을 위한 검증 값 체크만 잘하면 적어도 크래시가 나지 않게 동작 시키는 것은 무리가 아니지요.

덤프가 남았는데, 콜스택이 영~ 이상하다. 이럴 때 참 난감합니다.



이러한 상황을 만드는 데에는 변수 값 덮어쓰기가 주범이 되곤 합니다.

덮어 써진 메모리가 유효한 위치 (프로그램 내에서 할당된 영역)이라면 예외가 발생하지 않습니다.

이런 상황을 감염이라 부르는데, 문제가 생긴 곳에서 바로 증상이 생기지 않고, 영~ 쌩뚱 맞은 곳에서 오류가 생기기 때문입니다. (버그로 발현되기도 하고, 크래시가 되기도 함)

그래서 흔히 감염이 최악의 버그라고 평하곤 하지요.

가장 주의해야 될 버그이고, 가장 찾기 힘든 버그입니다. 그래서, 좋은 프로그래밍 습관을 필요로 하지요.

흔히 c스타일의 코딩(memcpy, strcpy, 포인터 직접 조작 등)에서 자주 발생하는 문제입니다. 코어 레벨의 코딩에서는 여러가지 상황으로 인해 포인터 조작이나 memcpy를 할 수 밖에 없다고 하더라도, 컨텐츠 단에서는 반드시 safe한 함수를 사용할 것을 권장합니다.

이외에도 예외를 발생 시키지 않는 프로그래밍 습관에 대한 이야기도 해보고 싶은데 이 글의 주제를 벗어나기 때문에 기회 되면 따로 글을 작성해보고 싶네요.


자~ 여기까지 내용이 서론이었습니다. 진짜 하고 싶은 이야기인 잡기 힘든 버그를 해결하는 과정에 대한 썰을 다음 글에서 풀어보고자 합니다.


다음 시간에 만나요~ 제발~~~


'C++ > Debugging' 카테고리의 다른 글

사고뭉치를 위한 디버깅 방법 #03  (0) 2012.05.21
사고뭉치를 위한 디버깅 방법 #02  (0) 2012.05.09
사고뭉치를 위한 디버깅 방법 #01  (2) 2012.02.06
Map 파일로 함수/라인 찾기  (0) 2010.09.14
Windbg 사용법  (0) 2010.03.30
Application Verifier 적용하기  (0) 2010.02.09
Posted by 엘키 엘키

댓글을 달아 주세요

  1. 초보자 2012.02.21 20:44  댓글주소  수정/삭제  댓글쓰기

    좋은 자료 감사합니다.

  2. 2012.03.29 14:23  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

pdb를 배포하는 것은 너무나 쉽게 리버싱을 허용하는 것이기 때문에, 조금이라도 리버싱을 어렵게 하기 위해선 exe 파일만 배포해야 합니다.
보통 서버의 경우 디버깅 정보를 매칭 시키는 수단으로 pdb도 바이너리와 같이 두기도 하는데요, 이렇게 하는 것에는 장단점이 있기에 이에 대한 코멘트는 생략하겠습니다.

pdb가 없을때와 있을때 VS2005를 통한 덤프 확인시에 어떻게 다른지 보여드리겠습니다.

정상적으로 매칭되는 pdb 파일이 존재할 때 남은 덤프 파일의 콜스택과 스레드 소스 코드 매칭 정보.

pdb 파일이 존재하지 않을때 남은 덤프 파일의 콜스택과 스레드.


이렇게 차이가 납니다. 실제로 pdb 파일이 없을 시에는 어느 함수에서 크래시가 났는지 확인하기 매우 어렵죠.
위에 크래시 난 곳의 주소를 보시면, 004010f5 입니다.

이 정보를 바탕으로 크래시 주소를 찾아보겠습니다.
004010f5 는 위 두 함수 사이의 주소입니다. 그 중에서 정확한 크래시 함수는 보다 작은 주소에 존재하는 함수입니다.
왜냐하면, 위 주소는 함수 주소 + 라인으로 구성되어 있기 때문이죠.


이렇게해서 찾은 함수는 Main 클래스의 Constructer 함수라는 것을 알 수 있었습니다.
여기서 어느 라인에서 문제가 발생했는지 보고 싶으시다면,  "Preferred load address" 를 알아내야 합니다.


찾아보니 00400000 이군요~ 이를 바탕으로 정확한 라인도 찾아 볼까요?

라인 정보는 아래 공식을 통해 얻어낼 수 있습니다.

Line number information = crash address - Preferred load address - the Size of PE
f5 = 004010f5 - 0040000 - 0x1000
바로 f5가 line number information 입니다.

하지만 안타깝게도, 2005버전 이상에서는 위 정보를 바탕으로 라인 정보를 얻어 낼 수 없습니다.
Mapfile에 line information을 export 하는 /MAPINFO:LINES 옵션이 2005 이상에서는 지원하지 않기 때문입니다.

이에 대한 내용이 밑에 링크에 논의되어있습니다. 허나 맘에 드는 정보는 찾을 수 없었습니다. (MAP파일에 LINE 정보를 export 하는)

오히려 msdn 토론보다 알찬 정보는 티스토리에 있었습니다.

Debugging Tips (1) - .map 파일과 .cod 파일 분석하기

위 링크에는 cod 파일을 생성하는 법도 포함되어있습니다.

여기서 제가 좀 헤멘 부분은, cod 파일내의 어셈블 코드를 얻는 방법이었어요.

35 = 004010f5 (Crash Address) - 004010c0 (Function Address) 을 해야 원하는 값이 나왔습니다.
그래서 나온 값이 35. 이 값은 Main.cod 파일 안의 Constructer 함수내에 어셈블 코드 라인을 의미하죠.


보시면, 00035에 해당하는 어셈블 코드는 idiv ecx 입니다. 이 코드에서 integer divide zero 가 발생했고, 그래서 크래시가 발생한 것이라고 알 수 있었습니다.

이렇게 충돌 주소만으로 크래시 난 곳을 알아내는 방법을 알아보았는데요, 사실 요즘은 이보다 pdb를 이용하는 추세이긴하지만, 이런 방법도 있고 나름의 활용법도 있지 않을까 하는 의미에서 정리해보았습니다.

긴 글 읽어주셔서 감사합니다 (__) 꾸벅~

참고 문서 -
Finding crash information using the MAP file (VS 2003 .net 기준)
http://www.codeproject.com/KB/debug/mapfile.aspx

'C++ > Debugging' 카테고리의 다른 글

사고뭉치를 위한 디버깅 방법 #02  (0) 2012.05.09
사고뭉치를 위한 디버깅 방법 #01  (2) 2012.02.06
Map 파일로 함수/라인 찾기  (0) 2010.09.14
Windbg 사용법  (0) 2010.03.30
Application Verifier 적용하기  (0) 2010.02.09
Userdump로 Dump 생성 하는 방법  (0) 2008.05.09
Posted by 엘키 엘키

댓글을 달아 주세요

2010. 3. 30. 13:47 C++/Debugging

Windbg 사용법

참고 자료

Windbg Tutorial
http://www.codeproject.com/KB/debug/windbg_part1.aspx

Windows에서 디버깅을 위해 만든 작은 메모리 덤프 파일을 읽는 방법
http://support.microsoft.com/default.aspx?scid=kb;ko;315263

Windbg로 덤프 남기는법
http://msdn.microsoft.com/en-us/library/ff562428.aspx

잊지 말아야 할 점은, windbg는 symbol과 source code의 매칭을 해주지 않는다.
반드시 Symbol File Path와, Source File Path 메뉴 에서 경로를 제대로 지정해주어야, 덤프 분석시 정보가 제대로 출력된다. 

windbg shell command

덤프 생성
.dump /f C:\경로\파일명.dmp

덤프 정보 분석
!analyze [-v] 

지역변수 보기
dv 

콜스택 보기
kb 

콜스택에 번호 매겨서 보기
kn 

스택 프레임 번호에 맞추기 (0번부터 시작)
.frame [번호]

Display Exception Context Record
.ecxr

레지스터 보기
r

디스 어셈블해서 보기
u [주소]

직접 메모리 내용 보기
dd [주소]

메모리의 내용과 심벌을 일치시켜 보여준다.
dds [주소]

프로세스 리스팅
!process 0 0

프로세스 어태치 시키기
.process /i [pid]


할당된 가상메모리 덤프

올리디버거의 Memory Map윈도의 기능

!vadump [-v]


해당메모리 주소가 어떤 속성인지 알려줌

 !vprot [주소]


현재 프로세스내에 동작중인 스레드의 스택을 보여줌

 !uniqstack [-b]

-b옵션을 주면 스택에 담긴 아규먼트까지 보여준다


현재 스레드에 할당된 권한(Privilege)를 보여줌

!token


각 스레드가 동작한 시간

 !runaway


레지스트리 정보 확인

 !dreg

예) 0:000> !dreg System\CurrentControlSet\Services\Tcpip!*


해당 주소를 UNICODE_STRING구조체 형식으로 살펴봄

!ustr [주소]

 typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING;


해당 주소를 ANSI_STRING혹은 OEM_STRING구조체로 살펴봄

!str [주소]

 typedef struct _STRING {
    USHORT Length;
    USHORT MaximumLength;
    PCHAR Buffer;
} STRING;
typedef STRING ANSI_STRING;
typedef STRING OEM_STRING;



스레드 로컬 스토리지 슬롯을 살펴봄

!tls

모든 슬롯 출력

예) !tls -1

Specifies the thread environment block (TEB). If this is 0 or omitted, the current thread is used

예) !tls 0


현재 스레드의 TEB정보를 출력

!teb


현재 프로세스의 PEB정보를 출력

!peb


잘 알려진 몇몇 STL템플릿정보를 출력

!stl


로딩된 dll모듈의 베이스주소와 길이 모듈명을 출력한다

lm


해당모듈의 자세한 정보를 출력

!lmi [모듈]

예) !lmi 00400000


반복적인 디버거명령을 실행시키면서, 링크드리스트 정보를 출력함

!list

예) !list "-t ntdll!_LIST_ENTRY.Flink -e -x \"dd @$extret l4; dt ntdll!_RTL_CRITICAL_SECTION_DEBUG @$extret-0x8\" ntdll!RtlCriticalSectionList"


현재 Shared User-mode Page를 출력함

현재 타임존과 시스템루트, TickCount와 시간을 출력함

!kuser


로드된 모듈들의 리로케이션되기전의 주소를 출력한다

!imgreloc [주소]


최근 에러코드를 리턴한다

!gle


에러코드를 가지고 무슨 에러인지 설명을 보여준다

!error [에러코드번호]


글로벌 플래그를 설정 혹은 보여준다

!gflag


로드된 모듈들에 대한 커스터마이징(?)된 출력을 해준다.

!for_each_module ["명령어"]

예) !for_each_module .echo @#ModuleIndex : @#Base @#End @#ModuleName @#ImageName  @#LoadedImageName

로드된 모듈에서 MZ로 시작되는것을 찾는다
예) !for_each_module s-a @#Base @#End "MZ"


가상메모리에서 이미지헤더를 검색한다 (MZ검색)

.imgscan


표현식을 헥사, 8진수, 2진수, 시간형, Float형, Double 형으로 변환한 형태로 보여준다

.formats [표현식]

디버그 레지스터 확인
0:000> rm 0x20;r
dr0=00000000 dr1=00000000 dr2=00000000
dr3=00000000 dr6=00000000 dr7=00000000
ntdll!KiFastSystemCallRet:
7c93eb94 6a01            push    1

범용레지스터 확인
0:000> rm 0x01;r
eax=00000000 ebx=00000000 ecx=00000006 edx=7c9ac080 esi=7c93e88e edi=00000000
eip=7c93eb94 esp=0007fde8 ebp=0007fee4 iopl=0         nv up ei pl zr na pe nc
ntdll!KiFastSystemCallRet:
7c93eb94 6a01            push    1

레지스터 값 변경
r eip=7c931230 
r eax = @ebx
r zf=0


특정 주소에 어셈블 코드 삽입
a <위치>
예) a eip
00401000    sub esp, 10


특정 주소에 원하는 값 삽입
e[옵션] <주소>
예) eb <주소>
00401000 90
00401001 90
00401002 90


현재 보여주는 숫자의 진수바꾸기
n <base>
예) n 8
예) n 16       # 16진수
예) n 10       #10진수로 보여줌


메모리가 참조하고 있는 데이터를 살펴보기
예) dpa esp             현재 스택을 아스키형태로 보여줌
예) dpu esp             현재 스택을 유니코드형태로 보여줌


메모리의 내용을 심벌과 매핑시켜서 보여줌
예) dds esp            현재 콜 스택을 보여줌


'C++ > Debugging' 카테고리의 다른 글

사고뭉치를 위한 디버깅 방법 #02  (0) 2012.05.09
사고뭉치를 위한 디버깅 방법 #01  (2) 2012.02.06
Map 파일로 함수/라인 찾기  (0) 2010.09.14
Windbg 사용법  (0) 2010.03.30
Application Verifier 적용하기  (0) 2010.02.09
Userdump로 Dump 생성 하는 방법  (0) 2008.05.09
Posted by 엘키 엘키
 TAG WinDbg

댓글을 달아 주세요

Download
http://www.microsoft.com/DownLoads/details.aspx?familyid=C4A25AB9-649D-4A1B-B4A7-C9D8B095DF18&displaylang=en

Testing Applications with AppVerifier
http://msdn.microsoft.com/en-us/library/ms807121.aspx

Using Application Verifier Within Your Software Development Lifecycle
http://msdn.microsoft.com/en-us/library/aa480483.aspx#appverifier_sdl_topic2

'C++ > Debugging' 카테고리의 다른 글

사고뭉치를 위한 디버깅 방법 #02  (0) 2012.05.09
사고뭉치를 위한 디버깅 방법 #01  (2) 2012.02.06
Map 파일로 함수/라인 찾기  (0) 2010.09.14
Windbg 사용법  (0) 2010.03.30
Application Verifier 적용하기  (0) 2010.02.09
Userdump로 Dump 생성 하는 방법  (0) 2008.05.09
Posted by 엘키 엘키

댓글을 달아 주세요

이전버튼 1 2 이전버튼

블로그 이미지
Software Engineer
엘키

공지사항

Yesterday31
Today29
Total1,605,483

달력

 « |  » 2020.8
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          

글 보관함