Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 3개 있습니다.)
(시리즈 글이 3개 있습니다.)
디버깅 기술: 39. DebugDiag 1.1을 사용한 덤프 분석
; https://www.sysnet.pe.kr/2/0/1026

디버깅 기술: 65. 프로세스 비정상 종료 시 "Debug Diagnostic Tool"를 이용해 덤프를 남기는 방법
; https://www.sysnet.pe.kr/2/0/1786

디버깅 기술: 137. 실제 사례를 통해 Debug Diagnostics 도구가 생성한 닷넷 웹 응용 프로그램의 성능 장애 보고서 설명
; https://www.sysnet.pe.kr/2/0/12067




프로세스 비정상 종료 시 "Debug Diagnostic Tool"를 이용해 덤프를 남기는 방법

일례로, w3wp.exe 프로세스가 원인도 모르게 죽는(crash) 경우가 있다면 어떻게 분석해야 할까요? 제가 생각할 수 있는 가장 좋은 방법은 풀 덤프를 뜨는 것입니다.

비정상 종료되는 경우가 일정한 규칙이 있고 해당 예외 타입을 알 수 있다면 procdump.exe를 이용해 쉽게 덤프를 뜰 수 있습니다. 이에 대해서는 저번에 한번 설명드렸죠? ^^

닷넷 응용 프로그램에서 특정 예외가 발생했을 때 풀 덤프받는 방법
; https://www.sysnet.pe.kr/2/0/1376

그런데, 말 그대로 비정상 종료를 해버리는 경우는 어떻게 덤프를 남겨야 할까요? 이를 위해 가장 쉬운 방법은 마이크로소프트에서 제공하는 "Debug Diagnostic Tool"을 사용하는 것입니다.

예를 들면서 한번 설명해 볼까요? ^^

닷넷 환경은 그런 경우가 많지 않기 때문에 최대한 실제 상황에 가깝도록 C/C++ Win32 DLL에서 crash를 발생시키는 예제를 만들어 보겠습니다. 우선, C/C++ 코드는 이렇게 구성하고,

#include "stdafx.h"
#include "Win32Project1.h"

DWORD WINAPI crashThread(LPVOID lpParameter)
{
    while (true)
    {
        HANDLE hWait = ::OpenEvent(SYNCHRONIZE, FALSE, L"test.event");
        if (hWait == NULL)
        {
            Sleep(1000);
            continue;
        }

        ::WaitForSingleObject(hWait, INFINITE); // "test.event"가 시그널되면!

        *((int*)0) = 0; // 비정상 종료!
    }

    return 0;
}

WIN32PROJECT1_API int _stdcall fnWin32Project1(void)
{
    HANDLE hHandle = ::CreateThread(NULL, 0, crashThread, NULL, 0, NULL);
    if (hHandle != NULL)
    {
        CloseHandle(hHandle);
    }

    return 42;
}

닷넷에서 P/Invoke를 이용해 fnWin32Project1 함수를 호출하는 페이지를 default.aspx로,

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Web.UI;

namespace WebApplication1
{
    public partial class _Default : Page
    {
        [DllImport("Win32Project1.dll")]
        static extern int fnWin32Project1();

        public static EventWaitHandle _event = new EventWaitHandle(false, EventResetMode.ManualReset, "test.event");
        protected void Page_Load(object sender, EventArgs e)
        {
            fnWin32Project1();    
        }
    }
}

"test.event" 이벤트를 시그널하는 페이지를 about.aspx로 두겠습니다.

using System;
using System.Web.UI;

namespace WebApplication1
{
    public partial class About : Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            _Default._event.Set();
        }
    }
}

이 웹 응용 프로그램을 IIS에서 호스팅하고 default.aspx를 방문한 다음 이어서 about.aspx를 방문하면 w3wp.exe가 비정상 종료되면서 사라집니다. 이때의 이벤트 로그를 보면 비정상 종료되었다는 항목은 남지만 막상 오류의 원인을 파악하려면 막막하기만 합니다.




웹 서버가 이런 상황이면 정말 난감한데요. 이때 필요한 것이 바로 "Debug Diagnostic Tool"입니다. 다음의 경로에서 최신 버전을 다운로드 받고,

Debug Diagnostic Tool v2 Update 3
; https://www.microsoft.com/en-us/download/details.aspx?id=58210

시작 메뉴를 통해 "DebugDiag 2 Collection"을 실행하면 다음과 같이 "Rule Type"을 선택할 수 있는 창과 함께 프로그램이 실행됩니다.

howto_debug_diag_1.png

당연히, "Crash" 유형을 선택하고 "Next" 버튼을 누르면 이제 덤프를 남길 응용 프로그램 대상의 종류를 선택하는 창으로 이동합니다.

howto_debug_diag_2.png

