Welcome to MSDN Blogs Sign in | Join | Help

關於KB 960715 安全性更新對應用程式所造成的影響及因應措施

微軟在今年(2009) 2月11 日發佈了 960745 安全性更新 ,當應用程式有使用到以下元件時,安裝KB960715將導致應用程式無法正常運作,受影響的程式類型包含 VB6或.NET應用程式,具有自訂表單的Office VBA, 使用ActiveX 元件的網頁應用程式。 由於這一兩個星期陸續接到客戶遇到這個問題,加上相關的資訊較為凌散,因此將它整埋出來供大家參考。

以下為受影響的元件清單:

元件

檔案名稱

CLASSID

Microsoft Animation Control, version 5.0(SP2)

ComCt232.ocx

{1E216240-1B7D-11CF-9D53-00AA003C9CB6}

Microsoft Chart Control 6.0 (SP6)

MSChrt20.ocx

{3A2B370C-BA0A-11d1-B137-0000F8753F5D}

Microsoft Animation Control 6.0 (SP6)

mscomct2.ocx

{B09DE715-87C1-11d1-8BE3-0000F8754DA1}

Microsoft DataGrid Control 6.0 (SP6)

MSDatGrd.ocx

{cde57a43-8b86-11d0-b3c6-00a0c90aea82}

Microsoft FlexGrid Control, version 6.0 (SP6)

MSFlxGrd.ocx

{6262d3a0-531b-11cf-91f6-c2863c385e30}

Microsoft Hierarchical FlexGrid Control 6.0 (SP6)

MShflxgd.ocx

{0ECD9B64-23AA-11d0-B351-00A0C9055D8E}

Microsoft Masked Edit Control, version 6.0 (SP6)

msmask32.ocx

{C932BA85-4374-101B-A56C-00AA003668DC}

Microsoft WinSock Control, version 6.0 (SP6)

MSWINSCK.ocx

{248dd896-bb45-11cf-9abc-0080c7e7b78d}

安全性更新是如何防堵上述元件的安全性漏洞?

此安全性更新將以下機碼中的Compatibility flags 值設為0x400

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\{CLSID}

如此一來當有使用到上述元件的應用程式,訪問到以下機碼, 判斷flag被設為0x400時,便停止運作。細節部份可參考:

240797 How to stop an ActiveX control from running in Internet Explorer
http://support.microsoft.com/default.aspx?scid=kb;EN-US;240797

但由於上述元件的版本已經非常舊,微軟在去年底發佈了最新的VB6 runtime extended files(累積更新),在此版所更新的元件已經修補了上述的安全性漏洞。因此若您的應用程式是以此版本的元件進行開發及佈署,則應用程式將不受KB960715所影響。

細節可參考以下文章:

Description of the cumulative update rollup for the Visual Basic 6.0 Service Pack 6 Runtime Extended Files

http://support.microsoft.com/kb/957924/en-us

此套件下載位置:http://www.microsoft.com/downloads/details.aspx?displaylang=zh-tw&FamilyID=cb824e35-0403-45c4-9e41-459f0eb89e36

它是如何做到的呢? 在安裝完此延伸檔後,系統會在先前所提到的機碼中(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\{CLSID})加入一個key 值, 稱為AlternateCLSID, 並將它的值指向新版元件的CLSID,如此應用程式便會去相對應的位置載入新版的元件來執行。

應用程式如何因應以解決由於KB960715所造成的問題? 這個問題必須針對2個方向來說明-開發環境及用戶端:

若您的應用程式為VB6應用程式或.NET應用程式但引用了ActiveX 元件:

開發環境:

1. 安裝VB6 SP6

2. 安裝 VB6 Runtime累積的延伸檔案

3. 將引用到元件的檔案重新設計(重新拖拉元件)

4. 重新編譯並部署到用戶端(VB6在封裝執行程式時必須選擇將新元件一起封裝至封裝檔案)

用戶端:

無需任何動作

若您的應用程式為使用自訂表單的Office VBA

開發環境:

1. 安裝VB6 SP6

2. 安裝 VB6 Runtime累積的延伸檔案

3. 將引用到元件的檔案重新設計(重新拖拉元件)

4. 透過檔案搜尋的方式, 將電腦中所有副檔名為.exd的檔案進行刪除(您必須在搜尋進階選項中勾選”搜尋隱藏檔案及資料夾)

用戶端:

1. 透過檔案搜尋的方式, 將電腦中所有副檔名為.exd的檔案進行刪除(您必須在搜尋進階選項中勾選”搜尋隱藏檔案及資料夾)

2. 將新版的元件由開發環境複製到用戶端的系統目錄(c:\windows\system32)下並重新註冊(使用Regsvr32.exe)

若您的網站應用程式有包含ActiveX元件並引用到上述有問題的元件:

開發環境:

1. 安裝VB6 SP6

2. 安裝 VB6 Runtime累積的延伸檔案

3. 將引用到元件的ActiveX元件重新設計(重新拖拉元件)

4. 重新編譯ActiveX元件並進行封裝部署(VB6在封裝執行程式時必須選擇將新元件一起封裝至封裝檔案)

5. 由於新版元件之執行授權尚未能被識別,因此您必須在網頁中ActiveX元件宣告的Tag加上授權檔(.lpk)的路徑,細節請參考以下文章:

如何在 Internet Explorer 中使用合法授權的 ActiveX

http://support.microsoft.com/?id=159923

LNK檔下載位置: 960715.lpk

6. 將ActiveX元件重新部署在網頁上

Posted by Terry Lin | 1 Comments
Filed under:

TechEd2008- .NET 應用程式除錯秘技系列番外篇 - Memory leak

Memory leak, 中文翻譯成"記憶體泄漏"(怪怪的), 用來說明程式因為疏忽或錯誤造成記憶體未能如期的進行釋放。從另一個角度看,就是記憶體的使用不斷的增長(因為沒釋放不再使用的記憶體或釋放的速度不如使用的速度)。有關於系統及應用程式的記憶體相關名詞,可能需要一篇專文來說明。 曾經在網路上聽到一些似是而非的言論~~"聽說呼叫GC回收可以立即釋放沒用到的記憶體~", "可是聽說呼叫GC回收很耗系統資源耶~~".....

我的建議是~在還沒有搞懂.NET 如何管理記憶體以及GC的運作原理前,請先忽略以上的"聽說"...

在我們team所處理過memory leak 的經驗當中(以.NET而言),有一半以上的問題都是記憶體的使用由一個或數個很大的物件所佔據,而這個物件短時間內又釋放不掉(或是需要長期使用),例如session, global 變數....此次的範例就是模擬程式執行時,記憶體不斷的增長,並且透過windbg來分析到底是誰佔用了記憶體。

範例及相關文件分成3個壓縮檔(08_Memoryleak.part01.rar , 08_Memoryleak.part02.rar , 08_Memoryleak.part03.rar共93MB)。之所以這麼大是因為為了方便大家學習,我將dump檔及分析的log都放在裏頭。因此解壓縮之後,您會發現有一個memoryleak.dmp 佔了七百多MB。

開啟專案後,請使用Ctrl + F5 執行程式,並在工作管理員觀察WebDev.WebServer.exe這個process。在工作管理員中,請按檢視=>選擇欄位並勾選"記憶體使用量"及"虛擬記憶體大小"2個選項。 執行後按下網頁上的"Eat Memory"按鈕,您將發現記憶體的使用會不斷成長。

當記憶體長到300MB以上時,您可以用WinDbg 附加到這個process以進行Memory leak的troubleshooting. 以下使用範例中的dump檔進行分析:

1. 輸入".loadby sos mscorwks" 指令來載入.NET 的extension

