這個問題的範例請從這裏下載,DB在此。
在這個範例當中,我們分成2個部份介紹,第一個部份是 - 如何在dump中找出SQL connection string。第二個部份才是介紹如何處理Idle hang的狀況。
在開始debug之前,由於我們要debug的是.NET 的應用程式,因此在WinDbg裏要先啟用CLR Exception的event。方法如下:
WinDbg=>Debug=>Event Filters ,在列表中找到CLR Exception並點選右邊的"Enable"。設定好之後就可以開始來Debugging囉~
問題重現步驟:
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斷下來。
6. 我們看到WinDbg攔到了一個first chance exception
此時我們需要載入針對.NET 應用程式所開發的WinDbg擴充程式 -- SOS.dll。
7. 在WinDbg輸入指令 ".loadby sos mscorwks "。輸入指令後,您可以利用!help來查看相關的指令及用法。
既然發生了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
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的主題。希望各位也可以把範例下載下來練習練習。