使用 Windbg 分析一个 异步操作 引发的 Crash 异常
上周我們收到了一個客戶的緊急求助,他們的一個 iis應用程序池 經歷了頻繁重啟,即使從錯誤日志中也不得到任何有用的信息,異常信息如下:
System.NullReferenceException?:?Object?reference?not?set?to?an?instance?of?an?object. System.Web.ThreadContext.AssociateWithCurrentThread(Boolean)System.Web.HttpApplication.OnThreadEnterPrivate(Boolean)System.Web.LegacyAspNetSynchronizationContext.CallCallbackPossiblyUnderLock(System.Threading.SendOrPostCallback,?System.Object)System.Web.LegacyAspNetSynchronizationContext.CallCallback(System.Threading.SendOrPostCallback,?System.Object)System.Threading.Tasks.AwaitTaskContinuation.RunCallback(System.Threading.ContextCallback,?System.Object,?System.Threading.Tasks.Task?ByRef)System.Threading.Tasks.AwaitTaskContinuation+<>c.b__18_0(System.Object)System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,?System.Threading.ContextCallback,?System.Object,?Boolean)System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext,?System.Threading.ContextCallback,?System.Object,?Boolean)System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()System.Threading.ThreadPoolWorkQueue.Dispatch()這種異常會殺掉進程,通過 google 搜索會發現這是 .NET 4.5 異步操作下的某種經典異常,大概就是說沒有在合適的地方等待 Task, 參考:https://stackoverflow.com/questions/18759987/async-code-without-waiting-for-completion ?,參考如下代碼:
var?getDataTask?=?getData();if(some_condition?==?true)?{return?some_object; }getDataTask.Wait();return?getDataTask.result;如果代碼無意走了 if(some_condition == true) 路徑并沒有 Wait 后續的 getDataTask 就會拋出這種異常,現在的問題是堆棧信息太少,并不能找到異常的源頭在哪里?
使用 windbg 分析
如果你有 crash 的 dump,用windbg就能挖到很多有價值的信息。
File → Open Crash Dump
首先使用 windbg 打開 crash 文件,如下圖:
從圖中可以看到,windbg 已經告知我們當前有一個 CLR 異常。
Symbols
雖然沒有客戶的 debug 符號,這里使用微軟的符號服務器, srv*C:\symbols*http://msdl.microsoft.com/download/symbols; 如下圖:
加載 SOS 擴展
在 windbg 命令窗口中輸入 ?.loadby sos clr 。
異常分析命令
使用 windbg 自帶的異常分析自動化命令 !analyze -v ,然后你會得到很多的 異常信息, 參見下圖:
上面的圖中會告訴我們出問題的線程,接下來我們怎么看是誰創建了 Task,它的源頭在哪里?可以用 !dso 查看這個問題線程的所有棧對象。
0:069>?!dumpstackobjects OS?Thread?Id:?0x2d20?(69) RSP/REG??????????Object???????????Name r15??????????????0000002031518d78?System.Threading.Thread 00000022C6EBE680?0000001db1a917a8?System.NullReferenceException 00000022C6EBE6A0?0000001db1a917a8?System.NullReferenceException 00000022C6EBE6F8?0000001db1a917a8?System.NullReferenceException 00000022C6EBE740?0000001db1a91c40?System.Threading.QueueUserWorkItemCallback 00000022C6EBE758?0000001db1a91ad0?System.Runtime.ExceptionServices.ExceptionDispatchInfo 00000022C6EBE760?0000001db1a917a8?System.NullReferenceException 00000022C6EBE780?0000002031518d78?System.Threading.Thread 00000022C6EBE788?0000001db1a91c40?System.Threading.QueueUserWorkItemCallback 00000022C6EBE790?0000001db1a917a8?System.NullReferenceException 00000022C6EBE7A8?0000001df1b95078?System.SByte[] 00000022C6EBE7B0?0000001df1392cc0?System.Threading.ContextCallback 00000022C6EBE7C0?0000001db1a917a8?System.NullReferenceException 00000022C6EBE880?0000001db1a91ad0?System.Runtime.ExceptionServices.ExceptionDispatchInfo 00000022C6EBE888?0000001db1a917a8?System.NullReferenceException 00000022C6EBE8A8?0000001db1a91c40?System.Threading.QueueUserWorkItemCallback 00000022C6EBE8B0?0000002031518d78?System.Threading.Thread 00000022C6EBE910?0000001df1392cc0?System.Threading.ContextCallback 00000022C6EBE920?0000001db1a917a8?System.NullReferenceException 00000022C6EBE928?0000001db1a91ad0?System.Runtime.ExceptionServices.ExceptionDispatchInfo 00000022C6EBE938?0000001db1a91cc8?System.Threading.ExecutionContext 00000022C6EBE940?0000001db1a91cc8?System.Threading.ExecutionContext 00000022C6EBE950?0000001df1392cc0?System.Threading.ContextCallback 00000022C6EBE990?0000002031518d78?System.Threading.Thread 00000022C6EBE9F0?0000001db1a91c40?System.Threading.QueueUserWorkItemCallback 00000022C6EBE9F8?0000001db1a91cc8?System.Threading.ExecutionContext 00000022C6EBEA60?0000001df1392c98?System.Threading.ThreadPoolWorkQueue 00000022C6EBEAB8?0000001fb114f2e8?System.Threading.TimerQueue 00000022C6EBEAD8?00000020314c0ae8?System.Threading.ThreadPoolWorkQueueThreadLocals 00000022C6EBEAE0?0000001df1392c98?System.Threading.ThreadPoolWorkQueue 00000022C6EBEB00?0000001db1a91c40?System.Threading.QueueUserWorkItemCallback 00000022C6EBEE18?0000001df19f8320?System.Web.Hosting.IIS7WorkerRequest這里有很多我們感興趣的對象,那源頭自然要深挖棧底對象 System.Web.Hosting.IIS7WorkerRequest ,使用 !do xxx 命令。
然后再挖里面的 _path 和 _httpVerb 字段,繼續使用 !do。
從圖中可以看到,線程的源頭是從 POST /Account/Login 請求開始的。
總結
當把調查結果反饋給客戶,幾分鐘后我收到了一封郵件,他們已經定位并解決了這個問題,據說是因為調用了第三方的 dll ,但這個 dll 并沒有合理的實現 wait 所致。
總結
以上是生活随笔為你收集整理的使用 Windbg 分析一个 异步操作 引发的 Crash 异常的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 10 新特性 —— CallerA
- 下一篇: C# 10 新特性 —— Lambda