2. 由於我們使用WinDbg附加到process,因此一開始所在的thread是被我們斷下來的session。此時我們可以透過 "~*e !clrstack" 指令來顯示所有thread的call stacks.

!clrstack 是用來顯示目前所在thread的call stacks. 在前面加上"~*e" 可以針對所有thread來執行後面的指令。其顯示的結果很長,看一下結果可以看到,第11條thread應該是目前在運作的thread (其他都在waiting)

OS Thread Id: 0x960 (11)
ESP       EIP     
0437d6d4 79ef1750 [HelperMethodFrame_1OBJ: 0437d6d4] System.Number.FormatDecimal(System.Decimal, System.String, System.Globalization.NumberFormatInfo)
0437d7c8 793ef69f System.Decimal.ToString()
0437d7cc 66187ba7 System.Web.UI.WebControls.BoundField.FormatDataValue(System.Object, Boolean)
0437d7e8 66188181 System.Web.UI.WebControls.BoundField.OnDataBindField(System.Object, System.EventArgs)
0437d804 66188ac7 System.Web.UI.WebControls.AutoGeneratedField.OnDataBindField(System.Object, System.EventArgs)
0437d83c 6613bb54 System.Web.UI.Control.OnDataBinding(System.EventArgs)
0437d84c 6613bc4f System.Web.UI.Control.DataBind(Boolean)
0437d888 6613bb6d System.Web.UI.Control.DataBind()
0437d88c 6613bd39 System.Web.UI.Control.DataBindChildren()
0437d8bc 6613bc59 System.Web.UI.Control.DataBind(Boolean)
0437d8f8 6613bb6d System.Web.UI.Control.DataBind()
0437d8fc 661e38e6 System.Web.UI.WebControls.GridView.CreateRow(Int32, Int32, System.Web.UI.WebControls.DataControlRowType, System.Web.UI.WebControls.DataControlRowState, Boolean, System.Object, System.Web.UI.WebControls.DataControlField[], System.Web.UI.WebControls.TableRowCollection, System.Web.UI.WebControls.PagedDataSource)
0437d930 661e196e System.Web.UI.WebControls.GridView.CreateChildControls(System.Collections.IEnumerable, Boolean)
0437da10 661a728c System.Web.UI.WebControls.CompositeDataBoundControl.PerformDataBinding(System.Collections.IEnumerable)
0437da20 661e7138 System.Web.UI.WebControls.GridView.PerformDataBinding(System.Collections.IEnumerable)
0437da30 66183880 System.Web.UI.WebControls.DataBoundControl.OnDataSourceViewSelectCallback(System.Collections.IEnumerable)
0437da3c 66144dca System.Web.UI.DataSourceView.Select(System.Web.UI.DataSourceSelectArguments, System.Web.UI.DataSourceViewSelectCallback)
0437da48 66183a86 System.Web.UI.WebControls.DataBoundControl.PerformSelect()
0437da5c 661831f7 System.Web.UI.WebControls.BaseDataBoundControl.DataBind()
0437da64 661e3a35 System.Web.UI.WebControls.GridView.DataBind()
0437da68 05970661 Memoryleak.Memoryleak.Button1_Click(System.Object, System.EventArgs)
0437da94 6619004e System.Web.UI.WebControls.Button.OnClick(System.EventArgs)
0437daa8 6619023c System.Web.UI.WebControls.Button.RaisePostBackEvent(System.String)
0437dabc 661901b8 System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(System.String)0437dd60 05970185 ASP.memoryleak_aspx.ProcessRequest(System.Web.HttpContext)

從上述的call stacks 來看,的確符合我們操作的流程,而且我們觀察到有DataGridView的操作,但這並不足以看出Memory Leak的原因。

3. 輸入 "!eeheap -gc" 指令,查看GC的數量以及大小,得到的結果如下:

Number of GC Heaps: 1
generation 0 starts at 0x16342658
generation 1 starts at 0x16023958
generation 2 starts at 0x012d1000
ephemeral segment allocation context: (0x16614fd0, 0x16616ff4)
segment    begin allocated     size
0022f558 04c67294  04c6d410 0x0000617c(24956)
.............
Large object heap starts at 0x022d1000
segment    begin allocated     size
022d0000 022d1000  032c5350 0x00ff4350(16728912)
.............
1c870000 1c871000  1ccdd420 0x0046c420(4637728)
Total Size  0x145a1edc(341450460)
------------------------------
GC Heap Size  0x145a1edc(341450460)

我們發現GC的大小就已經3百多MB,因此記憶體的確是.NET的應用程式所佔用。

4. 接下來我們使用"~11s" 指令將thread切換到第11條,並輸入"!dumpheap -stat" 指令,該指令會將heap中的所有物件的個數及大小依序列舉出來,顯示結果如下(僅列舉最後幾列):

total 4469762 objects
Statistics:
      MT    Count    TotalSize Class Name
654359c8      366     34579680 System.Data.RBTree`1+Node[[System.Int32, mscorlib]][]
65412bb4      366     34579680 System.Data.RBTree`1+Node[[System.Data.DataRow, System.Data]][]
65408b8c  1077500     68960000 System.Data.DataRow

我們可以發現有一百多萬個DataRow物件。這個非常可疑,而且光這些DataRow就6x MB。當然這些DataRow不會單獨存在,勢必是包含在一個或數個DataTable或DataSet當中。 上述資料的第一個欄位MT 表示 "Method Table" .NET會將相同型別的物件使用Method Table集中管理。

5.  輸入 "!dumpheap -mt 65408b8c" 指令 將method table 中的物件列舉出來:

Address       MT     Size
012d8414 65408b8c       64    
.......
01471ab4 65408b8c       64    
01471af4 65408b8c       64    
01471b34 65408b8c       64    
total 4017 objects

6. 輸入 "!do 01471b34" 指令, 將物件的資訊列出:

Name: System.Data.DataRow
MethodTable: 65408b8c
EEClass: 65408b1c
Size: 64(0x40) bytes
(C:\WINNT\assembly\GAC_32\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
65407d48  4000714        4 ...em.Data.DataTable  0 instance 014141f8 _table
654094f4  4000715        8 ...aColumnCollection  0 instance 0141447c _columns
79102290  4000716       18         System.Int32  1 instance     3967 oldRecord
79102290  4000717       1c         System.Int32  1 instance     3967 newRecord
79102290  4000718       20         System.Int32  1 instance       -1 tempRecord
79102290  4000719       24         System.Int32  1 instance     3968 _rowID
6541d178  400071a       28         System.Int32  1 instance        0 _action
7910be50  400071b       38       System.Boolean  1 instance        0 inChangingEvent
7910be50  400071c       39       System.Boolean  1 instance        0 inDeletingEvent
7910be50  400071d       3a       System.Boolean  1 instance        0 inCascade
654088b4  400071e        c ...m.Data.DataColumn  0 instance 00000000 _lastChangedColumn
79102290  400071f       2c         System.Int32  1 instance        0 _countColumnChange
6541c3f4  4000720       10 ...em.Data.DataError  0 instance 00000000 error
790fd0f0  4000721       14        System.Object  0 instance 00000000 _element
79102290  4000722       30         System.Int32  1 instance  1245184 _rbTreeNodeId
79102290  4000724       34         System.Int32  1 instance     3968 ObjectID
79102290  4000723      484         System.Int32  1   shared   static _objectTypeCount
    >> Domain:Value  001654f0:NotInit  00207008:NotInit  <<

7. DataRow物件有一個_table的欄位,輸入 "!do 014141f8" 指令  將物件資訊列出:

Name: System.Data.DataTable
MethodTable: 65407d48
EEClass: 654078f8
Size: 296(0x128) bytes
(C:\WINNT\assembly\GAC_32\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
7a7567d8  40009a3        4 ...ponentModel.ISite  0 instance 00000000 site
7a755968  40009a4        8 ....EventHandlerList  0 instance 00000000 events
790fd0f0  40009a2      1e4        System.Object  0   shared   static EventDisposed
    >> Domain:Value  001654f0:NotInit  00207008:NotInit  <<
65406ecc  4000799        c  System.Data.DataSet  0 instance 0140ac60 dataSet
65409004  400079a       10 System.Data.DataView  0 instance 00000000 defaultView
79102290  400079b       d0         System.Int32  1 instance  1077501 nextRowID
....
由於資訊太多,因此僅列出分析需要的部份。我們可以看到DataTable的NextRowID 屬性值是1077501, 表示這個DataTable下一筆新的DataRowID是這個數字,同時可以理解為目前在這個DataTable已經有這麼多筆資料。

8. 接下來我們有幾個思考方向,
    a) 我們從!dumpheap -stat 指令所找到的DataRow數量與DataTable裏DataRow的數量一致,因此這些DataRow都屬於同一個DataTable。
    b) 是否有其他使用記憶體較多的物件類別?
    c) 這些使用佔用記憶體的物件是否是必要的(一百多萬筆資料?)
