我们有一个使用async / await模式的复杂ASP.Net Core应用程序.应用程序最近停止响应,我们为它进行了内存转储.我们怀疑有一些异步操作会导致应用程序卡住,但不确定是哪一个.在为Web应用程序进行内存转储后,我们可以看到很少的运行线程,因为由于使用了async / await,线程被返回到线程池.
问题是,是否可以在内存转储中列出正在运行的任务,以及它们运行的位置,以便我可以判断哪个异步操作会使应用程序卡住?对于同步阻塞调用,它很容易 – 只列出所有活动线程的调用堆栈.但是对于异步操作,它不再起作用了. (添加更多跟踪是一种可能的方法,但情况是我们无法保证我们在应用程序及其依赖库中的每个异步操作都有足够的跟踪.)
问题是,是否可以在内存转储中列出正在运行的任务,以及它们运行的位置,以便我可以判断哪个异步操作会使应用程序卡住?对于同步阻塞调用,它很容易 – 只列出所有活动线程的调用堆栈.但是对于异步操作,它不再起作用了. (添加更多跟踪是一种可能的方法,但情况是我们无法保证我们在应用程序及其依赖库中的每个异步操作都有足够的跟踪.)
例如,如果ASP.Net Core应用程序卡在某些代码中,如何从内存转储中告诉它?
public async Task SomeBadMethodInADependentLibrary() { TaskCompletionSource<int> tcs = new TaskCompletionSource<int>(); await tcs.Task; }
解决方法
您当然可以在堆中找到任务对象,并使用SOS命令手动开始分析它们,例如:像这个调试会话的开始:
0:013> !dumpheap -stat -type Task Statistics: MT Count TotalSize Class Name [...] 71e03f28 4 160 System.Threading.Tasks.Task Total 28 objects 0:013> !dumpheap -mt 71e03f28 Address MT Size 022bd900 71e03f28 40 [...] 0:013> !do 022bd900 Name: System.Threading.Tasks.Task MethodTable: 71e03f28 EEClass: 719cd6e0 Size: 40(0x28) bytes File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll Fields: MT Field Offset Type VT Attr Value Name 71df1638 40019cd 1c System.Int32 1 instance 3 m_taskId 71defb44 40019ce 4 System.Object 0 instance 022bd8e0 m_action [...] 0:013> !DumpObj 022bd8e0 Name: System.Action MethodTable: 71e0588c EEClass: 719357b8 Size: 32(0x20) bytes File: C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll Fields: MT Field Offset Type VT Attr Value Name 71defb44 40002b5 4 System.Object 0 instance 022bd8e0 _target 71defb44 40002b6 8 System.Object 0 instance 00000000 _methodBase 71df2bdc 40002b7 c System.IntPtr 1 instance 4b00e64 _methodPtr 71df2bdc 40002b8 10 System.IntPtr 1 instance 4e0c30 _methodPtrAux [...] 0:013> !u 4e0c30 Unmanaged code 004e0c30 e833df8372 call clr!PrecodeFixupThunk (72d1eb68) [...]
现在它开始变得麻烦……
从我的观点来看,最方便的方法(在WinDbg中)是使用Mex (Github)的!TaskTriage命令:
0:013> !TaskTriage Normal Mode - not showing successful Tasks Address Target Status Method Exceptions ================================================================================================== 022bd900 | 022bd8e0 | TASK_STATE_DELEGATE_INVOKED | Demo.Program.printMessage() | <none> 022bd974 | 022bd868 | TASK_STATE_DELEGATE_INVOKED | Demo.Program+<>c.<Main>b__0_0() | <none> 022bd9bc | 022bd868 | TASK_STATE_STARTED | Demo.Program+<>c.<Main>b__0_1() | <none> 022bda04 | 022bd868 | TASK_STATE_STARTED | Demo.Program+<>c.<Main>b__0_2() | <none> ================================================================================================== Address Target Status Method Exceptions
在Visual Studio上使用WinDbg的想法很好,因为VS2015和VS2017都无法从转储文件中提供相同的结果: