태그 : debugging
2009/06/05   Attempted to read or write protected memory.
2009/05/12   Managed Stack Explorer
2009/05/08   Managed Application에서의 GDI 리소스 누수
2009/05/06   Silverlight Application과 SOS.DLL
2009/04/14   Process Explorer 를 이용한 High CPU Issue의 진단 [5]
Attempted to read or write protected memory.

Managed Code에서 "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." 와 같은 오류가 발생하는 것은 흔하지 않다. 이것은 Native Code에서 잘못된 메모리를 참조하는 경우에 발생하는 데, 아시다시피 Managed환경에서는 포인터를 이용한 직접적인 메모리를 다루는 경우가 거의 없기 때문에 경험할 일이 없을 것이다. 하지만, PInvoke 와 같은 Interop 를 사용하는 경우라면 달라질 수 있다. 이는 결국 Native COM Managed 에서 사용하는 것이므로, 여전히 Native COM에서의 문제가 Managed환경으로 전달되었을 때, Access Violation의 위험성이 있는 것이다. 결국, Pageheap enable 하거나 해서 Crash Dump를 다시 잡는 것이 필요할 수도 있다. 일단, Memory Dump를 확인하면 문제가 발생한 Callstack에 아래와 같은 System.AccessViolationException을 볼 수 있다.

 

0:131> !dso

OS Thread Id: 0xacc (131)

RSP/REG          Object           Name

0000000027bda3f0 00000008d0fff488 System.Threading.Thread

0000000027bda438 00000008d0fff488 System.Threading.Thread

0000000027bda588 00000009710807f8 System.String

0000000027bda5a8 00000009710807f8 System.String

0000000027bda5b0 0000000971080908 System.String

0000000027bda668 0000000971080908 System.String

0000000027bda7d0 00000008d100adc8 System.String

0000000027bda7f8 00000008d100ca20 System.String

0000000027bda840 00000008d100b930 System.String

0000000027bda900 00000008d100b930 System.String

0000000027bda9c0 00000008d0fff7c0 System.String

0000000027bda9c8 00000008d0fff7c0 System.String

0000000027bdaaf0 00000008d10095f8 System.String

0000000027bdab08 00000008d100ca20 System.String

0000000027bdab68 00000008d100ca20 System.String

0000000027bdab80 00000008d0fff340 System.AccessViolationException

0000000027bdabe0 00000008d0fff340 System.AccessViolationException

0000000027bdad28 00000008d0fff340 System.AccessViolationException

0000000027bdb0f0 00000008d0ffd308 System.String

0000000027bdb108 00000008d0ffd290 System.String

0000000027bdb110 00000008d0ffd2b0 System.String

0000000027bdb138 00000008d0ffd270 System.String

0000000027bdb140 00000008d0ffd240 System.String

0000000027bdb148 00000008d0ffd208 System.String

0000000027bdb680 00000008d0fff340 System.AccessViolationException

0000000027bdb910 00000008d0fff340 System.AccessViolationException

0000000027bdb938 00000008d0fff340 System.AccessViolationException

. . .

그리고, 먼저, !verifyheap 을 통해서 Managed Heap Check 하는 것도 필요하다.

 

0:131> !verifyheap

-verify will only produce output if there are errors in the heap

------------------------------

Heap 0

object 0000000880ff00d0: does not have valid MT

curr_object : 0000000880ff00d0

Last good object: 0000000880ff00b0

----------------

total 4 objects

 

출력된 결과는 curr_objectcorruption되었다는 것과 메모리 영역에서 가장 가까운 Object중에 정상적인 Object address를 알려준다. 그러므로, curr_object 를 살펴보면 invalid 하다는 결과를 얻는다.

 

0:131> !do 0000000880ff00d0

<Note: this object has an invalid CLASS field>

Invalid object

 

0:131> !do 0000000880ff00b0

Name: System.String

MethodTable: 00000642784365a0

EEClass: 000006427803e4f0

Size: 32(0x20) bytes

GC Generation: 0

 (C:\WINDOWS\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)

String: AES

Fields:

              MT            Field           Offset                 Type VT             Attr            Value Name

0000000000000000  4000096        8         System.Int32  1 instance                4 m_arrayLength

0000000000000000  4000097        c         System.Int32  1 instance                3 m_stringLength

0000000000000000  4000098       10          System.Char  1 instance               41 m_firstChar

0000000000000000  4000099       20        System.String  0   shared           static Empty

                                 >> Domain:Value  00000000d75a2aa0:NotInit Unable to get AppDomain d7900cc0