9. 針對這個dump來看,其實我們已經很了解造成memory leak的原因是由於在程式中keep 一個很大的DataTable, 但並非所有的memory leak 都是如此,也有在session中存放幾MB的資訊, 但同時太多user 在線上,造成記憶體無法即時釋放。 以下再介紹2個相關的指令:

"!gcroot 01471b34" : 這個01471b34 的位址之先前我們所使用的DataRow記憶體位址(在這個例子中,使用任何一個DataRow的位址都可以),我們得到以下結果:

Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 0 OSTHread 444
Scan Thread 2 OSTHread 29c
Scan Thread 6 OSTHread 1e4
Scan Thread 11 OSTHread 960
ESP:437d690:Root:0ae4facc(System.Web.UI.WebControls.AutoGeneratedField)->
0ae4f3d8(System.Data.DataColumnPropertyDescriptor)->
01414db8(System.Data.DataColumn)->
014141f8(System.Data.DataTable)->
01414438(System.Data.RecordManager)->
14871000(System.Object[])->
01471b34(System.Data.DataRow)
ESP:437d694:Root:16614be8(System.Web.UI.WebControls.DataControlFieldCell)->
0ae4facc(System.Web.UI.WebControls.AutoGeneratedField)->
013fce54(System.Web.UI.WebControls.GridView)->
0ae4efd8(System.Web.UI.WebControls.ReadOnlyDataSourceView)->
0dabcd4c(System.Data.DataView)->
0ae4efa4(System.Collections.Generic.Dictionary`2[[System.Data.DataRow, System.Data],[System.Data.DataRowView, System.Data]])->
1b071000(System.Collections.Generic.Dictionary`2+Entry[[System.Data.DataRow, System.Data],[System.Data.DataRowView, System.Data]][])
Scan Thread 12 OSTHread 888
Scan Thread 13 OSTHread a5c
Scan Thread 14 OSTHread 608
Scan Thread 16 OSTHread a74

這個指令會去callstack, heap裏找與物件相關聯的上層物件(root) 或handle.

!objsize 014141f8 : 這個指令固名思義是會去計算物件的大小,014141f8 是之前用過的DataTable物件。得到的結果如下:

sizeof(014141f8) =    211043384 (   0xc944438) bytes (System.Data.DataTable)

請注意: 這個指令需要非常久的時間(筆者光算上面這個物件就花了將近20分鐘) 。

 

在撰寫這篇文章時,我發現先前存下來的dump有問題,因此重現產生了一個dump上傳到下載路徑裏。若您實際在debugging時,可能記憶體位址會與文章裏面的不同,大家可以參考我放在壓縮檔裏面的log。

Posted by Terry Lin | 1 Comments
Filed under: ,

TechEd2008- .NET 應用程式除錯秘技系列(5) - 應用程式沒有回應(Busy Hang)

好久沒有更新了~因為之前休了2個星期的長假。回來又一堆事情等著處理,因此這TechEd 系列的最後一篇一直等到今天才有時間寫。由於在TechEd 2008上面的demo有點複雜,因此我寫了另一支簡化的程式來做為這個busy hang (CPU High)的範例,範例從這裏下載。

造成Busy的原因其實不少,包含無窮迴圈啦、過於頻繁的GC回收啦、memory leak...... 都可能會造成busy hang. 今天主要來談無窮迴圈(Infinite loop)所造成的busy hang.

可能很多人會說: 無窮迴圈還需要用到偵錯工具來分析嗎? 別笑死人了~ 看一下程式碼也知道哪裏有問題!!

但就我們support客戶的經驗來看,事實恐怕並非如此。以下狀況有可能會造成無窮迴圈(或是要算很久才會有結果):

1. While true ~ End While 迴圈 : 建議最好少用!

2. 同上, 但有判斷式,只是這個判斷式可能判斷得不是很周延,導致判斷式的結果恆成立或不成立,造成無窮迴圈

3. 使用遞迴(Recursion),但無法預估其深度,造成需要長時間計算或無窮迴圈。

當然還有更複雜的狀況所造成的無窮迴圈,而這些都是使用者始料未及的。 以下針對我們的範例先做一下說明:

 

  private void button1_Click(object sender, EventArgs e)
        {
            Infiniteloop();
        }
 
        void Infiniteloop()
        {
            while (true)
            {
                A();
            }
        }
 
        void A() { B(); }
 
        void B() {C();}
 
        void C() {D();}
 
        void D() { }

從以上的code 我們可以看到, 按下button1後,程式會呼叫 Infiniteloop, 之後會呼叫function A => funtion B => function C => function D ,不斷的重覆這樣的呼叫順序。面對這種狀況,我們怎麼找到哪一個funtion 才是造成無窮迴圈的元兇呢? 以下這兩張圖說明了程式執行的順序及troubleshooting的方式:

在Visual Studio中我們可以使用Step - In的方式單步執行我們的程式

036

針對此問題,由於我們會在hang的時候使用Visual Studio中斷我們的程式,程式有可能斷在Infiniteloop,function A,B,C,D其中一個function中,因此我們必須Step - out, 觀察程式退到哪一個function時會繼續執行程式,最後的那一個function便是造成無窮迴圈的funtion啦!!

037

 

以下為實際執行程式及偵錯的過程:

1. 執行程式,讓它High 起來,並且按下Beak All 按鈕讓程式斷下來!

038

2. 我們看到此程式斷在function B

039

3. 按一下Step-out後,程式退到function A

040

4. 再step -out一次, 程式退到Infiniteloop, 此時若再按一下step - out, 程式會離開中斷模式並繼續執行,因此我們可以斷定造成無窮迴圈的是InfiniteLoop函數

042

5. 再按一次Break All, 發現這次斷在 function D

041

重覆確認後,就可以找到元兇囉!!

總算把欠大家的文字說明寫完了~ 如果是沒來上過TechEd 的朋友可能還是看不太懂,因此接下來除了持續補充原先要在TechEd demo卻沒有demo的範例以外,也會從更基本的部份來跟各位分享!!

Posted by Terry Lin | 1 Comments
Filed under: ,

TechEd2008 .NET 應用程式除錯秘技系列(4) 應用程式沒有回應(Idle Hang) 下

終於進入到正題囉~馬上來看一下這個Idle Hang的問題如何重現?

重現步驟:

