.Net 調式案例—實驗2 崩潰(Crash)複習回顧 數據庫
如今發佈第二個實驗,後面會發佈一個有關內存的實驗。 windows
問題在現 瀏覽器
1) 瀏覽 http://localhost/BuggyBits/Reviews.aspx 這個頁面。 app
2) 點擊刷新按鈕,這會致使w3wp.exe 進程,iis5 上面是aspnet_wp.exe。 框架
注意:若是你安裝了Visual Studio,一個「Just-In-Time」的調試對話框會彈出來(點擊「否」),然而,這個消息框會始終在那裏並等待用戶輸入而後結束這個應用程序,你能夠禁止JIT調試,方法:把註冊表中的下面兩個鍵值刪除掉: asp.net
·HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ ide
Windows NT\CurrentVersion\AeDebug\Debugger 函數
·HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\ 工具
DbgManagedDebugger this
若是是64位的系統,還應該把下面兩個刪除:
·HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\
Windows NT\CurrentVersion\AeDebug\Debugger
·HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\
DbgManagedDebugger
檢查事件查看器
1) 打開應用程序和系統的事件查看器,下面的信息會因操做系統合iis版本的不一樣而有所差異,但大概意思都是同樣的
Event Type: Warning
Event Source: W3SVC
Event Category: None
Event ID: 1009
Date:2008-02-08
Time: 10:12:06
User: N/A
Computer: MYMACHINE
Description:
A process serving application pool 'DefaultAppPool' terminated unexpectedly. The process id was '4592'.
The process exit code was '0xe0434f4d'.
For more information, see Help andSupportCenterat
http://go.microsoft.com/fwlink/events.asp.
Q:你看到了什麼事件?
A:在個人機器上(Win2k3,.net2.0 sp1),我獲得了上面的如上面的系統事件日誌和以下面的兩個應用程序日誌,從第一個咱們能夠看到有某種類型的nullreference 的異常。
Event Type: Error
Event Source: .NET Runtime 2.0 Error Reporting
Event Category: None
Event ID: 5000
Date:2008-02-11
Time: 11:13:18
User: N/A
Computer: MYMACHINE
Description:
EventType clr20r3, P1 w3wp.exe, P2 6.0.3790.3959,………………
第二個錯誤徹底的給咱們講清楚了是一個空引用的異常,它發生在Review類的
finalizer/destructor中,咱們所獲得的好處是,咱們不須要去再w2k3 中去調試,基於IIS、操做系統和.net框架的幫助,咱們就獲得了第二個錯誤。
Event Type: Error
Event Source: ASP.NET 2.0.50727.0
Event Category: None
Event ID: 1334
Date:2008-02-11
Time: 11:13:15
User: N/A
Computer: PRATHER
Description:
An unhandled exception occurred and the process was terminated.
Application ID: /LM/W3SVC/1/Root/BuggyBits
Process ID: 4944
Exception: System.NullReferenceException
Message: Object reference not set to an instance of an object.
StackTrace: at Review.Finalize()
Q:這個0xe0434f4d 退出碼什麼意思?
A:這個錯誤代碼是表明 dotnet異常的本地(原生)錯誤代碼,任何dotnet的錯誤異常都會被轉換成0xe0434f4d,不管dotnet 的錯誤是SQLException, NullReferenceException 仍是 FileNotFoundException。
Q:你能說出是什麼緣由致使了崩潰的發生麼?
A:在上面說起的例子中,第二個異常的事件報告說明了原應。進程的崩潰是由於沒有被捕獲的在finalizer 線程中的一個空引用的異常。
獲得一個dump文件
1) 轉到瀏覽器的http://localhost/BuggyBits/Reviews.aspx 這個頁面,但不要點擊刷新。
2) 打開一個命令行窗口,切換到調試器目錄,輸入:
adplus -crash -pn w3wp.exe
敲回車。
Q:一個新的windows窗口會出如今工具欄上,它是什麼?
A:這是一個調試器,若是你最大化它,你會看見命令窗口,cdb正在運行,cdb是命令行方式下的WinDbg。
Q:調試器在等待什麼?提示:查看 Adplus 有關crash的幫助。
A:下面是一個表格,顯示了adplus監視器默認的 事件(events)/ 異常(exceptions),和它會作什麼,當發生這些事件時。
一些縮寫字母的含義:L=Log entry in debuglog, T=Record Time, S=Log stack, M=Mini dump, F=Full dump, E=log in the eventlog。你能夠本身編寫一個adplus 的配置文件,來改變這些事件被處理的方式。
Code |
Exception/Event |
Explanation/Note |
1st Chance |
2nd Chance |
av |
Access Violation |
An attempt to access to uninitialized memory. |
LTSM |
LTSFE |
ch |
Invalid Handle |
An attempt to access data using an invalid handle. Eg. window, file or other native handle. |
LTSM |
LTSFE |
ii |
Illegal Instruction |
An attempt is made to execute an invalid instruction. |
LTSM |
LTSFE |
dz |
Integer divide |
Integer divide by zero. Even in .net. |
LTSM |
LTSFE |
c000008e |
Floating divide |
Floating point divide by zero. |
LTSM |
LTSFE |
iov |
Integer overflow |
An attempt to store more than 32/64 bits in an integer. |
LTSM |
LTSFE |
lsq |
Invalid Lock Sequence |
An attempt is made to execute an invalid lock sequence. |
LTSM |
LTSFE |
sov |
Stack overflow |
The memory used for the stak (method pointers, parameters, local vars etc.) exceeds the amount allocated for the stack. Typically caused by recursion. |
LTSM |
LTSFE |
eh |
C++ EH exception |
C++ EH Exception |
LTS |
LTSFE |
* |
Unknown exception |
Any exception not specifically defined in this list |
LTS |
LTSFE |
clr |
.NET Exception |
Any .net exception |
- |
LTSFE |
bpe |
Breakpoint |
A break point is hit or the debugger window is shut down. |
LTSME |
- |
wkd |
Wake Debugger |
Wake debugger. |
LTSME |
- |
ld |
Dll Load |
A dll is loaded in the process |
L |
L |
ud |
Dll Unload |
A dll is unloaded from the process |
L |
L |
epr |
Exit Process |
The process exits. |
LTSFE |
- |
aph |
Application Hang |
The Windows OS has determined that the process is no longer responding. |
LTSM |
LTSFE |
cce |
Ctrl+C Console App |
Ctrl+C or Ctrl+Break is passed to a console app. |
LTSM |
LTSFE |
dm |
Data Misaligned |
A unit of data is read from or written to an address that is not a multiple of the data size. |
LTSM |
LTSFE |
gp |
Guard page violation |
An attempt to access data within a guard page. |
LTSM |
LTSFE |
ip |
In Page IO Error |
I/O Error |
LTSM |
LTSFE |
isc |
Invalid System Call |
Invalid System Call |
LTSM |
LTSFE |
sbo |
Stack Buffer overflow |
Stack buffer overflow |
LTSM |
LTSFE |
3) 再現問題,點擊那個瀏覽器的刷新按鈕。
Q:在dump目錄下什麼文件被建立了 ?提示:dump的文件夾在帶有crash模式的名字和今天的日期時間的調試器路徑下。
A :下面的文件是個人crash模式文件夾
ADPlus_report.txt
<DIR> CDBScripts
PID-4628__W3WP.EXE__Date_02-11-2008__Time_13-14-0808.log
Process_List.txt
PID-4628__W3WP.EXE__1st_chance_AccessViolation__mini_17a4_2008-02-11_13-14-19-358_1214.dmp
PID-4628__W3WP.EXE__1st_chance_Process_Shut_Down__full_17a4_2008-02-11_13-14-31-889_1214.dmp
PID-4628__W3WP.EXE__2nd_chance_NET_CLR__full_17a4_2008-02-11_13-14-20-093_1214.dmp
看看幾個重點地地方:咱們有一個1st(第一次)的違背正常訪問 的mini dump(最小化dump,多是咱們的NullReferenceException)。緊跟在進程關閉(shutdown)dump文件後面,還有2nd(第二次)的CLR 異常(和NullReferenceException 同樣,可是如今是2dn,由於它尚未被處理)。你能夠經過按照時間排序的順序來觀察它們。
在WinDbg中打開dump文件
1) 打開帶有「2nd Chance CLR Exception」這些字樣的dump文件,注意到,這個dump文件在1st 的進程被shutdown後建立。注意:若是你有一個異常,你有再try/catch塊中處理它們的機會。它第一次被拋出來,就成爲1st 異常且不是致命的。若是你不處理它,它會變成2nd 異常,任何2nd的異常會中斷進程。
2) 設置符號文件的路徑。
在crash dump文件中,活動線程是致使異常的那個線程(由於dump是由一個異常觸發)。
Q:哪個線程是活動的,當你打開dump文件後?提示:看看WinDbg的命令提示符的工具欄
A:活動的線程是#20,我能夠用它來和 !thread (輸出是Finalizer 的線程)做比較,獲得更多信息。
20 2116c001ac540 b220 Enabled02f0d28c:02f0f130 00199400 0 MTA (Finalizer)
System.NullReferenceException (02f0cb3c)
在!thread 的輸出中,咱們還能夠看出該線程拋出了NullReferenceException,這個異常的地址是0x02f0cb3c。
檢查調用堆棧和異常
1) 檢查本地(原生)和託管堆棧。
kb 2000
!clrstack
Q:線程是什麼類型的?
A:從kb的輸出中咱們看到事實上是finalizer線程,可是!clrstack 並無給咱們完整的更多的有關託管堆棧的信息。看起來,咱們是在異常處理代碼的內部了。
0:020> kL 2000
ChildEBP RetAddr
02a0f9d879f071ackernel32!RaiseException+0x53
02a0fa387a10733d mscorwks!RaiseTheExceptionInternalOnly+0x2a8
02a0fa507a1073b1 mscorwks!RaiseTheException+0x4e
02a0fa787a1073e2 mscorwks!RealCOMPlusThrowWorker+0x71
02a0faa47a1073f0 mscorwks!RealCOMPlusThrow+0x30
02a0fab47a14f0d4 mscorwks!RealCOMPlusThrow+0xd
02a0fbe47a025a5b mscorwks!Thread::RaiseCrossContextException+0x41f
02a0fc9879f01747 mscorwks!Thread::DoADCallBack+0x293
02a0fcb879f04a71 mscorwks!ManagedThreadBase_DispatchInner+0x47
02a0fcc479f04b01 mscorwks!ManagedThreadBase::FinalizerAppDomain+0x25
02a0fd20 79efe5b4 mscorwks!SVR::DoOneFinalization+0x141
02a0fda8 79efe70amscorwks!SVR::FinalizeAllObjects+0x56
02a0fdc0 79ef3207 mscorwks!SVR::GCHeap::FinalizerThreadWorker+0xe7
02a0fdd4 79ef31a3 mscorwks!ManagedThreadBase_DispatchInner+0x4f
02a0fe68 79ef30c3 mscorwks!ManagedThreadBase_DispatchMiddle+0xb1
02a0fea4 79fb9643 mscorwks!ManagedThreadBase_DispatchOuter+0x6d
02a0fecc 79fb960d mscorwks!ManagedThreadBase_NoADTransition+0x32
02a0fedc 79fd0c91 mscorwks!ManagedThreadBase::FinalizerBase+0xd
02a0ff1479f95a2e mscorwks!SVR::GCHeap::FinalizerThreadStart+0xbb
02a0ffb8 7d4dfe21 mscorwks!Thread::intermediateThreadProc+0x49
02a0ffec 00000000 kernel32!BaseThreadStart+0x34
0:020> !clrstack
OS Thread Id: 0x116c(20)
ESP EIP
02a0fa8c7d4e2366 [GCFrame:02a0fa8c]
02a0faf0 7d4e2366 [GCFrame:02a0faf0]
02a0fcec 7d4e2366 [GCFrame:02a0fcec]
Q:線程在幹什麼?
A:它正在嘗試調用對象的finalizer方法,正在作這個的時候,它拋出了一個異常,該異常正在被看成2nd的異常被再次拋出。
2) 檢查異常拋出。
!pe 注意:!pe/!PrintException若是沒有跟參數會打印出在棧上的當前全部異常。
Q:什麼樣的異常?
A:一個NullReferenceException 異常
0:020> !pe
Exception object:02f0cb3c
Exception type: System.NullReferenceException
Message: Object reference not set to an instance of an object.
InnerException:
StackTrace (generated):
StackTraceString:
HResult: 80004003
注意:在一個案例中,好比在這裏,異常被再次拋出,原先的棧跟蹤可能會不可見,在這種狀況下,若是你找到原先的異常你會獲得更多的信息。
3)查看棧上的對象,找到原先的異常的地址
!dso
Q:原先異常的地址是什麼?提示:看看你剛纔的!pe 的輸出的再次拋出的異常的對象的地址,把它和對象棧中的地址比較,你可能會有不少異常,一些是再次拋出的異常的地址,但在底部有一個異常,你看,那就是原先的異常(查找地址不一樣的那個)。
A:惟一的一個地址和前面列出的(0x2f0cb3c)不一樣的是0x6f1e5e4。
0:020> !dso
OS Thread Id: 0x116c(20)
ESP/REG Object Name
02a0f9f402f0cb3cSystem.NullReferenceException
02a0f9f802f0cb3cSystem.NullReferenceException
02a0fa4002f0cb3cSystem.NullReferenceException
02a0fa5802f0cb3cSystem.NullReferenceException
02a0faac02f0cb3cSystem.NullReferenceException
02a0fabc02f0cb3cSystem.NullReferenceException
02a0fb5806f1e5e4 System.NullReferenceException
02a0fb5c02f0bd70 System.Byte[]
02a0fb7802f0cb3cSystem.NullReferenceException
02a0fb7c02f0bd70 System.Byte[]
02a0fb8c02f0bd70 System.Byte[]
02a0fc58 06eb281cSystem.RuntimeType
02a0fcb0 02efd4b0 Review
4)打印出原先的異常,查看輸出的信息。
!pe <original exception address>
0:020> !pe06f1e5e4
Exception object:06f1e5e4
Exception type: System.NullReferenceException
Message: Object reference not set to an instance of an object.
InnerException:
StackTrace (generated):
SP IP Function
02A0F950 0FE90F24 App_Code_wbwztx_4!Review.Finalize()+0x14
StackTraceString:
HResult: 80004003
Q:在什麼函數中異常被拋出?
A:在Review類中的finalizer/destructor 函數中。
Q:什麼對象正在被結束?注意:你可能實際經過二次拋出的異常已經獲得了信息,但經過 !pe original address 能獲得更清晰的信息。
A:Review對象。
Q:正常狀況下,ASP.NET 拋出的異常會被全局(global)的異常處理或由錯誤頁面來顯示給用戶,但爲何這裏會發生呢?爲何會致使崩潰呢?
A:asp.net 的全局異常處理函數僅僅會處理在處理一個請求過程出現的異常。在這個地方,異常發生在finalizer中,意思是:沒有用戶看到這個異常,所以信息不能發送給任何人。
在1.1 或者在2.0 中使用繼承原先的1.1 的方式,在finalizer中的異常不是致命的。它不會致使進程崩潰,而是finalizer的行爲會中止,這個是很是危險的,由於它意味着你沒有處理的代碼來釋放本地(原生,非託管)的資源,於是你會之內存泄露的方式來結束一段程序。這個是很是難找到的錯誤,數據庫鏈接等資源也會泄露。全部在finalizer線程中沒有處理的異常都必須認真研究和認真對待。
檢查代碼,確認狀況
1)打開Review.cs ,找到 銷燬/終結(destructor/finalizer)的代碼:
~Review()
{
if (quote.ToString() != string.Empty)
{
quote = null;
}
}
Q:哪一行的代碼或方法致使了異常?
A:在這個例子中代碼很簡單,也很容易準確的找到問題,quote.ToString()的那個判斷若是是if quote = null 則發生異常。