0000000000000000  400009a       28              SZARRAY  0   shared           static WhitespaceChars

                                 >> Domain:Value  00000000d75a2aa0:NotInit Unable to get AppDomain d7900cc0

 

정상적인 Data를 같이 보여주는 것은 GC에서는 Memory Allocation이 순차적으로 일어난다는 것과 무관하지 않다. low address에서 아래와 같이 Check 하여  Data를 살펴보거나 해당 Object MethodTable 정보를 Check 하는 것은 개발자가 관련있는 Code verify 하는 데 도움이 될 수 있기 때문이다.

 

0:131> dc 0000000880ff00b0

00000008`80ff00b0  784365a0 00000642 00000004 00000003  .eCxB...........  <--- valid Object

00000008`80ff00c0  00450041 00000053 00000000 00000000  A.E.S...........

00000008`80ff00d0  7843e5e0 00000642 0000000d 00000000  ..CxB...........  <--- invalid Object

00000008`80ff00e0  32313338 30323031 .................

 

또한 !gcroot 에 대한 결과로 관련된 Object 등을 Check 할 수 있는 데, 아래의 경우는 조금 실망스러운 결과이다.

 

0:131> !gcroot 0000000880ff00b0

Note: Roots found on stacks may be false positives. Run "!help gcroot" for

more info.

Scan Thread 52 OSThread 1440

Scan Thread 79 OSThread f4c

Scan Thread 145 OSThread 1f64

Scan Thread 149 OSThread 144c

Scan Thread 99 OSThread 18e4

Scan Thread 148 OSThread 1d38

Scan Thread 93 OSThread 1c0c

Scan Thread 91 OSThread 149c

Scan Thread 146 OSThread 1980

Scan Thread 96 OSThread 1dac

Scan Thread 136 OSThread f84

Scan Thread 98 OSThread 6ac

RSP:33c6cbb8:Root:  0000000880ff00b0(System.String)

RSP:33c6cd88:Root:  0000000880ff00b0(System.String)

RSP:33c6ce28:Root:  0000000880ff00b0(System.String)

RSP:33c6cfc0:Root:  0000000880ff00b0(System.String)

Scan Thread 106 OSThread 1a10

Scan Thread 108 OSThread 1ce4

Scan Thread 122 OSThread 190c

Scan Thread 121 OSThread 1698

Scan Thread 110 OSThread 14a8

Scan Thread 107 OSThread 1c10

Scan Thread 116 OSThread 1b80

Scan Thread 117 OSThread 141c

Scan Thread 131 OSThread acc

Scan Thread 135 OSThread 6e8

Scan Thread 126 OSThread 1d1c

Scan Thread 132 OSThread 1e18

 

해당 Object가 존재하는 98 Thread에서 연관된 Callstack Check 할 수 있다. 하지만, Native에서도 마찮가지로 Heap Corruption이 발생한 것은 Dumping 시점 이전에 문제가 발생한 것이 이후에 해당 Managed Heap의 사용에 의해 AV가 발생한 경우가 많기 때문에 문제가 정확히 드러나지 않을 수 있다. 그러므로, 다소 시간을 걸리더라도 Pageheap enable 하고 여러 차례 Dump를 수집할 필요가 있을 지도 모른다. 그리고, 1st chance AV 및 기타 Exception에 대해서도 Callstack Check 라던가 관련성 여부를 살펴보는 것이 필요할 수 있다.

 

by 강세윤 | 2009/06/05 13:39 | Windows debugging | 트랙백 | 덧글(0)
Managed Stack Explorer

High CPU 문제에 대해서 Process explorerHigh CPU시점의 Thread Stack을 손쉽게 볼 수 있다는 데에 많은 도움이 되는 데, 문제는 managed callstack이다. 이것이 참 아쉬웠는데, 이런게 나왔다.

 

Managed Stack Explorer


Useful IIS/ASP.NET Information provided by Microsoft Support Teams : How To View what ASP.NET Requests are doing at runtime on IIS 6.0


수행하면 상위의 그림과 같다. 일단, 좌측의 Process list에 Managed process가 아니면 나오질 않는 다. 그리고, 해당 Process를 Click하여 Thread list를 보면, 중간 창의 Thread list가 나오고, 다시 특정 Thread를 Click하여 Stack trace를 보면, 맨 오른쪽의 Managed thread stack을 보여준다. 아래 CPU의 사용량이나, Time in GC 값이 보이는 것이 유용해 보인다. 근데, .NET Framework 2.0 기반의 Application 대상인 듯 ... 1.1 Application을 대상으로 수행하면, Thread view를 볼 수가 없다.
1.x
는 어떻게 하라고 ....