1. 執行應用程式並開啟WinDbg,在WinDbg中直接附加到HangDemo.exe這個process。忘記的請參考前一篇文章。

2. 在轉帳旁邊的TextBox輸入一個金額(Ex, 2000),按一下"轉帳"鈕,然後很快的切到"支票轉存款帳戶",再按一次"轉帳"

034

3. 此時您再去按顯示餘額(無論支票或存款)都會變成灰色且無回應。

035

4. 這時候我們已經確定應用程式Hang住了~可以回到WinDbg裏,在Debug=>Break或按一下snap026 圖示將程式中斷下來。

5. 載入SOS.dll (輸入 ".loadby sos mscorwks")

6. 輸入"!syncblk",如果不知道指令的意思及用法,可以用!help <command>取得說明。例如 !help syncblk 會得到如下結果:

0:015> !help syncblk
-------------------------------------------------------------------------------
!SyncBlk [-all | <syncblk number>]

A SyncBlock is a holder for extra information that doesn't need to be created
for every object. It can hold COM Interop data, HashCodes, and locking
information for thread-safe operations.

When called without arguments, !SyncBlk will print the list of SyncBlocks
corresponding to objects that are owned by a thread. For example, a

    lock(MyObject)
    {
        .... (略)

輸入!syncblk後會得到以下資訊:

0:015> !syncblk
Index SyncBlock MonitorHeld     Recursion      Owning  Thread Info     SyncBlock Owner
     34   003fdad4             7         1 0044a0b8          19b0           9              029c3774 HangDemo.Account
     36   003fdb34             5         1 0044da00            9fc           8             029729c0 HangDemo.Account
-----------------------------
Total           37
CCW             2
RCW             0
ComClassFactory 0
Free            0

以上有幾個資訊要注意,一是Owning Thread,另一個是Own住的物件位址及型別。 Syncblk指令主要會列出目前lock的資訊。

7. 使用 "!threads"可以列出所有threads。使用"~<n>s" 指令可以切換到不同thread,以此例來看,我們應該看第8及第9條thread在做什麼,以致於應用程式沒有回應。

8. 輸入 "~8s" 切換到第8條thread。然後輸入 "!clrstack",這個指令可以顯示當前thread的call stack,若加上 -a的參數,則可以顯示相關的參數及區域變數。執行後得到的結果如下:

0:009> ~8s
eax=00000000 ebx=00000001 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=77d60bc5 esp=05faef64 ebp=05faeffc iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
ntdll!NtWaitForMultipleObjects+0x15:
77d60bc5 c21400          ret     14h
0:008> !clrstack
OS Thread Id: 0x9fc (8)
ESP       EIP    
05faf1f0 77d60bc5 [GCFrame: 05faf1f0]
05faf32c 77d60bc5 [HelperMethodFrame: 05faf32c] System.Threading.Monitor.Enter(System.Object)
05faf380 008622ad HangDemo.Form1.SavToChk()
05faf3a4 793b0d1f System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
05faf3ac 79373ecd System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
05faf3c4 793b0c68 System.Threading.ThreadHelper.ThreadStart()
05faf5ec 79e7c74b [GCFrame: 05faf5ec]

我們看到程式在執行SavToChk函式後,呼叫了Monitor.Enter方法。

9. 切換到第9條thread,同樣來看看它的call stack

0:008> ~9s
eax=00000004 ebx=00000001 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=77d60bc5 esp=0619f144 ebp=0619f1dc iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
ntdll!NtWaitForMultipleObjects+0x15:
77d60bc5 c21400          ret     14h
0:009> !clrstack
OS Thread Id: 0x19b0 (9)
ESP       EIP    
0619f3d0 77d60bc5 [GCFrame: 0619f3d0]
0619f50c 77d60bc5 [HelperMethodFrame: 0619f50c] System.Threading.Monitor.Enter(System.Object)
0619f560 0086254d HangDemo.Form1.ChkToSav()
0619f584 793b0d1f System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
0619f58c 79373ecd System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
0619f5a4 793b0c68 System.Threading.ThreadHelper.ThreadStart()
0619f7cc 79e7c74b [GCFrame: 0619f7cc]

這call stack看起來好眼熟喔~但仔細一看,這裏執行的是ChkToSav (第8條執行的是SavToChk),對照我們剛剛在程式裏操作的步驟:支票轉存款,然後存款轉支票~Bingo!!好像離答案愈來愈近了。

既然有source code,當然是拿來看看這兩個方法在做什麼最快了!請看以下程式碼:

   
void ChkToSav()
        {
            double amount = Convert.ToDouble(transf_textBox.Text);
            Monitor.Enter(acnt_checking);
            acnt_checking.withdraw_checking(123, amount);
            Thread.Sleep(5000);
            Monitor.Enter(acnt_saving);
            acnt_saving.deposit_saving(123, amount);
            Monitor.Exit(acnt_saving);
            Monitor.Exit(acnt_checking);
        }
 
        void SavToChk()
        {
            double amount = Convert.ToDouble(transf_textBox.Text);
            Monitor.Enter(acnt_saving);
            acnt_saving.withdraw_saving(123, amount);
            Thread.Sleep(5000);
            Monitor.Enter(acnt_checking);
            acnt_checking.deposit_checking(123, amount);
            Monitor.Exit(acnt_checking);
            Monitor.Exit(acnt_saving);
        }

看到了嗎? ChkToSav是支票轉存款,它會先lock acnt_checking,然後sleep 5秒後再lock acnt_saving. SavToChk則剛好相反。所以我們就是利用這5秒鐘造成一個deaklcok。

可是~~剛剛我們在dump裏看到的不是物件位址029729c0029c3774  嗎? 怎麼確定它就是acnt_checking和acnt_saving呢? 給各位一個提示,用!do指令去看看Form物件裏有哪些欄位吧!

另外要思考的是,我們找出deadlock的原因了,但怎麼解決呢? 也許我們應該要思考的是,轉帳這個動作用lock適合嗎?有沒有其他的做法呢? 留給各位當練習囉~

Posted by Terry Lin | 0 Comments
Filed under: , ,

TechEd2008- .NET 應用程式除錯秘技系列(4) - 應用程式沒有回應(Idle Hang) 上

這個問題的範例請從這裏下載,DB在此

在這個範例當中,我們分成2個部份介紹,第一個部份是 - 如何在dump中找出SQL connection string。第二個部份才是介紹如何處理Idle hang的狀況。

在開始debug之前,由於我們要debug的是.NET 的應用程式,因此在WinDbg裏要先啟用CLR Exception的event。方法如下:

WinDbg=>Debug=>Event Filters ,在列表中找到CLR Exception並點選右邊的"Enable"。設定好之後就可以開始來Debugging囉~

031

 

問題重現步驟:

1. 執行範例(請直接執行應用程式,不要在VS2005/2008的debug模式執行)並開啟WinDbg。

2.  WinDbg => File => Attach to a process => HangDemo.exe => OK

3. 當WinDbg 附加到process後,程式會被WinDbg中斷下來,此時可以在WinDbg中輸入"g"(亦即go)讓程式繼續執行。

4. 回到程式中,點選"存款帳戶餘額"=>按"顯示餘額" 檢查餘額。

5. 在"從存款帳戶提領"旁邊的textbox輸入大於餘額的數字並按下提領按鈕,如下圖所示,此時您會發現,程式再次被WinDbg斷下來。

snap025

6. 我們看到WinDbg攔到了一個first chance exception

032

此時我們需要載入針對.NET 應用程式所開發的WinDbg擴充程式 -- SOS.dll。

7. 在WinDbg輸入指令 ".loadby sos mscorwks "。輸入指令後,您可以利用!help來查看相關的指令及用法。

033

既然發生了Exception,我們怎麼來看是什麼Exception呢? 在WinDbg裏輸入" !printexception " 或 "!pe" 得到以下結果:

0:000> !printexception
Exception object: 020695e4
Exception type: System.Exception
Message: Balance could not be negative
InnerException: <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131500

我們可以看到這是一個自訂的Exception,對應到的程式碼如下:

 try
            {
                con.Open();
                cmd.ExecuteNonQuery();
                this.Balance = this.Balance - amount;
                if (this.Balance < 0)
                {
                    throw new Exception("Balance could not be negative");
                }
            }

好~回到正題,到底在dump裏怎麼找到連線字串呢? 我們可以透過"!dso" 指令,其完整名稱是"!dumpstackobject",這個指令會將當前正在執行的程式中所有的物件列舉出來。執行結果如下:

0:000> !dso
OS Thread Id: 0x1404 (0)
ESP/REG  Object   Name
0030ec20 020695e4 System.Exception
0030ec6c 020695e4 System.Exception
0030ec80 01fcbbfc HangDemo.Account
0030ec84 02066380 System.Windows.Forms.MouseEventArgs
0030ec94 01fcbbfc HangDemo.Account
0030ecb0 020695e4 System.Exception
0030ecb8 01fcbbfc HangDemo.Account
0030ecc0 02066380 System.Windows.Forms.MouseEventArgs
0030ed14 020695e4 System.Exception
0030ed34 020695e4 System.Exception
0030ed38 01fcbbfc HangDemo.Account
0030ed3c 02069468 System.Data.SqlClient.SqlCommand
0030ed40 020693ac System.String    update accounts set balance = balance + (-6000) where accountno=123 and accounttype=2
0030ed44 02069390 System.String    6000
0030ed48 02069290 System.String    update accounts set balance = balance + (-
0030ed4c 02069358 System.Data.SqlClient.SqlConnection
0030ed54 02069468 System.Data.SqlClient.SqlCommand
0030ed58 02069358 System.Data.SqlClient.SqlConnection
0030ed5c 01fcbbfc HangDemo.Account
0030ed88 02066380 System.Windows.Forms.MouseEventArgs
0030ed8c 02066380 System.Windows.Forms.MouseEventArgs
0030ed90 0200d500 System.EventHandler
0030ed94 0200c390 System.Windows.Forms.Button
0030edb4 02069274 System.String    6000
0030edb8 01fcbbfc HangDemo.Account
0030edbc 01fcba60 HangDemo.Form1
0030eddc 0200c390 System.Windows.Forms.Button
0030ede8 02066380 System.Windows.Forms.MouseEventArgs
0030edec 0200d4d0 System.ComponentModel.EventHandlerList
0030edf4 0200c390 System.Windows.Forms.Button
0030edf8 02066380 System.Windows.Forms.MouseEventArgs
0030ee04 0200c390 System.Windows.Forms.Button
0030ee08 02066380 System.Windows.Forms.MouseEventArgs
0030ee30 0200c390 System.Windows.Forms.Button
0030eeec 0200c390 System.Windows.Forms.Button
0030ef08 0200c460 System.Windows.Forms.Control+ControlNativeWindow
0030ef14 0200c390 System.Windows.Forms.Button
0030ef48 0200c390 System.Windows.Forms.Button
0030ef4c 0200c460 System.Windows.Forms.Control+ControlNativeWindow
0030ef70 0200c460 System.Windows.Forms.Control+ControlNativeWindow
0030ef80 0200c460 System.Windows.Forms.Control+ControlNativeWindow
0030f0c8 020098bc System.Windows.Forms.Application+ThreadContext
0030f120 020098bc System.Windows.Forms.Application+ThreadContext
0030f164 0201b5c4 System.Windows.Forms.NativeMethods+MSG[]
0030f168 020098bc System.Windows.Forms.Application+ThreadContext
0030f170 02019a14 System.Windows.Forms.Application+ComponentManager
0030f1e8 020098bc System.Windows.Forms.Application+ThreadContext
0030f214 02067c9c System.Windows.Forms.Internal.DeviceContext
0030f224 020169a0 System.Windows.Forms.ApplicationContext
0030f248 02067c9c System.Windows.Forms.Internal.DeviceContext

我們以用Ctrl + F 來搜尋跟SQL有關的物件,從左邊算過來第二列是物件的記憶體位址,有了位址後,我們可以透過"!do" (DumpObject)指令來顯示物件的詳細資訊。

以上面紅色high light的sqlconnection為例,我們輸入!do 02069358 ,其結果如下:

0:000> !do 02069358 
Name: System.Data.SqlClient.SqlConnection
MethodTable: 5f5ed75c
EEClass: 5f50c1e4
Size: 56(0x38) bytes
 (C:\Windows\assembly\GAC_32\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
70f10508  400018a        4        System.Object  0 instance 00000000 __identity
706a29d8  40008c3        8 ...ponentModel.ISite  0 instance 00000000 site
706bccac  40008c4        c ....EventHandlerList  0 instance 00000000 events
70f10508  40008c2      108        System.Object  0   static 00000000 EventDisposed
5fa09008  4000bd6       10 ...hangeEventHandler  0 instance 00000000 _stateChangeEventHandler
5fa2dc58  400171c       14 ...t.SqlDebugContext  0 instance 00000000 _sdc
70ee43b8  400171d       30       System.Boolean  1 instance        0 _AsycCommandInProgress
5f5f0e84  400171e       18 ...ent.SqlStatistics  0 instance 00000000 _statistics
70ee43b8  400171f       31       System.Boolean  1 instance        0 _collectstats
70ee43b8  4001720       32       System.Boolean  1 instance        0 _fireInfoMessageEventOnUserErrors
5f5ef410  4001723       1c ...ConnectionOptions  0 instance 01ff3c88 _userConnectionOptions
5f5eef50  4001724       20 ...nnectionPoolGroup  0 instance 01ff4228 _poolGroup
5f5ef54c  4001725       24 ...onnectionInternal  0 instance 01ff6d88 _innerConnection
70f12b38  4001726       28         System.Int32  1 instance        0 _closeCount
70f12b38  4001728       2c         System.Int32  1 instance        5 ObjectID
70f10508  400171b      798        System.Object  0   static 01fcc170 EventInfoMessage
5f5eedbc  4001721      79c ...ConnectionFactory  0   static 01fcc17c _connectionFactory
70f126b4  4001722      7a0 ...eAccessPermission  0   static 01fe1a74 ExecutePermission
70f12b38  4001727      880         System.Int32  1   static        5 _objectTypeCount

上面紅色high light是幾個主要的資訊,在Type的部份,如果是整數,布林,字串的話,在Value的部份會直接顯示它的值。其它的話會顯示物件的記憶體位址。

因此只要您有興趣,都可以用"!do"這個指令來看任何的物件。 接下來我們來看 _userConnectionOptions (!do 01ff3c88),然後看到_usersConnectionString (!do 01fcc02c ),之後我們就看到了熟悉的連線字串:

0:000> !do 01fcc02c 
Name: System.String
MethodTable: 70f108ec
EEClass: 70ccd64c
Size: 174(0xae) bytes
 (C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: Data Source=.\SQLEXPRESS;Initial Catalog=HangDemo;User ID=sa;Password=08010921
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
70f12b38  4000096        4         System.Int32  1 instance       79 m_arrayLength
70f12b38  4000097        8         System.Int32  1 instance       78 m_stringLength
70f115cc  4000098        c          System.Char  1 instance       44 m_firstChar
70f108ec  4000099       10        System.String  0   shared   static Empty

如果Value欄位是0的話,表示是Null值 (布林型別除外)。

今天針對WinDbg debug .NET 應用程式做一個基本的介紹,後續會進入到Hang的主題。希望各位也可以把範例下載下來練習練習。

Posted by Terry Lin | 0 Comments
Filed under: ,

TechEd2008- .NET 應用程式除錯秘技系列(3) - Internet Explorer Crashed

在這個範例裏,會首度介紹WinDbg這個工具(之前有插播Kernel Debugging)。坦白說,在還沒有進微軟之前,我也不曉得有WinDbg這工具(謎之音:就算知道也不懂怎麼用啊~~),一切就在於國內關於這方面知識的介紹實在太少。我想也跟debugging本身並不是一門顯學也有關係。坊間的書大都教大家怎麼寫程式,但很少提到怎麼除錯。而實際上除錯也沒有想像中的簡單,除了對程式語言及邏輯的了解以外,還需要對系統的行為有所了解。

當然,除錯自己的程式總比除錯別人的程式來得容易(尤其是在有source code的狀況下)。反觀我們在幫客戶除錯時,大都沒有客戶的程式碼,這在難度上就更高了,甚至在某些狀況下,都要用合理的懷疑來判斷可能造成問題的原因。但也因此才能累積那麼多的經驗。

這個範例的專案可以在這裏下載。

這個問題是這樣的,使用IE去訪問一個簡單的ASP.NET 頁面,卻造成了IE Crash,而已幾乎每次都會發生。頁面與程式如下:

1) 頁面上只放了一個TreeView 控製項

019

2) 在Page_Load時讀取本機上的XML並將內容加到TreeView

020

3) 程式執行畫面如下:

021

4) 當點下連結後,IE 就Crash了(不見得點一次就會Crash,有時候要多點幾次)

