记一次 .NET某机械臂上位系统 卡死分析

一:背景

1. 讲故事

前些天有位朋友找到我,说他们的程序会偶发性的卡死一段时间,然后又好了,让我帮忙看下怎么回事?窗体类的程序解决起来相对来说比较简单,让朋友用procdump自动抓一个卡死时的dump,拿到dump之后,上 windbg 说话。

二:WinDbg 分析

1. 主线程在做什么

要想看主线程在做什么,很显然用 k 命令观察非托管栈即可。


0:000> k# Child-SP          RetAddr               Call Site
00 000000ef`11d1cb70 00007ffc`e65ddc4a     ntdll!RtlSetLastWin32Error+0x38
01 000000ef`11d1cbc0 00007ffc`e660e1a4     clr!JIT_RareDisableHelperWorker+0xca
02 000000ef`11d1cd00 00007ffc`b5c4ea25     clr!JIT_RareDisableHelper+0x14
03 000000ef`11d1cd40 00007ffc`b5c41d35     System_Drawing_ni+0x6ea25
04 000000ef`11d1ce00 00007ffc`87948876     System_Drawing_ni!System.Drawing.StringFormat..ctor+0x15
....
10 000000ef`11d1d8b0 00007ffc`881fc86f     xxx!xxx.AutoResizeColumns+0x106
...

从卦中数据看,托管的栈顶上有一个 RtlSetLastWin32Error 函数,看样子 JIT_RareDisableHelperWorker 方法中某一个函数返回错误码了,那这个错误码是多少呢?要知道这个答案,先要知道它的签名是什么样的,参考链接: https://source.winehq.org/WineAPI/RtlSetLastWin32Error.html


void RtlSetLastWin32Error
(DWORD err
)

从签名可以看到,这个 err 是一个 int ,接下来观察 RtlSetLastWin32Error 方法的 rcx 寄存器,有没有存到 线程栈上,如果有的话直接提取即可。