by 강세윤 | 2009/05/12 15:30 | Windows debugging | 트랙백 | 덧글(0)
Managed Application에서의 GDI 리소스 누수

Managed Application에서 Memory 누수가 발생하면, 대부분이 Managed Object Dispose 되지 않아서 발생한 Issue로 접근하는 데, 물론, 맞는 말이긴 하다. 근데, Managed Memory, GC handling 하는 Virtual Address 영역에서 문제가 반드시 되는 것은 아니다. 이 말은 다시 말해서 Managed Application Leak Detection 하기 위해 Hang Dump를 수집하고, Managed Heap traversal 을 해도 별다른 성과가 나오지 않을 수 있다는 것이다. 다음의 Leaktrack report를 보면 어떤 생각이 드나? 이것은 Managed Application에 대한 Leaktrack report 이다.

 

Type

Description

Recommendation

  Warning

gdiplus.dll is responsible for 105.07 MBytes worth of outstanding allocations. The following are the top 2 memory consuming functions:

gdiplus!GpMemoryBitmap::AllocBitmapData+71: 98.96 MBytes worth of outstanding allocations.
gdiplus!GpMalloc+16: 6.11 MBytes worth of outstanding allocations.

This was detected in ManagedMyApp.exe__PID__3992__Date__03_28_2009__Time_04_12_08AM__754__Leak Dump - Private Bytes.dmp

If this is unexpected, please contact the vendor of this module for further assistance with this issue.

  Warning

gdiplus.dll is responsible for 8.25 MBytes worth of outstanding allocations. The following are the top 2 memory consuming functions:

gdiplus!GpMemoryBitmap::AllocBitmapData+71: 7.66 MBytes worth of outstanding allocations.
gdiplus!GpMalloc+16: 609.25 KBytes worth of outstanding allocations.

This was detected in ManagedMyApp.exe__PID__3992__Date__03_27_2009__Time_06_18_15PM__475__Leak Dump - Virtual Bytes.dmp

If this is unexpected, please contact the vendor of this module for further assistance with this issue

 

간혹, 이런 report 를 보고 Microsoft GDI+에서 Leak이 발생하였으므로 gdiplus관련 Fix가 되야 한다고 생각하는 분들도 계시지만, 그것보단, 대략 아. Application에서 GDI resource 를 많이 사용하고 있겠구나, 또는 UI가 복잡한 Form Application이구나 하는 생각이 맞을 듯 하다. GDI resource Native resource 영역이므로, GC 영역으로 잡히지 않는 다. 또한, GC Managed Heap 영역의 Pressure 를 받아야 구동된다는 기본 개념에서는 쉽사리 Clear 되지 않겠다는 생각도 들게 한다. 그래서 그런지, 다음 류의 문서가 끊임없이 publishing 되고 있다.

 

In the .NET Framework 2.0 SDK, you must explicitly call the IDisposable.Dispose method for all the System.Drawing objects

 

요지는 System.Drawing object, , GDI resource 관련 Object들은 반드시 Dispose Call을 명백히 하라는 것이다. (Most of the System.Drawing objects contain unmanaged Microsoft Graphics Device Interface (GDI) resources. Additionally, most of the System.Drawing objects implement the IDisposable interface.  . . . You must explicitly call the IDisposable.Dispose method for the objects that implement the IDisposable interface.)

보통, Managed Objects는 이런 거 신경 쓰지 않는 다. 하지만, System.Drawing이나 ServicedComponent IDisposable interface를 가지고 있고 Native resource와 연관이 있다는 점에서 Resource clear에 매우 민감한가 보다. 그러므로, 이 들 resource에 대해서는 개발자들이 Dispose Call에 대해서 좀 더 신경을 써야 할 듯 하다.

 

그럼, Dispose Call만 잘하면 되나? 하는 의문이 들게 하는 경우도 존재한다. 다음의 Code PictureBox Image Object를 반복하여 할당하는 경우이다. 간혹, Dispose Call을 간과하는 경우들이 있는 데, 반복할당은 마치 하나의 공간에 겹쳐서 resource를 할당하므로, 그리, 누수가 되지 않을 거라고 생각할 수 있는 데, 그렇지 않은 듯 하다.

 

using (FileStream fs = new FileStream(filePath . . .))

 {

             PictureBox1.Image = Image.FromStream(fs) ;

 }

 

filepath를 파라메터로 전달받아 필요에 따라 PictureBox1 Image를 중복해서 할당하더라도 나중에 Form release 될 때, PictureBox1.Image.Dispose()를 불러주면 된다고 생각해선 안될 듯 하다. 그러므로, 중복 image 할당의 경우는 그때 그때 마다 Dispose Call이 필요할 듯 . 예를 들어,

 