022

當這樣的問題發生時,各位認為是ASP.NET網頁的問題還是IE的問題呢? 如果客戶跟你說,IE去瀏覽其他網頁都沒問題,而且用其他Browser訪問這個網頁也沒有問題,當下是不是也會覺得是產品的問題呢? 但不管是不是我們產品的問題,總是要解決嘛~ 既然這麼容易重現問題,我們可以用WinDbg來幫助我們做初步的分析。

但這時候問題又來了,到底我們要先看IIS的W3WP.exe還是IE的Iexplore.exe呢? 以經驗上來判斷,一個server side的網頁是不會造成用戶端的IE Crash的。因此我們將焦點放在IE, 看到底是什麼原因造成的IE 的Crash。

1) 首先我們再重新開啟IE並訪問有問題的網頁,然後開啟WinDbg並attach到Iexplore.exe這個處理序(Process)。

023 024

2) 當Windbg attach到IE時,一開始會先中斷下來(此時您無法操作IE),您必須輸入"g"這個指令(亦即go的意思),才能讓程式繼續執行。

026

3) 當指令列顯示如下時,就可以繼續操作IE

027

4) 這時候我們必須讓問題重現,當IE再次Crash時,WinDbg將會偵測到並中斷下來。此時我們輸入"k"這個指令,顯示目前執行緒的呼叫堆疊(Call Stacks)。

