昨天我遇到了一个非常奇怪的错误,一天后我几乎没有任何进展,所以我想这是一个很好的向社区询问的候选人。我会请一些病人,因为我认为这是一个棘手的问题。
我有一个C#Winforms应用程序,在生产过程中点击几下就会挂起。这种情况在开发环境中永远不会发生,只有在生产环境中才会发生。当挂起发生时,什么都没有发生(没有错误消息,但根据任务管理器,任务进入“无响应”状态),但GUI变得不负责任。我在同一个环境中尝试过,我可以确认这种行为。
不幸的是,无法在prod-env中安装开发工具和调试应用程序。我能做的最好的事情就是在应用程序停止时从中转储内存。问题是我完全不明白我在崩溃转储中看到了什么:我的主线程(GUI线程)似乎被一条指令卡住了,我找不到任何原因。
这是我的主线程的堆栈跟踪:
KERNELBASE.dll!_RaiseException@16() + 0x54 bytes
[External Code]
CFAPControlLibrary.dll!CFAPControlLibrary.Communication.Base.GetSetting(string settingName) Line 850 + 0x10 bytes C#
CFAPControlLibrary.dll!CFAPControlLibrary.ConfigHelper.Get<CFAPControlLibrary.DataTypes.ActionSortingOption>(string settingName) Line 25 + 0x35 bytes C#
CFAPControlLibrary.dll!CFAPControlLibrary.ConfigHelper.Get<CFAPControlLibrary.DataTypes.ActionSortingOption>(string settingName, CFAPControlLibrary.DataTypes.ActionSortingOption defaultVal) Line 15 + 0x9 bytes C# CFAPControlLibrary.dll!CFAPControlLibrary.DataTypes.ActionStorage.Sort(System.Collections.Generic.List<CFAPControlLibrary.DataTypes.ActionClass> subject) Line 167 + 0xe bytes C#
CFAPControlLibrary.dll!CFAPControlLibrary.DataTypes.ActionStorage.GetByStatus(string pStatus) Line 162 + 0x46 bytes C#
CFAPControlLibrary.dll!CFAPControlLibrary.ActionSelector.FillNodes() Line 48 + 0x26 bytes C#
CFAPControlLibrary.dll!CFAPControlLibrary.CFAPMain.OnActionDetailsArrived(CFAPControlLibrary.CFAPMain.RawActionDetails bwr) Line 371 + 0x10 bytes C#
CFAPControlLibrary.dll!CFAPControlLibrary.CFAPMain.OnGetDetailsCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) Line 337 + 0xb bytes C#
user32.dll!_InternalCallWinProc@20() + 0x23 bytes
user32.dll!_UserCallWinProcCheckWow@32() + 0xb3 bytes
user32.dll!_DispatchMessageWorker@8() + 0xe6 bytes
user32.dll!_DispatchMessageW@4() + 0xf bytes
[External Code]
CFAPHost.exe!CFAPHost.Program.Main(string[] args) Line 50 + 0x1d bytes C#
[External Code]
mscoreei.dll!__CorExeMain@0() + 0x38 bytes
mscoree.dll!_ShellShim__CorExeMain@0() + 0x227 bytes
mscoree.dll!__CorExeMain_Exported@0() + 0x8 bytes
kernel32.dll!@BaseThreadInitThunk@12() + 0x12 bytes
ntdll.dll!___RtlUserThreadStart@8() + 0x27 bytes
ntdll.dll!__RtlUserThreadStart@8() + 0x1b bytes
下面是我从顶部堆栈框架中获得的源代码:
从KernelBase.dll进行的反汇编:
与我代码中的最后一帧相比,m_SettingCache是一个Dictionary,它不包含请求的密钥:
接下来的几帧:
我认为代码非常简单——它只是带有默认值的通用设置。如果出现问题(设置名称未定义或无法转换),将返回默认值。这个代码确实有效。我从转储中看到的是,从字典中读取的内容永远不会返回,尽管它应该抛出KeyNotFoundException,但这种情况从未发生过。有什么建议吗?
注意:主线程确实在转储捕获的状态下停止:每次进行转储时,结果都是一样的。
注意2:挂起从来不会发生在这个代码路径的第一次执行时,在每个场景中,这个完全相同的代码路径都是在挂起之前执行的(从应用程序日志推断)
我将根据要求提供更多细节。
提前谢谢。
编辑:
CFAPControlLibrary.dll是应用程序的主程序集。它包含窗口窗体及其相应的逻辑。与服务器的通信是通过WCF实现的。更大的请求是在使用BackgroundWorker的并行线程中发出的。您在调用堆栈中看到的执行路径是由这样一个BackgroundWorker的completion事件调用的。
我粘贴了请求的代码位
here
我的AppDomain.CurrentDomain.UnhandledException处理程序为
here
堆栈中我首先认为不重要但后来被证明很重要的部分(从图像中删除敏感字符串文字):
这表明Application.Run被调用了,我不知道为什么它没有显示在调用堆栈中。
使现代化
在花了三天时间没有找到问题的原因后,我决定尝试一种变通方法。由于内存转储显示应用程序总是挂在同一点:当KeyNotFound异常应该被抛出时。最简单的解决方法是重构代码,尽可能不抛出。那个版本通过了测试,从未挂起。
这根本不是一个解决方案,但我们不能再在这上面花时间了。所以基本上我会交叉手指发送代码,希望我再也不会看到这种崩溃。
谢谢你的所有建议