记一次 .NET WPF布草管理系统 挂死分析
一:背景
1. 讲故事
这几天看的 dump 有点多,有点伤神伤脑,晚上做梦都是dump,今天早上头晕晕的到公司就听到背后同事抱怨他负责的WPF程序挂死了,然后测试的小姑娘也跟着抱怨。。。嗨,也不知道是哪一个迭代改出来的问题,反正客户不起义问题都不大。
不过我听到程序无响应,内心深处真的是一拘灵。。。本能反应吧,给他发了一个 procdump 过去生成两个 dump 发过来。
话说回来,WPF这种带UI界面的挂死问题其实很好分析的,无非就是 UI线程 失去响应了,至于为啥失去响应了,肯定是做了什么见不得光的事情,比如耍小聪明用 Task.Result,还有一点要特别注意的是 UI 独有的 SynchronizationContext,如 Winform 的 : WindowsFormsSynchronizationContext ,WPF 的 DispatcherSynchronizationContext,后面我准备开一篇文章用 Windbg 深入剖析一下这个死锁形成的原因,好,说了这么多,dump 也到了,上 Windbg 分析吧。
二: windbg 分析
1. 审查UI线程
做法很简单,先通过 ~0s 切到0号,也就是UI线程,再通过 !dumpstack 调出UI线程的托管和非托管栈,为了保护隐私,我就稍微精简下。
0:000> ~0s
eax=00000000 ebx=01855bf8 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=776a171c esp=014fe3b8 ebp=014fe410 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
ntdll!NtWaitForSingleObject+0xc:
776a171c c20c00 ret 0Ch
0:000> !dumpstack
OS Thread Id: 0x4ee0 (0)
Current frame: ntdll!NtWaitForSingleObject+0xc
ChildEBP RetAddr Caller, Callee
014fe3b4 7468a9c5 mswsock!SockWaitForSingleObject+0x125, calling ntdll!NtWaitForSingleObject
014fe410 7469932c mswsock!SockDoConnectReal+0x36b, calling mswsock!SockWaitForSingleObject
014fe4b4 74698df7 mswsock!SockDoConnect+0x482, calling mswsock!SockDoConnectReal
014fe544 74699861 mswsock!WSPConnect+0x61, calling mswsock!SockDoConnect
014fe564 77316cf7 ws2_32!WSAConnect+0x77
014fe5a0 6422aeea (MethodDesc 64088970 +0x5a DomainBoundILStubClass.IL_STUB_PInvoke(IntPtr, Byte[], Int32, IntPtr, IntPtr, IntPtr, IntPtr))
014fe5d4 6422aeea (MethodDesc 64088970 +0x5a DomainBoundILStubClass.IL_STUB_PInvoke(IntPtr, Byte[], Int32, IntPtr, IntPtr, IntPtr, IntPtr))
014fe5f4 641c72eb (MethodDesc 63ff4310 +0x4b System.Net.Sockets.Socket.DoConnect(System.Net.EndPoint, System.Net.SocketAddress)), calling 1d4d538c
014fe628 642160c5 (MethodDesc 640847c4 +0x7d System.Net.Sockets.Socket.Connect(System.Net.EndPoint)), calling (MethodDesc 63ff4310 +0 System.Net.Sockets.Socket.DoConnect(System.Net.EndPoint, System.Net.SocketAddress))
014fe644 1d4d5bd3 (MethodDesc 1c93d404 +0x33 xxx.SocketHelper.xxxSocket.Connect(System.Net.IPEndPoint)), calling (MethodDesc 640847c4 +0 System.Net.Sockets.Socket.Connect(System.Net.EndPoint))
014fe660 1d4d5834 (MethodDesc 1c01df50 +0x114 xxx.MainWindow.Connect()), calling (MethodDesc 1c93d404 +0 xxx.Utilities.SocketHelper.xxxSocket.Connect(System.Net.IPEndPoint))
014fe714 1d4d8d84 (MethodDesc 1c01e094 +0x9c xxx.MainWindow.<IniTimer>b__18_0(System.Object, System.EventArgs)), calling (MethodDesc 1c01df50 +0 xxx.MainWindow.Connect())
从上面的调用堆栈可以看出,MainWindow 中做了一个 Socket.Connect 连接,最后卡死在非托管的 mswsock!SockDoConnectReal方法上,应该是 Socket 连不上造成的,既然是 Socket ,把它的 ip 和 port 拿出来 telnet 一下不就好啦,对吧,可以用 !dso 把当前线程栈中所有的托管对象找出来。
0:000> !dso
OS Thread Id: 0x4ee0 (0)
ESP/REG Object Name
014FE4D8 03a47588 System.Net.SafeCloseSocket+InnerSafeCloseSocket
014FE598 03a476bc System.Net.EndpointPermission
014FE5E4 03a4762c System.Byte[]
014FF068 03681374 System.AppDomain
014FF4D8 03681238 System.SharedStatics
014FE6B4 036a4dfc System.String 9901
014FE6C4 036a4ba0 System.String 192.168.1.79
哈哈,从最后两行可以看出,socket 地址就是:192.168.1.79:9901, telnet 一下果然不通,问了下,原来是测试机最近重启了, Socket 服务端并没有随机器启动,貌似问题就这样找到了。。。
是不是觉得有哪里不对劲呢? 对, 就是为啥要在主线程做 Connect 呢? 万一 Socket 连不上,这不就是把自己陷入不仁不义的地步嘛,问了下实施,说WPF和SocketServer都是一同部署的,据说在现场也偶尔遇到,可能坑踩多了他们自己也摸索出来了,把 SockerServer 重启一下就搞定了,不过这次可能研发自己都看不下去了吧 , 真是自曝家丑。。。
2. 查看问题代码
问题还是要解决的,先把问题代码导出来,用 !name2ee + !savemodule 即可。
0:000> !name2ee *!xxx.MainWindow.Connect
Module: 01754044
Assembly: xxx.exe
Token: 06000af5
MethodDesc: 1c01df50
Name: xxx.MainWindow.Connect()
JITTED Code Address: 1d4d5720
0:000> !savemodule 01754044 E:\dumps\3.dll
3 sections in file
section 0 - VA=2000, VASize=3835b4, FileAddr=200, FileSize=383600
section 1 - VA=386000, VASize=3520, FileAddr=383800, FileSize=3600
section 2 - VA=38a000, VASize=c, FileAddr=386e00, FileSize=200
然后用 ILSpy 打开 3.dll ,查看精简后的代码如下:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Connect();
}
private bool Connect()
{
string ipString = ConfigurationManager.AppSettings["ServerSocketIp"];
IPAddress address = IPAddress.Parse(ipString);
IPEndPoint iPEndPoint = new IPEndPoint(address, Convert.ToInt32(ConfigurationManager.AppSettings["ServerPort"]));
sockClient = (xxxSocket)(object)new xxxSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
sockClient.Connect(iPEndPoint);
((Socket)(object)sockClient).IOControl(IOControlCode.KeepAliveValues, KeepAlive(1, 1000, 1000), (byte[])null);
sockClient.add_RecievedMessage((EventHandler<SocketMessage>)sockClient_RecievedMessage);
}
catch (SocketException ex)
{
return false;
}
return true;
}
很清楚的看到在主线程做了 Connect 操作,这是大忌哈。。。 可能这段 Socket 代码也是网上找的,应该也没注意太多吧。。。
三:总结
知道前因后果之后,优化办法就比较简单了。
- 把 Connect 丢到 Task.Run 中,释放主线程,简单粗暴,
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
Task.Run(()=> { Connect() });
}
- 使用 async, await
在这个 1+1 都要使用异步写法的时代,不用它真的感觉落伍了。。。这里我就不费脑子怎么用 XXXAsync 家族了哈。
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
string address = "192.168.1.79";
int port = 9901;
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.SendTimeout = 1000 * 10;
socket.ReceiveTimeout = 1000 * 10;
await socket.ConnectAsync(address, port);
//....
}
这个真实案例很简单,难度等级0, 不知道您学会了吗? 其实有时也感叹一下,像这种案例会 Windbg 3分钟解决,不会要摸头一上午。
更多高质量干货:参见我的 GitHub: dotnetfly