using (FileStream fs = new FileStream(filePath . . .))

 {

             if (PictureBox1.Image != null)

             {

                 PictureBox1.Image.Dispose ();

                 PictureBox1.Image = null;

             }

             PictureBox1.Image = Image.FromStream(fs) ;

 }

 

그리고, Form release 될 때, PictureBox1.Image.Dispose() 역시 마무리로 call해 줘야 할 듯 하다.

의외로 GDI Dispose call이 잘 처리되었음에도 Complexity Form 구조나 빈번한 GDI의 사용에 따라서 GDI Memory 누수가 여전히 보이는 경우도 존재하는 데, 이와 같은 경우는 불가피하게 GC를 강제적으로 호출하여 Performance 측면에 도움을 얻기도 하는 데, 아래와 같이 처리하기도 한다.

 

using (FileStream fs = new FileStream(filePath . . .))

 {

             if (PictureBox1.Image != null)

             {

                 PictureBox1.Image.Dispose ();

                 PictureBox1.Image = null;

             }

             PictureBox1.Image = Image.FromStream(fs) ;

 }

 // . . .

 GC.Collect() ;

 GC.WaitForPendingFinalizer();

 GC.Collec() ;

 

GC Async 하게 호출되는 데 반하여 상위의 Code GC를 즉시 호출하게 하는 효과를 가져온다. 물론, 호출 이후에 Memory Dump를 통해서 Finalizequeue Objects를 확인해 보면 Dispose Objects들이 Clear 된 것을 확인할 수 있다. GC가 호출되는 경우 GC type에 따라서 Threads Pending 등이 발생하므로 심히 고려 되야 하지만, 필요하다면 약이 될 수도 있다.

by 강세윤 | 2009/05/08 13:58 | Windows debugging | 트랙백 | 덧글(0)
Silverlight Application과 SOS.DLL

Silverlight 2.0 기반의 Application 도 역시 .NET Framework 기반이므로, 여타의 Managed Application debugging과 동일하게 SOS.dll을 이용하여 Debugging을 할 수 있다. 하지만, 이용하는 SOS.dll의 위치가 다르다. 만일, Silverlight Application의 개발환경이라면, C:\Program Files\Microsoft Silverlight\2.0.40115.0 folder SOS.dll이 존재할 것이다. 이를 이용하여 Windbg에서 Debugging을 할 수 있다. (만일, Silverlight 개발환경이 아닐 경우 즉, runtme install이 된 Machine에서는 해당 Silverlight runtime에 존재하는 Fodler에서 SOS.dll을 확인할 수 없다. 이것이 미묘하게 중요할 수 있다. )

 

간단한 Silverlight Application을 개발한 후에 Internet explorer에서 hosting 하고, WinDbg Attach 해보자. 그리고 늘 그렇듯이

 

<UserControl x:Class="SilverlightApplication1.Page"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Width="400" Height="300">

    <Grid x:Name="LayoutRoot" Background="White">

        <Button x:Name="BigButton" Content="눌러 여기" FontSize="36" Click="BigButton_Click" />

    </Grid>

</UserControl>

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

 

namespace SilverlightApplication1

{

    public partial class Page : UserControl

    {

        public Page()

        {

            InitializeComponent();

        }

 

        private void BigButton_Click(object sender, RoutedEventArgs e)

        {

            BigButton.Background = new SolidColorBrush(Colors.Red);

            BigButton.Content = "눌렀군 .. ";

        }

    }

}

 

 

0:016> .sympath srv*c:\pubsymbols*http://msdl.microsoft.com/download/symbols

Symbol search path is: srv*c:\pubsymbols*http://msdl.microsoft.com/download/symbols

Expanded Symbol search path is: srv*c:\pubsymbols*http://msdl.microsoft.com/download/symbols

0:016> .reload

Reloading current modules

................................................................

................................................

0:016> .loadby sos mscorwks

Unable to find module 'mscorwks'

 

. 이상하지 않다. Silverlight Application mscorwks 를 사용하지 않는 다. Coreclr.dll을 사용한다. 그러므로, 다음과 같이 해야 한다.

 

0:016> .loadby sos coreclr

0:016>

 

그리고 나서는 Debugging이 비슷하다.

 

0:007> !bpmd SilverlightApplication1 SilverlightApplication1.Page.BigButton_Click

Found 1 methods...

MethodDesc = 03943a80

Adding pending breakpoints...

0:007> g

CLR notification: method 'SilverlightApplication1.Page.BigButton_Click(System.Object, System.Windows.RoutedEventArgs)' code generated