請注意紅色框起來的部份,在WinDbg中,除了方法的起始及返回的記憶體位址外,若有載入對應的symbol檔,就會顯示方法名稱以及參數等資訊。以下圖為例,在!左邊是模組名稱,右邊是方法名稱。因此Kernel32!HeapFree 表示程式在此處執行的是Kernel32.dll的HeapFree方法。

028

但HeapFree這個方法在作業系統已經好幾年了,是一個非常基本的方法,出問題的機率實在太低了。通常針對這樣的問題,我們會先看看是不是有其他非微軟的模組被載入並且正在執行,比較常見的有Flash Player啦或其他的Add-on,再不然就是廣告軟體、間碟軟體或病毒等。在這個例子中,我們看到呼叫HeapFree的是底下稱為Jccatch的QueryInterface的方法。但這並不是微軟的模組,因此若要檢視模組的詳細資訊,我們可以輸入"lmvm <Module Name>"此例為"lmvm jccatch",如下圖所示:

029

從上面的圖我們可以看到,這是一個Flashget的Add-on。有了這樣的訊息後,我們可以想想Flashget和IE的關聯性。FlashGet是很知名的下載軟體,他還會監視IE有沒有下載檔案的行為,進而取代IE進行下載的動作。由此我們可以測試看將IE裏與FlashGet相關的Add-on先暫時disable後,問題還會不會發生。

030

在Disable上圖所框選的3個Add-on後再進行測試,問題就不再發生了。

 

後記:其實這個範例還沒完全結束,原因是這個問題之所以會造成IE Crash,是由於Heap corruption所導致,因此不見得點一下TreeView上的Node都會造成IE Crash,有時要點好幾下,甚至點了不下50次問題都不會發生。但這個範例是很容易重現問題的。後續我們有機會再來介紹Gflag這個工具,針對Heap corruption的問題,使用Gflag可以加速問題重現。

Posted by Terry Lin | 0 Comments
Filed under: ,

插播--作業系統Blue Screen 也能Debugging

這星期有點小忙~所以只能趁著休假把該還的幾篇TechEd文章寫一寫。當我正要開始寫的時候(本來系列之3要寫IE 訪問由ASP.NET寫的網頁會Crash,這個問題與Flashget有關),我的桌機就出現BSOD(Blue Screen On Death),已經很久沒有碰到過Blue Screen了,當然要趁這個機會來debugging一下。我的桌機是四核心加上8GB的RAM. 執行的作業系統是Windows Server 2008 64 bit的版本。

在系統Crash後,在進階系統設定的啟動及修復選項中,預設是會在%Systemroot% 目錄下產生一個名為 MEMORY.DMP的檔案,下圖是我的設定:

snap013

在重新開機後,馬上打開我的WinDbg來debug。 之前在TechEd都是示範如何debug user mode的應用程式,但像這種系統crash的狀況,我們要分析的是Kernel mode的dump。坦白說,如果是單純Crash的話,只要透過一個指令,有70%的機會可以找到答案(別問我70%怎麼來,只是機會真的很高)。產生的dump檔約550MB

snap014

在這邊有一點要注意,如果作業系統是X64,建議最好同時安裝32bit 及 64bit的Windbg。原因是,如果您要Debug的是32bit的程式, 就要用32bit的Windbg,如果是64bit的程式或者是系統本身,就要用64bit的Windbg。 如果用錯版本,會有相關的錯誤訊息,而且得到的資訊也會不對。由於我的OS是64bit,因此我就打開64bit的WinDbg, 並把dmp檔直接拖到WinDbg視窗裏:

dump一拖進來後,會先上網下載對應的symbol,所以要等個1~2分鐘。再來介紹一下幾個重要的資訊:

1. Windbg的版本

2. 作業系統資訊

3. 產生dump的時間

4. 這是最重要的啦,就是在先前講到的指令(!analyze -v),常常透過這個指令就能找到答案,待會再說明。

5. 這個位置,如果是debug user mode dump,會顯示thread ID, 如果是Kernel mode dump,則顯示"Kd"。

snap015

好的,其實也不用打指令,我就知道答案了,請看"*"的地方,人家有講可能的原因是來自於一個名為"netr7064.sys"的檔案(其實它是driver)。但還是打個指令看看好了(直接點一下畫面中 !analyze -v的連結也可以)