여기서는 w3wp.exe를 감시할 텐데요. 이 중에서 문제가 발생한 웹 애플리케이션을 알고 있으므로 좀 더 범위를 좁히기 위해 "A specific IIS web application pool"을 선택했습니다. (여러분들은 개별 상황에 맞게 다른 타입을 선택하시면 됩니다.)

제 경우 "AppPool"을 선택했으므로 이제 어떤 AppPool을 감시해야 할지를 선택하게 되는데요. 저는 테스트로 구성한 웹 사이트가 "TestSite" AppPool에 있기 때문에 아래와 같이 선택했습니다.

howto_debug_diag_3.png

그리고 나오는 세부 항목 설정에서는 기본값으로 두고 넘어갑니다.

howto_debug_diag_4.png

참고로, 위의 설정에서 "Unconfigured First Chance Exceptions"는 가능한 선택하지 않는 것이 좋습니다. "First Chance Exception"은 상황에 따라 얼마든지 발생할 수 있기 때문에 이것을 켜두면 너무나 잦은 로그(또는 덤프)가 남게 되는데 별다르게 도움이 되지 않습니다. 그것보다는 우측 하단의 "Exceptions..."를 눌러 나오는 예외 종류를 세분화시켜서 선택하는 것이 더 도움이 될 수 있습니다.

하지만, 비정상 종료되는 지금의 상황에서는 굳이 선택할 필요는 없습니다. 이제 다음 단계로 넘어가면 "규칙(Rule)"의 이름과 해당 규칙이 적용되어 덤프 및 로그를 남겨야 하는 폴더를 지정할 수 있는 창이 나오는데요.

howto_debug_diag_5.png

자신의 상황에 맞게 지정해 주시면 됩니다. 그 다음은 해당 규칙을 활성화 시킬지를 묻는데,

howto_debug_diag_6.png

당연히 "Activate the rule now"를 선택합니다. 이 창을 마지막으로 "Finish" 버튼을 누르면 "Debug Diagnostic Tool" 프로그램의 "Rules" 탭에 방금 생성한 규칙이 활성화 된 것을 확인할 수 있습니다.

howto_debug_diag_7.png

일단, 이렇게 활성화시켰으면 이제 서버에서 로그아웃을 해도 됩니다. 왜냐하면 DbgSvc.exe라는 NT 서비스가 덤프 모니터링을 책임지기 때문에 굳이 서버에 연결되어 있을 필요가 없습니다. (오~~~ 운영 서버에서 언제 발생할지 모르는 crash 덤프를 남길 때 정말 필요한 기능입니다. ^^)

재미있는 점은, 크래시 모니터링 대상이 되는 w3wp.exe 프로세스 하나 당 DbgHost.exe 프로세스가 하나씩 대응된다는 점입니다. 그래서 WebGarden을 설정해 AppPool 하나에 3개의 작업자 프로세스가 실행하도록 구성한 경우라면 DbgSvc.exe 프로세스와 함께 3개의 DbgHost.exe가 함께 떠 있게 됩니다.

"Debug Diagnostic Tool"에 대해 제가 가장 마음에 드는 부분이 있는데요. 똑똑하게도 iisreset이나 작업관리자를 통해 강제 종료하는 경우는 덤프를 남기지 않는다는 특징이 있습니다. 이것 역시 운영 서버에서 제대로 된 crash 덤프를 선택하는데 정말 필요한 기능입니다. ^^




이렇게 구성한 상태에서, 예제 웹 응용 프로그램의 default.aspx, about.aspx를 차례대로 방문하면 c:\temp 폴더에 덤프 파일이 남게 됩니다. 이 덤프 파일이 있으면 어느 정도까지 도움이 될지 감이 잘 안 오실 텐데요.

대개의 경우 '빌드 서버'를 통해 웹 응용 프로그램을 빌드할 것이므로, 바로 그 빌드 서버에 설치된 Visual Studio 2013에서 "File" / "Open" / "Crash Dump..." 메뉴를 이용해 덤프 파일을 열면?

다음과 같이 정확하게 문제를 발생시킨 소스 코드 위치까지 보여주면서 디버깅 상태로 진입합니다.

howto_debug_diag_8.png

그야말로, 놀라운 디버깅 능력입니다! ^^

(첨부한 파일은 이 글의 예제 프로젝트를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/10/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2017-11-01 09시45분
[guest] 증권회사에서 제공하는 API중 COM 버전을 이용해서 프로그램을 개발하고 있습니다.

http://res.sysnet.pe.kr/sysnetimages/howto_debug_diag_2.png

위 링크 이미지의 선택창에서 A specific COM+ application 를 선택하면 되나요?
[guest]
2017-11-01 10시15분
[guest] "Debug Diagnostic Tool"에 대해 제가 가장 마음에 드는 부분이 있는데요. 똑똑하게도 iisreset이나 작업관리자를 통해 강제 종료하는 경우는 덤프를 남기지 않는다는 특징이 있습니다. "

부분을 적어 놓으셨는데요.

프로그램 코드 내에서 application.exit 또는 application.restart 하는것도 잡히지 않나요?
[guest]
2017-11-02 12시32분
첫 번째 질문은, 거기서 말하는 COM+ 응용 프로그램은 서버 유형으로 뜨는 독립 .exe COM+를 의미합니다. 따라서 일반 프로세스라면 그냥 "A specific process"를 선택하세요.

두 번째 질문은, 직접 해보시고 답을 달아주시면 되지 않을까요? ^^
정성태

... 121  122  123  124  125  126  127  128  129  130  131  [132]  133  134  135  ...
NoWriterDateCnt.TitleFile(s)
1756정성태9/23/201427487기타: 48. NVidia 제품의 과다한 디스크 사용 [2]
1755정성태9/22/201434280오류 유형: 241. Unity Web Player를 설치해도 여전히 설치하라는 화면이 나오는 경우 [4]
1754정성태9/22/201424659VC++: 80. 내 컴퓨터에서 C++ AMP 코드가 실행이 될까요? [1]
1753정성태9/22/201420611오류 유형: 240. Lync로 세미나 참여 시 소리만 들리지 않는 경우 [1]
1752정성태9/21/201441071Windows: 100. 윈도우 8 - RDP 연결을 이용해 VNC처럼 사용자 로그온 화면을 공유하는 방법 [5]
1751정성태9/20/201438951.NET Framework: 464. 프로세스 간 통신 시 소켓 필요 없이 간단하게 Pipe를 열어 통신하는 방법 [1]파일 다운로드1
1750정성태9/20/201423832.NET Framework: 463. PInvoke 호출을 이용한 비동기 파일 작업파일 다운로드1
1749정성태9/20/201423733.NET Framework: 462. 커널 객체를 위한 null DACL 생성 방법파일 다운로드1
1748정성태9/19/201425385개발 환경 구성: 238. [Synergy] 여러 컴퓨터에서 키보드, 마우스 공유
1747정성태9/19/201428493오류 유형: 239. psexec 실행 오류 - The system cannot find the file specified.
1746정성태9/18/201426106.NET Framework: 461. .NET EXE 파일을 닷넷 프레임워크 버전에 상관없이 실행할 수 있을까요? - 두 번째 이야기 [6]파일 다운로드1
1745정성태9/17/201423037개발 환경 구성: 237. 리눅스 Integration Services 버전 업그레이드 하는 방법 [1]
1744정성태9/17/201431064.NET Framework: 460. GetTickCount / GetTickCount64와 0x7FFE0000 주솟값 [4]파일 다운로드1
1743정성태9/16/201420985오류 유형: 238. 설치 오류 - Failed to get size of pseudo bundle
1742정성태8/27/201426973개발 환경 구성: 236. Hyper-V에 설치한 리눅스 VM의 VHD 크기 늘리는 방법 [2]
1741정성태8/26/201421334.NET Framework: 459. GetModuleHandleEx로 알아보는 .NET 메서드의 DLL 모듈 관계파일 다운로드1
1740정성태8/25/201432523.NET Framework: 458. 닷넷 GC가 순환 참조를 해제할 수 있을까요? [2]파일 다운로드1
1739정성태8/24/201426555.NET Framework: 457. 교착상태(Dead-lock) 해결 방법 - Lock Leveling [2]파일 다운로드1
1738정성태8/23/201422050.NET Framework: 456. C# - CAS를 이용한 Lock 래퍼 클래스파일 다운로드1
1737정성태8/20/201419765VS.NET IDE: 93. Visual Studio 2013 동기화 문제
1736정성태8/19/201425579VC++: 79. [부연] CAS Lock 알고리즘은 과연 빠른가? [2]파일 다운로드1
1735정성태8/19/201418229.NET Framework: 455. 닷넷 사용자 정의 예외 클래스의 최소 구현 코드 - 두 번째 이야기
1734정성태8/13/201419888오류 유형: 237. Windows Media Player cannot access the file. The file might be in use, you might not have access to the computer where the file is stored, or your proxy settings might not be correct.
1733정성태8/13/201426363.NET Framework: 454. EmptyWorkingSet Win32 API를 사용하는 C# 예제파일 다운로드1
1732정성태8/13/201434480Windows: 99. INetCache 폴더가 다르게 보이는 이유
1731정성태8/11/201427086개발 환경 구성: 235. 점(.)으로 시작하는 파일명을 탐색기에서 만드는 방법
... 121  122  123  124  125  126  127  128  129  130  131  [132]  133  134  135  ...