记一次 .NET WPF布草管理系统 挂死分析的更多相关文章
- 记一次 .NET 某纺织工厂 MES系统 API 挂死分析
一:背景 1. 讲故事 这个月中旬,有位朋友加我wx求助他的程序线程占有率很高,寻求如何解决,截图如下: 说实话,和不同行业的程序员聊天还是蛮有意思的,广交朋友,也能扩大自己的圈子,朋友说他因为这个b ...
- 记一次 .NET医疗布草API程序 内存暴涨分析
一:背景 1. 讲故事 我在年前写过一篇关于CPU爆高的分析文章 再记一次 应用服务器 CPU 暴高事故分析 ,当时是给同济做项目升级,看过那篇文章的朋友应该知道,最后的结论是运维人员错误的将 IIS ...
- 记一次 .NET 某RFID标签管理系统 CPU 暴涨分析
一:背景 1. 讲故事 前段时间有位朋友说他的程序 CPU 出现了暴涨现象,由于程序是买来的,所以问题就比较棘手了,那既然找到我,就想办法帮朋友找出来吧,分析下来,问题比较经典,有必要和大家做一下分享 ...
- 记一次 .NET 某云采购平台API 挂死分析
一:背景 1. 讲故事 大概有两个月没写博客了,关注我的朋友应该知道我最近都把精力花在了星球,这两个月时间也陆陆续续的有朋友求助如何分析dump,有些朋友太客气了,给了大大的红包,哈哈,手里面也攒了1 ...
- 记一次 .NET 某新能源汽车锂电池检测程序 UI挂死分析
更多高质量干货:参见我的 GitHub: dotnetfly 一:背景 1. 讲故事 这世间事说来也奇怪,近两个月有三位朋友找到我,让我帮忙分析下他的程序hangon现象,这三个dump分别涉及: 医 ...
- 日志分析工具、日志管理系统、syslog分析
日志分析工具.日志管理系统.syslog分析 系统日志(Syslog)管理是几乎所有企业的重要需求.系统管理员将syslog看作是解决网络上系统日志支持的系统和设备性能问题的关键资源.人们往往低估了对 ...
- Eventlog Analyzer日志管理系统、日志分析工具、日志服务器的功能及作用
Eventlog Analyzer日志管理系统.日志分析工具.日志服务器的功能及作用 Eventlog Analyzer是用来分析和审计系统及事件日志的管理软件,能够对全网范围内的主机.服务器.网络设 ...
- 记一次 .NET 某风控管理系统 内存泄漏分析
一:背景 1. 讲故事 上个月中旬,星球里的一位朋友在微信找我,说他的程序跑着跑着内存会不断的缓慢增长并无法释放,寻求如何解决 ? 得,看样子星球还得好好弄!!! 不管怎么说,先上 windbg 说话 ...
- 记一次 .NET 某药品仓储管理系统 卡死分析
一:背景 1. 讲故事 这个月初,有位朋友wx上找到我,说他的api过一段时间后,就会出现只有请求,没有响应的情况,截图如下: 从朋友的描述中看样子程序是被什么东西卡住了,这种卡死的问题解决起来相对简 ...
随机推荐
- 学习String源码的部分方法
先看构造器: private final char value[]; //char类型的数组 以下均会用到 private int hash; //缓存字符串的哈希值 //以下均会用到 public ...
- git相关问题
1.git查看远程分支更新到本地 git clone 项目地址,示例如下: git clone https://github.com/zhongyushi-git/vue-test.git 在拉取时, ...
- cartographer 调参(1)-lua文件配置参考文档
cartographer 调参(1)-lua文件配置参考文档 https://blog.csdn.net/SimileciWH/article/details/84861718 Lua configu ...
- 基于μcOS-II实时操作系统源码实现RMS和EDF调度(共享资源)
μcOS-II多任务实验报告(RMS.EDF调度) 目录 μcOS-II多任务实验报告(RMS.EDF调度) 一.实验概述 二.环境搭建 三.代码分析 四.实验步骤 1 给TCB块添加扩展 2 创建并 ...
- SpringMVC异步处理的 5 种方式
作者:丁仪 来源:https://chengxuzhixin.com/blog/post/SpringMVC-yi-bu-chu-li-de-5-zhong-fang-shi.html 前段时间研究了 ...
- tcpdump后台抓包并输出给wireshark
# 先进到/tmp 目录执行,方便Filezila 传输 # 开启抓包 nohup tcpdump -i eth0 -s0 -nnA 'port 22' -w dump22.pcap & [1 ...
- Kafka SASL ACL配置踩坑总结
源起:工程现阶段中间件采用的是kafka.满足了大数据的高吞吐,项目间的解耦合,也增强了工程的容错率与扩展性.但是在安全这一块还有漏洞,kafka集群中,只要网站内的任何人知道kafka集群的ip与t ...
- Java安全初学之反射
前言: 复现fastjson的时候深深意识到了需要好好学习一下Java和Java安全,激情的学习了一番java安全中重要的几部分:反序列化.反射.rmi.动态代理,从反射开始做个总结. 反射:java ...
- go中errgroup源码解读
errgroup 前言 如何使用 实现原理 WithContext Go Wait 错误的使用 总结 errgroup 前言 来看下errgroup的实现 如何使用 func main() { var ...
- 关于windows下服务一直处于启动ing的处理办法
1,找到服务名称,xxxx 进入cmd 2,sc queryex xxxx,找到pid 3,taskkill /f /pid 123 就可以终止这个启动中的进程了