0:000> uf ntdll!RtlSetLastWin32Error
ntdll!RtlSetLastWin32Error:
00007ffd`01a00780 894c2408        mov     dword ptr [rsp+8],ecx
00007ffd`01a00784 4883ec48        sub     rsp,48h
00007ffd`01a00788 488b05813d1300  mov     rax,qword ptr [ntdll!_security_cookie (00007ffd`01b34510)]
00007ffd`01a0078f 4833c4          xor     rax,rsp
....0:000> k# Child-SP          RetAddr               Call Site
00 000000ef`11d1cb70 00007ffc`e65ddc4a     ntdll!RtlSetLastWin32Error+0x38
01 000000ef`11d1cbc0 00007ffc`e660e1a4     clr!JIT_RareDisableHelperWorker+0xca0:000> dd 000000ef`11d1cbc0 L1
000000ef`11d1cbc0  00000006

从卦中看这个 err=6 ,那这个错误码是什么意思呢?继续查 MSDN: https://learn.microsoft.com/zh-cn/windows/win32/debug/system-error-codes–0-499-

从卦中可以清晰的看到,原来是 无效的句柄 导致的,那这个错误会导致程序的卡死吗?

2. 无效的句柄会卡程序吗

按照我的过往经验没有这么一说,其实 win32api 不像编程语言直接用 try catch 与 SEH 集成,返回错误码的这种方式编码起来虽然麻烦,但性能是最高的,所以这玩意导致程序卡死基本上是不可能的,那接下来的分析方向在哪里呢? 其实在这种场景下抓多dump就尤为重要了,毕竟多个dump之间可以相互参考来观察程序的走势,目前是没有这个条件的,那就从其他的路子上探究吧。

接下来我们试探性的观察所有的托管线程栈,看看他们此时都在做什么。


0:000> ~*e !clrstack
OS Thread Id: 0x555c (98)Child SP               IP Call Site
000000ef180fd0b8 00007ffd01a4dc04 [HelperMethodFrame_1OBJ: 000000ef180fd0b8] System.Threading.WaitHandle.WaitOneNative(System.Runtime.InteropServices.SafeHandle, UInt32, Boolean, Boolean)
000000ef180fd1e0 00007ffce4e2ddfc System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean)
000000ef180fd210 00007ffce4e2ddcf System.Threading.WaitHandle.WaitOne(Int32, Boolean)
000000ef180fd250 00007ffcb5573d74 System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle)
000000ef180fd2c0 00007ffcb4dc0d54 System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control, System.Delegate, System.Object[], Boolean)
000000ef180fd400 00007ffcb5577674 System.Windows.Forms.Control.Invoke(System.Delegate, System.Object[])
000000ef180fd470 00007ffc882040d4 xxxx.backgroundWorker_ProgressChanged(System.Object, System.ComponentModel.ProgressChangedEventArgs)
000000ef180fe060 00007ffce4e1ae56 System.Threading.ThreadPoolWorkQueue.Dispatch() OS Thread Id: 0x4528 (101)Child SP               IP Call Site
000000ef183fe1d8 00007ffd01a4dc04 [HelperMethodFrame_1OBJ: 000000ef183fe1d8] System.Threading.WaitHandle.WaitOneNative(System.Runtime.InteropServices.SafeHandle, UInt32, Boolean, Boolean)
000000ef183fe300 00007ffce4e2ddfc System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean)
000000ef183fe330 00007ffce4e2ddcf System.Threading.WaitHandle.WaitOne(Int32, Boolean)
000000ef183fe370 00007ffcb5573d74 System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle)
000000ef183fe3e0 00007ffcb4dc0d54 System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control, System.Delegate, System.Object[], Boolean)
000000ef183fe520 00007ffcb5577674 System.Windows.Forms.Control.Invoke(System.Delegate, System.Object[])
000000ef183fe590 00007ffc882040d4 xxxx.backgroundWorker_ProgressChanged(System.Object, System.ComponentModel.
000000ef183ff180 00007ffce4e1ae56 System.Threading.ThreadPoolWorkQueue.Dispatch() 
000000ef183ff608 00007ffce66112c3 [DebuggerU2MCatchHandlerFrame: 000000ef183ff608] 
...

观察上面的线程栈之后,发现有两个线程在 MarshaledInvoke 上等待,而且都是 backgroundWorker_ProgressChanged 方法,看样子有一个 backgroundWorker 控件在这里,其实这个信息还是值得警惕的,为什么这么说呢? 因为它往往会预示着这个 Control 的 Queue 队列可能有很多的数据积压,那就往这个方向走。

3. Queue 队列有积压吗

要找到这个答案,需要观察主线程的线程栈上是否有 Queue 队列。


0:000> !dso
OS Thread Id: 0x918 (0)
RSP/REG          Object           Name
r13              000002a70660a860 System.Drawing.StringFormat
...
000000EF11D1D9B0 000002a7008b5a18 System.ComponentModel.BackgroundWorker
...
000000EF11D1E328 000002a7004ea8b8 System.Collections.Queue
...
0:000> !do 000002a7004ea8b8
Name:        System.Collections.Queue
MethodTable: 00007ffce48cd9d0
EEClass:     00007ffce49f5fd0
Size:        56(0x38) bytes
File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:MT    Field   Offset                 Type VT     Attr            Value Name
00007ffce48d0ba0  40018c3        8      System.Object[]  0 instance 000002a7039d3278 _array
00007ffce48d32c0  40018c4       18         System.Int32  1 instance              103 _head
00007ffce48d32c0  40018c5       1c         System.Int32  1 instance               74 _tail
00007ffce48d32c0  40018c6       20         System.Int32  1 instance              227 _size
00007ffce48d32c0  40018c7       24         System.Int32  1 instance              200 _growFactor
00007ffce48d32c0  40018c8       28         System.Int32  1 instance             1366 _version
00007ffce48d0b08  40018c9       10        System.Object  0 instance 0000000000000000 _syncRoot

从卦中数据看,这个 BackgroundWorker.Queue 当前有 227 个任务在队列积压,这说明主线程是没有问题的,只不过是在忙碌的处理任务而已,再回答最后一个问题,为什么会卡一阵子?

4. 为什么会卡一阵子

这是朋友提到的一个疑问,要想找到这个问题的答案,我们再回头看下主线程,看下它是如何从 Queue 中取数据的。


0:000> !clrstack
OS Thread Id: 0x918 (0)Child SP               IP Call Site
000000ef11d1cd70 00007ffd01a007b8 [InlinedCallFrame: 000000ef11d1cd70] System.Drawing.SafeNativeMethods+Gdip.GdipCreateStringFormat(System.Drawing.StringFormatFlags, Int32, IntPtr ByRef)
...
000000ef11d1d970 00007ffc87911aac xxx.backgroundWorker_ProgressChanged(System.Object, System.ComponentModel.ProgressChangedEventArgs)
000000ef11d1d9e0 00007ffcdeab652b System.ComponentModel.BackgroundWorker.OnProgressChanged(System.ComponentModel.ProgressChangedEventArgs)
000000ef11d1dc30 00007ffce66112c3 [DebuggerU2MCatchHandlerFrame: 000000ef11d1dc30] 
000000ef11d1dea8 00007ffce66112c3 [HelperMethodFrame_PROTECTOBJ: 000000ef11d1dea8] System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean)
000000ef11d1e020 00007ffce4dfcf58 System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(System.Object, System.Object[], System.Object[])
000000ef11d1e080 00007ffce4dcbd20 System.Delegate.DynamicInvokeImpl(System.Object[])
000000ef11d1e0d0 00007ffcb4dc702d System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry)
000000ef11d1e110 00007ffcb4dc6f49 System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(System.Object)
000000ef11d1e160 00007ffce4ddfbe8 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000ef11d1e230 00007ffce4ddfad5 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000ef11d1e260 00007ffce4ddfaa5 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
000000ef11d1e2b0 00007ffcb4dc6ecc System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry)
000000ef11d1e300 00007ffcb4dc6c36 System.Windows.Forms.Control.InvokeMarshaledCallbacks()
000000ef11d1e370 00007ffcb4db06fb System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
000000ef11d1e430 00007ffcb4dafa72 System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)
000000ef11d1e4d0 00007ffcb553d682 DomainBoundILStubClass.IL_STUB_ReversePInvoke(Int64, Int32, Int64, Int64)
000000ef11d1e810 00007ffce660fc9e [InlinedCallFrame: 000000ef11d1e810] System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef)
...
000000ef11d1ea30 00007ffcb4dc5982 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
000000ef11d1ecd0 00007ffc86e308e0 xxx.Program.Main(System.String[])
000000ef11d1ef08 00007ffce66112c3 [GCFrame: 000000ef11d1ef08] 

从卦中看,线程栈上的 InvokeMarshaledCallback 方法就是取数据的函数,接下来用 ILSpy 反编译下这段代码,简化后如下:


private void InvokeMarshaledCallbacks()
{ThreadMethodEntry threadMethodEntry = null;lock(threadCallbackList){if (threadCallbackList.Count > 0){threadMethodEntry = (ThreadMethodEntry)threadCallbackList.Dequeue();}}while (threadMethodEntry != null){try{InvokeMarshaledCallback(threadMethodEntry);}catch (Exception ex){threadMethodEntry.exception = ex.GetBaseException();}lock(threadCallbackList){threadMethodEntry = ((threadCallbackList.Count <= 0) ? null : ((ThreadMethodEntry)threadCallbackList.Dequeue()));}}
}

从代码中的while true来看,这方法真的很轴,不懂得变通,要么不取,要么就是一次性的取完,当 Queue=threadCallbackList 中的数据较多时,主线程就会非常的忙碌,所以这就是卡死一阵子的真正底层原因。

有了前因后果之后,建议朋友做如下两点修改:

  1. Invoke 的逻辑尽量简单,让 UI 可以秒杀。
  2. Invoke 的逻辑是否可以批量化,来减少 Queue 的积压。

三:总结

这次朋友的生产事故,对我们做分析的人来说还是有很大的教训意义,有时候主线程的一些抛错或者阻塞假象会诱导我们陷入分析误区,这就需要调试人员具有一双慧眼识别,及时的浪子回头。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/29725.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

0. 云原生之基于乌班图远程开发

云原生专栏大纲 文章目录 安装乌班图配置静态IP重置root密码开启root远程登录开启远程SSH访问安装docker安装docker-compose安装Edge浏览器安装搜狗输入法安装TeamViewer安装虚拟显示器安装JDK安装maven安装vscodevscode插件安装VSCode配置maven、git、jdk、自动报错vscode快捷…

30v-180V降3.3V100mA恒压WT5107

30v-180V降3.3V100mA恒压WT5107 WT5107是一款恒压单片机供电芯片&#xff0c;它可以30V-180V直流电转换成稳定的3.3V直流电&#xff08;最大输出电流300mA&#xff09;&#xff0c;为各种单片机供电。WT5107的应用也非常广泛。它可以用于智能家居、LED照明、电子玩具等领域。比…

jupyter使用的一个奇怪bug——SyntaxError: invalid non-printable character U+00A0

bug来由&#xff1a;从其他部分例如kaggle里复制来的代码直接粘贴在jupyter notebook里&#xff0c;每一行代码都会出现&#xff1a; Cell In[5], line 1 warnings.filterwarnings(ignore) ^ SyntaxError: invalid non-printable character U00A0 单元格 In[5]&#xff0c;第 …

多网页登录Cookie免登通俗理解

背景&#xff0c;现在有A、B两个系统&#xff0c;其中B是乾坤框架的微前端&#xff0c;里面又有若干可以单独运行的系统C、D、E、F&#xff0c;现在的目标是&#xff0c;如果没有登录过其中任一系统&#xff0c;则需要跳转登录页登录&#xff0c;登录后&#xff0c;所有的A-F都…

Python 自动化测试入门有哪些内容?

自动化测试是软件测试领域中的一个重要技术&#xff0c;它利用脚本和工具来执行测试任务&#xff0c;减少了人工操作的工作量和时间消耗。Python 是一种功能强大且易于学习的编程语言&#xff0c;被广泛应用于自动化测试领域。本文将从0到1讲解如何使用 Python 进行自动化测试&…

网络编程(一)基本概念

文章目录 一、概念&#xff08;一&#xff09;网络发展阶段1. ARPAnet阶段2. TCP/IP两个协议阶段3. 网络体系结构和OSI开放系统互联模型4. TCP/IP协议簇体系结构&#xff08;1&#xff09; 应用层&#xff1a;&#xff08;2&#xff09;传输层&#xff1a;&#xff08;3&#x…

白名单在数据防篡改方面的优势与局限

白名单作为一种安全机制&#xff0c;其主要目的是通过预先设定受信任的程序、文件或用户列表&#xff0c;来限制系统资源的访问和执行权限。这种机制在防止未授权的程序或文件运行方面具有一定的效果&#xff0c;从而在一定程度上减少病毒入侵的机会。然而&#xff0c;当我们探…

读书笔记-《人人都是产品经理》

在开发工程师与产品经理的段子中&#xff0c;常看到“人人都是产品经理”这句话&#xff0c;用来调侃这个岗位似乎没有什么门槛。 很明显&#xff0c;这句话的出处&#xff0c;即本书作者想表达的是每个人都可以运用产品思维去解决问题。 01 产品 产品&#xff1a;用来解决某…

python中scrapy

安装环境 pip install scrapy 发现Twisted版本不匹配 卸载pip uninstall Twisted 安装 pip install Twisted22.10.0 新建scrapy项目 scrapy startproject 项目名 注意&#xff1a;项目名称不允许使用数字开头&#xff0c;也不能包含中文 eg: scrapy startproject scrapy_baidu_…

热重启(硬重启)获取Bitlocker密钥取证

计算机内存&#xff08;条&#xff09;在系统运行时存储了大量敏感信息&#xff0c;当断电后&#xff0c;内存中的数据荡然无存。反之&#xff0c;当一直通电的情况下&#xff0c;内存中的一些敏感数据一直存在。当然&#xff0c;正如某些人了解的&#xff0c;当断电后&#xf…

管道光电液位传感器应用在哪些设备

管道光电液位传感器在现代智能设备中扮演着至关重要的角色&#xff0c;特别是在需要检测水管缺水的应用场景中。与传统的浮球开关相比&#xff0c;这种传感器具有高可靠性、稳定性和不易卡死等优点。其先进的光学感应原理无需机械运动&#xff0c;确保了长久的寿命和稳定的性能…

《CGT行业监管及上市路径解析》——锦天城高级合伙人虞正春先生在线分享

细胞和基因治疗&#xff08;Cell and Gene Therapy, CGT&#xff09;已成为近年来全球药物研发的热点与前沿领域&#xff0c;也得到了国家政策的大力支持。《“十四五”生物经济发展规划》中提到“发展基因诊疗、干细胞治疗、免疫细胞等新技术”&#xff0c;“围绕加快创新药上…

Spring中事务的隔离级别和传播机制

上一篇博客中讲解了关于事务的两种使用方式包括Transactional的详解。 Transactional 注解当中的三个常⻅属性: 1. rollbackFor: 异常回滚属性. 指定能够触发事务回滚的异常类型. 可以指定多个异常类型 2. Isolation: 事务的隔离级别. 默认值为 Isolation.DEFAULT 3. propagat…

短视频矩阵系统:高效运营,解决多账号管理难题

前言 在当下短视频风靡的时代&#xff0c;如何高效管理和运营多个短视频账号&#xff0c;成为了众多运营者面临的挑战。而今&#xff0c;一款全新的短视频矩阵系统应运而生&#xff0c;它不仅融合了AI文案生成与剪辑模式等先进功能&#xff0c;更支持多平台授权&#xff0c;助…

超分辨率重建——2022冠军RLFN网络推理测试(详细图文教程)

&#x1f4aa; 专业从事且热爱图像处理&#xff0c;图像处理专栏更新如下&#x1f447;&#xff1a; &#x1f4dd;《图像去噪》 &#x1f4dd;《超分辨率重建》 &#x1f4dd;《语义分割》 &#x1f4dd;《风格迁移》 &#x1f4dd;《目标检测》 &#x1f4dd;《暗光增强》 &a…

如何用Vue3打造一个炫酷的树状图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 基于 Vue.js 的 Treemap 可视化组件 应用场景介绍 Treemap 可视化组件是一种强大的工具&#xff0c;用于以直观的方式展示分层数据。它将数据点绘制为矩形&#xff0c;其中每个矩形的大小与数据点的大小成正比…

阿里云平台创建设备及连接

使用阿里云平台创建项目&#xff0c;利用MQTT.fx软件配置相关的连接&#xff0c;在软件上完成消息的订阅与推送&#xff0c;与手机APP进行同步数据。了解MQTT相关的协议。 1.注册阿里云平台账号&#xff0c;完成实名注册&#xff01; 618创新加速季_新迁入云享5亿算力补贴-阿里…

JCR一区 | Matlab实现GAF-PCNN、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断

JJCR一区 | Matlab实现GAF-PCNN、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断 目录 JJCR一区 | Matlab实现GAF-PCNN、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断分类效果格拉姆矩阵图GAF-PCNNGASF-CNNGADF-CNN 基本介绍程序设计参考资料 分类效果 格拉姆…

【网络编程开发】16.域名解析与http服务器实现原理

16.域名解析与http服务器实现原理 文章目录 16.域名解析与http服务器实现原理gethostbyname 函数HTTP的操作过程实现httphome.htmlhttp-head.txtserver.c gethostbyname 函数 原型&#xff1a; #include <netdb.h> struct hostent *gethostbyname(const char *hostname);…

技巧|手机上看SwanLab实验的两种方法

什么是SwanLab? SwanLab是一个深度学习实验管理与训练可视化工具&#xff0c;由西安电子科技大学创业团队打造&#xff0c;融合了Weights & Biases与Tensorboard的特点&#xff0c;可以记录整个实验的超参数、指标、训练环境、Python版本等&#xff0c;并可视化图表&…