Driver的東西我是不懂的,但從下面的call stack,也可以看到最後呼叫的module就是netr7064.sys (MS的module先排除):
您可以看到底下的記憶體位址是8位數`8位數 (這是64bit的記憶體位址表示方法,32只有8位數)。

snap016

在顯示結果底下有module相關的訊息:

snap017

手移過去後,底下狀態列就會顯示Hyper Link會執行的指令是 "lmvm netr7064",執行結果如下:

snap018

知道了出問題的檔案路徑及檔名,我們就可以上網search 一下這個檔案,發現這是一個wireless card driver的檔案。到我的裝置管理員看一下,證實無誤!

snap019

像這類由driver所引發的blue screen, 解決的方法當然是看有沒有新版可以更換(有時候可能需要換回舊版才會正常),像我是拿Vista X64的driver來裝,用了一陣子沒問題以為沒事,誰知還是遇到了,原先的版本是3.00.2,上網找到3.01.0,更新後果然就沒問題了。

 

如何? 是不是很簡單呢? 要學會分析dump,第一件事就是......要敢打開來看。雖然我不太懂Driver,但打開來看後,還是有機會可以看出問題原因並解決的。但有一點必須說明,不是所有Kernel mode dump都那麼容易分析的。先前有提到,如果是Crash,而且問題出在device driver或防毒軟體,則可能好解決。如果是系統Hang 或kernel resource 不足或跟page pool有關的Kernel dump,恐怕就沒那麼簡單囉~

Posted by Terry Lin | 0 Comments
Filed under:

TechEd2008- .NET 應用程式除錯秘技系列(2) - Design Time Exception

故事是這樣的~ 我建立了一個簡單到不行的web 專案, 裏頭只有一個sitemap設定及default page. 在default page中放了一個TreeView控制項。並且將TreeView的DataSource指定為SiteMapDataSource,結果當我要看設計畫面時,一個錯誤噴出了!!

snap006

不是應該要出現一個美美的(其實也還好)TreeView,然後顯示各個節點(TreeNode)的資訊嗎?

範例可以在這裏下載!!

 

開發工具本來就是讓我們用來開發除錯的~結果開發工具自己卻出錯(汗...),怎麼來處理這個問題呢? 我們可以用另外一個VS2008來debug有問題的web 專案!!

首先先開啟另一個Visual Studio 2008, 點選 Tools -> Attach To Process , 選擇有問題的web專案(PID是4820)

snap007

接下來我們要設定讓VS2008在接收到什麼Exception時會中斷下來,預設是沒有設定的(就像是程式跑在debug mode,但沒有設定任何斷點一樣)但這裏不是設定斷點,而是Exception事件觸發,我們可以在Debug -> Exceptions加以設定,但要設定什麼呢? 這時候就必須倚賴經驗了,從上面的錯誤訊息,我們可以推斷它跟"URI"有關,因此我們可以透過"Find"按鈕來去尋找相關的exception,如下圖所示,我們透過"URI"這個關鍵字找到了"System.UriFormatException",記得勾選旁邊的小框框, 以確認在這個exception發生時,VS2008能被通知到。

snap009

除了"URI"以外,您也可以嚐試其他不同的關鍵字,例如"FileNotFound"、"NullReference"、"SocketException".... 如果測試時發現您所指定的Exception並不能夠被觸發,表示您設定的Exception是不正確的,您可以多指定幾個,或是將這個Exception的上一層全部選取(以此例來說,您可以選取"System"),但system底下的事件不少,在實際的除錯當中,可能會常被中斷。

snap010

設定好Exception之後,我們要切回原本有問題的專案,讓問題重現(就是再開啟一次default.aspx的設計畫面)。若是您有設定讓VS2008下載.NET Framework source code (請參考前一篇文章),用來debug的VS2008會自動下載相關的source code並斷在出錯的地方。

snap011

我們可以看到,發生錯誤的function是 "CreateThis",在這個funtion裏呼叫並執行完"InitializeUri"後,拋出了一個UriFormatExcption。根本的原因是,TreeView在設計時期會檢查資料來源的路徑,若是一個絕對路徑,則會造成解析上的錯誤,我們再來看一下TreeView的DataSource,也就是SiteMapDataSource:

snap012

的確有一個絕對路徑指向"Http://www.microsoft.com",儘管這會造成Design Time的小錯誤,但專案還是可以執行的。

 

補充:

這個問題若要透過WinDbg來分析是比較困難的,原因如下:

1. WinDbg只能針對CLR Exception去Handle,無法細到要去handle某一個特定的.NET Exception ( Win32的Exception是可以的)

2. 無法搭配source code來檢視問題發生原因。

 

有興趣挑戰的朋友,可以試試看。後面再介紹完TechEd系列後,會接著介紹WinDbg的基本概念,有機會再來分享這個問題如何從WinDbg分析。

Posted by Terry Lin | 2 Comments
Filed under: ,

TechEd2008- .NET 應用程式除錯秘技系列(1) - 在VS2008中對.NET Framework source code進行除錯

終於順利講完今年的TechEd, 在課程結束後,有人上講台詢問了相關的問題,其中最多人問到的是 - 在這個場次講到的工具都是公開的嗎? 感覺以前沒有人講過類似的議題..... 是的~除了Visual Studio是需要付費購買的以外,其他介紹到的工具都是可以免費下載的。至於跟debugging 相關的教學資源,在國內的確很少,而即使在國外,也沒有系統化的教材,相關的資源的確比較分散。

 

主要是因為我認為debugging 並不是一門顯學,進入的門檻也比較高,而真正使用到的場合則是因人而異。如果應用程式是自行開發,對source code有非常深入的了解,則在除錯時可能只要靠log檔或錯誤訊息就能解決大部份的問題了。 但對於我們技術支援的角色而言,常常是拿不到客戶的source code的(就算客戶願意給,可能整個source project review下來~其他case也不用做了)。所以debugging 對我們來講是很重要的技能。

 

在進入微軟之前,我對memory dump也一無所悉,而在我們的內部訓練當中,也只針對基本的除錯有相關的訓練。這是因為問題的類型千變萬化,工具的熟練對除錯的幫助並不大,必須靠著紥實的基本概念及除錯的經驗,才能逐步的踏向除錯達人之路。

 

出來混總是要還的,在講這個課程的時候,答應大家會將裏頭的內容全部分享出來,並且對用到的相關指令及操作做一個較為完整的介紹,因此我會針對課堂中所講到的5個sample,外加原本有準備但被刪掉的3個sample做一系列的說明。 以下是我在做這些sample時所用的設備及環境說明:

 

1. CPU - Intel Petium M 1.73G

2. RAM - 2GB

3. HDD - 250GB

4. OS - Windows Vista Enterprise Edition with SP1

5. Visual Studio 2008 Team System RTM version

6. Debugging Tools for Windows (Windbg) 6.9.0003.11

 

首先介紹到的是,如何在Visual Studio 中debug .NET Framework Souce code,點我下載source code

2張圖片說明相關的設定(投影片也有,可以在颱風退散~還我TechEd 2008文章裏下載)

 

1. Tools => Options => Debugging => General

014

2. Tools => Options => Debugging => Symbols

015

在設定好之後,我們透過範例中很簡單的一段程式來進行測試。我們在Windows Form上佈了一個button, 並用以下程式做為button_click的處理

  private void button1_Click(object sender, EventArgs e)
        {
            foreach (string d in Directory.GetDirectories("c:\\"))
            {
                this.textBox1.Text += d + "\r\n";
            }
        }

當我們在foreach 迴圈設定斷點後,按下F11進行單步偵錯(Step-in),VS 就會幫我們下載有用到的source code,並且可以針對.NET Framework 的source code進行除錯,如下圖所示:

016

雖然我們使用Directory.GetDirectories方法時只傳入一個參數(path),從上圖可以看到,它會自動呼叫其他overload的方法。

 

以上的設定方式可以讓VS2008連到source server 將source code及symbol (.pdb) 下載下來,但可不可以將所有的source code一次下載下來呢?

這樣講當然是有囉~ 在codeplex上有兩個牛人寫了一支程式可以將所有版本的source code都完整的下載下來,在CodeProject也有一篇專文介紹如何在VS 2005及VS 2008 Expression Edition進行相關的設定,請參考:

NetMassDownloader Download .Net Framework Source Code At Once Without Any Visual Studio Installed , Enables Offline Debug In VS 2008,VS2008 Express Edition,2005 And CodeGear Rad Studio.

 

另外針對可編譯的.NET Framework,有一個稱為Rotor的計劃(又稱為SSCLI),各位可以至Microsoft Download下載,這是一個可編譯的版本,包含C#的compile也在裏頭,若您要自行編譯,其系統需求如下:

System Requirements
  • Supported Operating Systems: Windows XP Service Pack 2

On Windows you will need:

由於裏頭有部份的source code是用ANSI字元存檔,因此在中文的作業系統要進行編譯時可能會發生問題,這裏有相關的解答。

Posted by Terry Lin | 1 Comments
Filed under: ,

颱風退散~還我TechEd 2008

去年的TechEd 2007 第一天剛好遇到颱風,以致於第一天的課程要拆到後兩天來上,我想我們負責TechEd的伙伴一定忙壞了~ 誰知道今年剛好在這幾天也有颱風逼近,目前預估應該會從巴士海峽掃過,希望它也是依照既定的行程來走~畢竟這是我在TechEd的處女秀啊~。 今年會在第二天下午的第一個場次跟大家分享.NET 除錯秘技 (DEV305)

 

說是秘技,一點也不為過。這個課程是我和我的同事Louis Wang在今年年初規劃的,針對我們的主要客戶及夥伴所上的2天的課程。由於很多學員在上完課後反應熱烈~因此一口氣開了5場! 之後在規劃TechEd 2008 時,便將這課程重新設計了一下,將它縮減為1個小時的課程。 2天的課怎麼縮減到1個小時呢? 當然就是把重點中的重點,精華中的精華把它截取出來講囉!! 有興趣或是還沒決定要不要來上的朋友,可以先下載下面的投影片一賭為快!!

點我下載投影片。

由於這是一個不算簡單的課程,因此我和DPE部門的王森決定,會將課程中所介紹到的工具及所有內容,透過我的Blog或是MSDN短片的方式做一個完整及延伸的整理,讓對這個議提有興趣的朋友,在國內至少還找得到一些Debugging 的資源。此外,原本我在這個課程中準備了8個demo(瘋了),後來在第一次自己rehearsal的時候,發現整個講完要2個半小時。於是只好忍痛砍了3個Demo,讓課程可以控制在一個小時結束。 第一次接觸debugging 或dump的朋友,可別被一堆指令嚇到囉~ Terry 會在這個平台上持續的分享平時我們做case的一些經驗及相關的文章。 讓大家都可以成為除錯達人唷!!

Posted by Terry Lin | 3 Comments
Filed under:

如何在網頁中內嵌 .NET User control

這是上個月在MSDN "教學短片" 所分享的一個技巧,由於我所在的部門(GTSC - Global Technical Support Center),現在叫做CSS (Customer Service and Support),主要以提供客戶技術服務為主(是做服務,但偶爾也要做做功德)。因此我們會不定期的整理客戶常問的問題,並透過不同的管道(如Microsoft 技術支援服務首頁,MSDN教學短片網站等等)。而最近最常被問到的,就是這個問題。

其實客戶會問這個問題是有原因的,因此Visual Basic 6, 這個全球超過百萬開發人員在使用的語言版本,已經在今年的4/8正式引退,微軟不再對Visual Basic 6 提供技術支援。另外一個比較困擾人的是VB6 runtime的部份。VB6的runtime(msvbvm60.dll)仍然被內建在後續的作業系統中(Windows Vista / Windows Server 2008),在這篇文章中有非常詳細的說明。也正因為如此,以前我們常常在網頁中使用的ActiveX元件,目前可被支援的技術剩VC++及.NET。

回到正題,對這個技術有興趣的朋友,可以參考一下底下的圖片連結(還有範例的程式可以下載):

snap371

 

這個影片包含2個範例,第一個範例示範如何讓網頁與.NET的user control進行互動,所謂的互動,就是.NET的user control可以接受來自網頁所傳過來的參數,或是由網頁來接收或取得user control的屬性。 第二個範例是將.NET 的ReportViewer控制項放在user control裏,然後再放到網頁中,這樣的好處是可以享受Windows 控制項所帶來的優點,例如分頁及列印的能力 (在Web ReportViewer控制項, 要去實作分頁有點麻煩,使用page-break 不見得能很精準的在指定的筆數進行分頁)

Posted by Terry Lin | 0 Comments
Filed under:

部落格本鋪開張!!

一直想說要寫個Blog大概有N年了吧! 但工作忙加上自己愛玩,所以就把這事一直擱著。 直到最近公司發佈了一項德政,寫Blog也能算進我們的工時,也不用懷胎十月,我的Blog就這麼開了。當然前提是要跟技術有關的囉~所以這個Blog算是Terry 的技術本鋪。既然有本鋪~會不會有分鋪呢? 或許吧~畢竟宅男的生活除了技術以外,沒多少其他東西可以寫啦~

既然是開張的第一帖,我想也不好寫太硬的東西,於是想到把年初跟我的同事Louis Wang拍的一支MSDN Video再貼出來給大家笑笑。

 

這個短片當初的設定是要把我們support客戶時的常見問題,透過短片讓大家都能夠有所助益,景氣不好,能少開個case就少開。 但我後來發現看過的人都說...你怎麼演得那麼白痴啊~ 每句台詞後面都要加個ㄏㄏ... 然後平時很喜歡笑的Louis要這麼嚴肅. 其實是我們達不到導演想要的意境啊~~~

Anyway, 在這個短片中還是有幾個重要的東東要注意的,後續有機會會再針對下面的課題所深入的討論。

 

Office 伺服器端自動化的考量因素

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

ASP.NET 出現高記憶體層級時,應快速檢查的事項

http://support.microsoft.com/kb/893660/zh-tw

當您在執行 Windows Server 2003 的電腦上裝載使用 ASP.NET 的 Web 應用程式時,可能會收到錯誤訊息,或者電腦可能停止回應

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

Performance is poor when you run a non-interactive .NET Windows server application on a multiprocessor computer

http://support.microsoft.com/kb/840523/en-us

針對使用者帳戶控制相容性的 Windows Vista 應用程式開發需求

http://www.microsoft.com/taiwan/msdn/library/2007/Sep/vista/default.mspx

Windows Vista Security Guide

http://www.microsoft.com/downloads/details.aspx?familyid=a3d1bbed-7f35-4e72-bfb5-b84a526c1565&displaylang=en&Hash=xTvoosH2qisFyS2tIKJedS5J9cW5x7UmGZmwvX1jPsCZTbk%2fbkQCTYd9ggExrwjLtMbvnZD6Tt9w3m6sZpxdZQ%3d%3d#filelist

Data Access and Transactions

http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit/iisbook/c07_server-side_cursors.mspx?mfr=true

HOW TO:針對 .NET Framework 版本使用應用程式組態檔

http://msdn2.microsoft.com/zh-tw/library/9w519wzk(VS.80).aspx

Breaking Changes in .NET Framework 2.0

http://msdn2.microsoft.com/zh-tw/netframework/Aa570326.aspx

Posted by Terry Lin | 0 Comments
Filed under:
 
Page view tracker