(fd4.8f4): CLR notification exception - code e0444143 (first chance)

JITTED SilverlightApplication1!SilverlightApplication1.Page.BigButton_Click(System.Object, System.Windows.RoutedEventArgs)

Setting breakpoint: bp 03AE0398 [SilverlightApplication1.Page.BigButton_Click(System.Object, System.Windows.RoutedEventArgs)]

Breakpoint 0 hit

eax=03943a80 ebx=00000000 ecx=069dcc08 edx=069ddbfc esi=05c1e178 edi=04bbf7ac

eip=03ae0398 esp=04bbf6f0 ebp=04bbf708 iopl=0         nv up ei pl nz na po nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202

*** WARNING: Unable to verify checksum for SilverlightApplication1.dll

SilverlightApplication1_3ad0000!BigButton_Click:

03ae0398 55              push    ebp

0:005> !clrstack

OS Thread Id: 0x8f4 (5)

ESP       EIP    

04bbf6f0 03ae0398 SilverlightApplication1.Page.BigButton_Click(System.Object, System.Windows.RoutedEventArgs)

04bbf6f8 05f8dfc5 System.Windows.Controls.Primitives.ButtonBase.OnClick()

04bbf710 05f8dec8 System.Windows.Controls.Button.OnClick()

04bbf720 05f8ddf5 System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(System.Windows.Input.MouseButtonEventArgs)

04bbf730 05f8dd59 System.Windows.Controls.Control.OnMouseLeftButtonUp(System.Windows.Controls.Control, System.EventArgs)

04bbf740 05f0a0e7 MS.Internal.JoltHelper.FireEvent(IntPtr, IntPtr, Int32, System.String)

04bbf914 544d17b0 [GCFrame: 04bbf914]

04bbf9d0 544d17b0 [ContextTransitionFrame: 04bbf9d0]

04bbfac8 544d17b0 [UMThkCallFrame: 04bbfac8]

 

by 강세윤 | 2009/05/06 14:39 | Windows debugging | 트랙백 | 덧글(0)
Process Explorer 를 이용한 High CPU Issue의 진단

Process Explorer는 여러 모로 Application Troubleshooting에 유용한 툴이다. 개인적으로 경험상, High CPU DLL Injection에 대한 감시 등에 해당 Tool을 유용하게 사용하곤 했다.

일단, http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx 이곳에서 Process Explorer Download 받을 수 있다. 처음 실행하면, 상위와 같은 UI 화면을 확인 할 수 있는 데, View에서 Show Low Pane이 선택되어 있다면, 아래의 Pane을 확인할 수 있으며, 상위의 그림과 같이 선택된 Process Module list를 확인할 수 있다. 이는 알 수 없는 DLL injection을 감시할 수 있도록 해 준다. 일반적으로 View Menu Low Pane View에서는 DLLs을 보여 줄지 Handles를 보여줄 지를 선택할 수 있는 데, Handles의 경우는 Handle Leak에 대한 감시를 할 수 있기도 하다.

 

상위의 화면을 보면, PID CPU column 에서 CPU 사용량이 실시간으로 바뀌는 것을 볼 수 있는 데, 특정 Process CPU 점유가 눈에 띄게 보이는 경우, 해당 Process를 선택하고 오른 쪽 마우스 클릭에서 Properties 를 확인할 수 있다.

속성을 보면, Threads TAB이 존재하고 CPU를 점유하고 있는 Thread Stack을 확인할 수 있다. 하지만, 그전에 Debugging Tools for Windows 라는 MS에서 제공하는 Tool Install 해야만 한다. 그리고, Process Explorer main menu Options에서 Configure Symbols에 적절한 Symbol을 넣어 주지 않으면, 상위 속성 창에서의 Stack button을 눌렀을 때 적절한 Thread Stack을 확인할 수는 없다. 일반적으로 Microsoft에서 제공하는 Public Symbol path를 입력하고, 그리고 추가로 해당 Process가 사용하는 Exe,DLL에 대한 Symbol 필요하다.  보통은 SRV*c:\symbols*http://msdl.microsoft.com/download/symbols; c:\MySymbols; 이렇게 입력한다. 예상하다시피 c:\MySymbols Folder는 모니터링 Process가 사용하는 EXE, DLLs에 대한 Symbol이 저장된 위치이다. 그와 같은 경우에 아래와 같은 Thread Stack에 대한 정보를 얻을 수 있다.

 

by 강세윤 | 2009/04/14 09:25 | Windows debugging | 트랙백 | 덧글(5)
< 이전페이지 다음페이지 >