记一次 .NET 某数控机床控制程序 卡死分析

一:背景

1. 讲故事

前段时间有位朋友微信上找到我,说它的程序出现了卡死,让我帮忙看下是怎么回事? 说来也奇怪,那段时间求助卡死类的dump特别多,被迫训练了一下对这类问题的洞察力 😄😄😄,再次声明一下,我分析 dump 是免费的,没有某软高额的分析费用,你要问我图什么,图技术的精进。

回到正题,卡死类的问题分析入口点在于主线程此时在做什么,导致它不能处理自己的任务队列的原因是各种各样的,接下来上 windbg 分析一下。

二:WinDbg 分析

1. 主线程此时在做什么

看主线程很简单,直接一个 k 命令搞定。

0:000> k# ChildEBP RetAddr      
00 00f3ec2c 74ef6439     ntdll!NtWaitForSingleObject+0xc
01 00f3ec2c 70293370     KERNELBASE!WaitForSingleObjectEx+0x99
02 00f3ec5c 702933cc     clr!CLREventWaitHelper2+0x33
03 00f3ecac 70293319     clr!CLREventWaitHelper+0x2a
04 00f3ece4 7037bbcf     clr!CLREventBase::WaitEx+0x14b
05 00f3ecfc 70374f08     clr!WKS::GCHeap::WaitUntilGCComplete+0x35
06 00f3ed44 7032c2a2     clr!Thread::RareDisablePreemptiveGC+0x20b
07 00f3edc8 6d453712     clr!JIT_RareDisableHelper+0x22
08 00f3ee4c 6d4530d1     System_Windows_Forms_ni!System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr, Int32, Int32)$##600550A+0x48e
09 00f3eea0 6d452f23     System_Windows_Forms_ni!System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)$##6005539+0x175
0a 00f3eecc 6d42b83d     System_Windows_Forms_ni!System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)$##6005538+0x4f
0b 00f3eee4 02d90c83     System_Windows_Forms_ni!System.Windows.Forms.Application.Run(System.Windows.Forms.Form)$##60005FA+0x35
...

从卦上信息看,主线程正在等待(WaitUntilGCComplete) GC 处理完成,接下来看下是哪一个线程触发了 GC 操作,并看下它正在 GC 三阶段的哪一步?

0:000> !t
ThreadCount:      33
UnstartedThread:  3
BackgroundThread: 29
PendingThread:    3
DeadThread:       0
Hosted Runtime:   noLock  ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception0    1 17a0 0113ecd0     26020 Preemptive  00000000:00000000 01139490 0     STA 2    2 2074 0114e0e0     2b220 Preemptive  00000000:00000000 01139490 0     MTA (Finalizer) 5    3 27f4 011ddaa8   102a220 Preemptive  00000000:00000000 01139490 0     MTA (Threadpool Worker) 8    4 2568 05bf0b90   1029220 Cooperative 00000000:00000000 01139490 1     MTA (GC) (Threadpool Worker) 0:000> ~8s
eax=00000000 ebx=05bf0b90 ecx=00000000 edx=00000000 esi=00000000 edi=00000f6c
eip=77e5aa5c esp=098ce03c ebp=098ce0ac 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:
77e5aa5c c20c00          ret     0Ch
0:008> k# ChildEBP RetAddr      
00 098ce0ac 74ef6439     ntdll!NtWaitForSingleObject+0xc
01 098ce0ac 70293370     KERNELBASE!WaitForSingleObjectEx+0x99
02 098ce0dc 702933cc     clr!CLREventWaitHelper2+0x33
03 098ce12c 70293319     clr!CLREventWaitHelper+0x2a
04 098ce164 7029333b     clr!CLREventBase::WaitEx+0x14b
05 098ce17c 7032a9b7     clr!CLREventBase::Wait+0x1a
06 098ce1fc 7032a9ef     clr!`anonymous namespace'::CreateSuspendableThread+0x165
07 098ce378 704322bd     clr!GCToEEInterface::CreateThread+0x15c
08 098ce3a8 704332e5     clr!WKS::gc_heap::prepare_bgc_thread+0x36
09 098ce3a8 70378585     clr!WKS::gc_heap::garbage_collect+0x215
0a 098ce3c8 7037868a     clr!WKS::GCHeap::GarbageCollectGeneration+0x1bd
0b 098ce3ec 70378703     clr!WKS::gc_heap::trigger_gc_for_alloc+0x1f
0c 098ce424 70396f6a     clr!WKS::gc_heap::try_allocate_more_space+0x152
0d 098ce46c 70397311     clr!WKS::gc_heap::allocate_large_object+0x51
0e 098ce478 702fde2d     clr!WKS::GCHeap::AllocLHeap+0x11
0f 098ce494 7063f1af     clr!AllocLHeap+0x4b
10 098ce4e8 7028f598     clr!FastAllocatePrimitiveArray+0xa7
11 098ce58c 0e2d3fb6     clr!JIT_NewArr1+0x126
12 098cf26c 6f1f1b5a     0xe2d3fb6
13 098cf274 6f188604     mscorlib_ni!System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.InvokeMoveNext(System.Object)$##6006FCB+0x1a
...

从卦中的 clr!GCToEEInterface::CreateThread 方法看,如果你没有这方面的分析经验,或许你已经无能为力了,以我的经验来说,既然卡死,那这里的 CreateThread 自然无法创建成功,创建不成功的原因在于 进程加载锁 LdrpLoaderLock 被别人持有了,导致它获取不到这把锁而一直茫然等待。

2. 谁持有了加载锁

要想找到谁持有了加载锁,可以在 ntdll 中搜 LdrpLoaderLock 字段,接下来使用 x 命令搜索。

0:008> x ntdll!LdrpLoaderLock
77f053b8          ntdll!LdrpLoaderLock = <no type information>
0:008> !cs 77f053b8
-----------------------------------------
Critical section   = 0x77f053b8 (ntdll!LdrpLoaderLock+0x0)
DebugInfo          = 0x77f0573c
LOCKED
LockCount          = 0x0
WaiterWoken        = No
OwningThread       = 0x00001314
RecursionCount     = 0x1
LockSemaphore      = 0x0
SpinCount          = 0x04000000

从卦中的 OwningThread = 0x00001314 迹象看,当前是线程 1314 持有了加载锁,看样子是要一生一世的持有。。。。 难怪程序不卡死。。。。 接下来切到这个线程看看到底咋这么痴情?

0:043> ~~[1314]s
eax=00000000 ebx=0589f784 ecx=00000000 edx=00000000 esi=00000000 edi=0589f770
eip=77e5c6bc esp=0589f72c ebp=0589f748 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
ntdll!NtWaitForAlertByThreadId+0xc:
77e5c6bc c20800          ret     8
0:043> kb # ChildEBP RetAddr      Args to Child              
00 0589f748 77e1336b     0ad114e8 00000000 00000000 ntdll!NtWaitForAlertByThreadId+0xc
01 0589f748 77e50630     00000000 00000000 ffffffff ntdll!RtlpWaitOnAddressWithTimeout+0x33
02 0589f78c 77e13299     00000004 00000000 00000000 ntdll!RtlpWaitOnAddress+0xa5
03 0589f7c8 77e2ed76     00000000 0ad114e0 0ad114e4 ntdll!RtlpWaitOnCriticalSection+0xaf
04 0589f7f0 77e2ec99     0589f814 0ac5d1b4 0ad114e4 ntdll!RtlpEnterCriticalSectionContended+0xd6
05 0589f7f8 0ac5d1b4     0ad114e4 00000003 0ac50000 ntdll!RtlEnterCriticalSection+0x49
06 0589f814 0ac5a890     00001314 0acddd2a 0ac50000 fwlibe1!xxx+0x13e3
07 0589f83c 77e5a9f6     0ac50000 00000003 00000000 fwlibe1!xxx+0x290
...

从卦中看,当前是 fwlibe1 这个非托管dll 准备获取 临界区 时卡死,接下来可以用 !cs 把 临界区变量 给提取出来看看到底谁在持有这个 临界区变量。

0:043> !cs 0ad114e4
-----------------------------------------
Critical section   = 0x0ad114e4 (fwlibe1!xxxx+0x3A1C2)
DebugInfo          = 0x0ff43360
LOCKED
LockCount          = 0x2
WaiterWoken        = No
OwningThread       = 0x00002378
RecursionCount     = 0x1
LockSemaphore      = 0xFFFFFFFF
SpinCount          = 0x020007d0

从卦中可以清晰的看到,当前临界区变量被  2378 号线程持有,接下来切到这个线程看看到底啥情况。

0:043> ~~[2378]s
eax=00000036 ebx=00002378 ecx=00000400 edx=00000157 esi=0ad114fc edi=0ad114e4
eip=0ac5cf88 esp=0caddf18 ebp=0cade7fc iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
fwlibe1!xxx+0x11b7:
0ac5cf88 8d45f0          lea     eax,[ebp-10h]0:028> k# ChildEBP RetAddr      
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0cade7fc 0ac5a8df     fwlibe1!xxx+0x11b7
01 0cade938 0ac5abae     fwlibe1!xxx+0x22
02 0cadea20 09417ccd     fwlibe1!xxx+0x17
03 0cadebfc 09412c11     0x9417ccd
04 0caded2c 6f1f1b5a     0x9412c11
05 0caded34 6f188604     mscorlib_ni!System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.InvokeMoveNext(System.Object)$##6006FCB+0x1a
...0:028> !clrstack 
OS Thread Id: 0x2378 (28)
Child SP       IP Call Site
0cade9b4 0ac5cf88 [InlinedCallFrame: 0cade9b4] 
0cade998 0941a0e9 *** WARNING: Unable to verify checksum for System.Data.ni.dll
DomainBoundILStubClass.IL_STUB_PInvoke(System.Object, UInt16, Int32, UInt16 ByRef)
0cade9b4 09417ccd [InlinedCallFrame: 0cade9b4] Focas.xxx(System.Object, UInt16, Int32, UInt16 ByRef)

从卦中看,托管层调用了 Focas.xxx 函数进入了 fwlibe1.dll ,最后有一句 WARNING: Stack unwind information not available. Following frames may be wrong. ,可能是展开有一定的问题,不过排查到这里,应该就是这个 Focas.xxx 的问题了。

3. 真相大白

接下来将我的排查结果跟朋友反馈了下,朋友排查下来说是最近改了这里面的连接方式所致,修改之后就搞定了。

7778c76af3b044b6710778542edd437f.png

三:总结

这个案例告诉我们:只要代码还能跑,就不要动它,它要是不动了,你得要做好动的准备。

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

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

相关文章

ASP.NETCoreWeb开发之OptionsPattern

这节我们来讲一下&#xff0c;在ASP.NET Core Web开发中&#xff0c;读取配置文件信息的新方式&#xff1a;Options。前言 /Options在ASP.NET Web框架中&#xff0c;我们读取配置文件中的数据&#xff0c;在不使用第三方框架的情况下&#xff0c;可能需要通过ConfigurationMana…

SpringMVC执行流程图

2019独角兽企业重金招聘Python工程师标准>>> 转载于:https://my.oschina.net/u/2607324/blog/827946

CentOS 7系统安装配置图解教程

操作系统&#xff1a;CentOS 7.3 备注&#xff1a; CentOS 7.x系列只有64位系统&#xff0c;没有32位。生产服务器建议安装CentOS-7-x86_64-Minimal-1611.iso版本 一、安装CentOS 7.3 成功引导系统后&#xff0c;会出现下面的界面 界面说明&#xff1a; Install CentOS 7 #安装…

这份《.NET/C#面试手册》超神啦!

这几天给.neter们整理了一份《.NET/C#面试手册》&#xff0c;目前大约4万字左右&#xff0c;初衷也很简单&#xff0c;就是希望在面试的时候能够帮助到大家&#xff0c;减轻大家的负担和节省时间。对于没有跳槽打算的也可以复习一下相关知识点&#xff0c;就当是查缺补漏&#…

Dinic算法----最大流常用算法之一

——没有什么是一个BFS或一个DFS解决不了的&#xff1b;如果有&#xff0c;那就两个一起。 最大流的$EK$算法虽然简单&#xff0c;但时间复杂度是$O(nm^2)$&#xff0c;在竞赛中不太常用。 竞赛中常用的$Dinic$算法和$SAP$&#xff0c;其实也不太难。 那么&#xff0c;$Dinic$算…

javascript学习笔记 null和undefined

null是javascript语言的关键字&#xff0c;它表示一个特殊值&#xff0c;常用来描述“空值”。对null执行typeof预算&#xff0c;结果返回字符串“object”&#xff0c;也就是说&#xff0c;可以将null认为是一个特殊的对象值&#xff0c;含义是“非对象”。但实际上&#xff0…

C# 为什么高手都是用IsNullOrWhiteSpace对字符串判空?

判断字符串为空有好几种方法&#xff1a;方法一&#xff1a; 代码如下&#xff1a;static void Main(string[] args){string str "";if (str ""){Console.WriteLine("a is empty"); ;}Console.ReadKey();}运行结果&#xff1a;a is empty这样…

Blazor University (51)依赖注入 —— 拥有多个依赖项:错误的方式

原文链接&#xff1a;https://blazor-university.com/dependency-injection/component-scoped-dependencies/owning-multiple-dependencies-the-wrong-way/拥有多个依赖项&#xff1a;错误的方式OwningComponentBase[1] 类是一个合适的解决方案&#xff0c;当我们需要我们的组件…

Centos 7 搭建.net web项目

现在的.NET Core 1.0版本是一个很小的核心&#xff0c;APIs和工具也并不完整&#xff0c;但是随着.Net Core的不断完善&#xff0c;补充的Apis和创新也会一起整合到.NET Framework中。 安装centos系统 请自行安装或百度教程 安装 libicu包 和 dotnet 温馨提示&#xff1a;如果需…

IDEA 快捷注释

1. 新建类的注释模板 1) File->settings->Editor->Live Templates 2) 点击绿色号&#xff0c;选择template group &#xff0c;输入group的name&#xff0c;然后点ok 3) 选中刚才添加的group,点击号,选择live Template 4) 代码模板位置,个人用的代码: 1 /** 2 * &…

matlab 如何hidden,Matlab基本函数-hidden函数

1、hidden函数&#xff1a;设置或取消隐藏线模式2、用法说明(1)hidden on 函数对当前图形打开隐藏线条删除&#xff0c;使网格图后面的线条被前面的线条遮住。设置曲面图形对象的属性FaceColor为坐标轴背景颜色&#xff1b;(2)hidden off 函数对当前图形关闭隐藏线条删除&#…

异常处理,究竟是处理什么

“系统中每行代码&#xff0c;都应该是有意义的&#xff0c;如果一段代码可有可无&#xff0c;那它就不应该存在。”01—内容简述异常处理是软件开发的必备技能&#xff0c;但“异常处理&#xff0c;究竟是处理什么&#xff1f;”&#xff0c;很多小伙伴并没有一个清晰的认识&a…

第十一篇:(顺序)容器的好伴侣 --- 容器适配器

前言 vector容器的数据结构原型是顺序表&#xff0c;它很好的实现了顺序表的功能&#xff0c;大大方便了编程。好了&#xff0c;现在假设有天我又想用栈&#xff0c;那么有没有栈对应的容器呢&#xff1f;很遗憾&#xff0c;木有。但基于“栈”可以由顺序表或者链表实现这一特性…

第一季度ADC市场份额揭榜 A10 Networks再获用户青睐

近日&#xff0c;根据全球知名咨询公司IDC 发布的2018年第一季度中国ADC市场分析报告显示&#xff0c;A10 Networks 稳占中国ADC市场份额第二名。数据来源&#xff1a;IDC 2018年Q1 ADC市场报告 从厂商排名来看依次为 F5 30%, A10Networks 12%, DPtech 12% ,Sangfor 9% &#…

利用linux shell自己主动顶贴

在论坛上面发帖问个什么东西的话&#xff0c;一旦不顶。帖子就秒沉了&#xff0c;可是又实在不想每时每刻都去顶&#xff0c;怎么办&#xff1f;以下展示了怎样利用shell 的crontab实现自己主动顶贴。 闲话不多说了&#xff0c;以豆瓣为例—– 1&#xff1a; 用chrome打开豆瓣…

深度学习库 SynapseML for .NET 发布0.1 版本

2021年11月 微软开源一款简单的、多语言的、大规模并行的机器学习库 SynapseML&#xff08;以前称为 MMLSpark&#xff09;&#xff0c;以帮助开发人员简化机器学习管道的创建。具体参见[1]微软深度学习库 SynapseML&#xff1a;可直接在系统中嵌入 45 种不同机器学习服务、支持…

支持回调处理 php函数,PHP支持回调的函数有哪些?

PHP支持回调的函数有&#xff1a;1、匿名函数&#xff0c;代码为【$server->on Request】&#xff1b;2、类静态方法&#xff0c;代码为【static function test $req】&#xff1b;3、函数&#xff0c;代码为【my_onRequest $req】。PHP支持回调的函数有&#xff1a;1、匿名…

病毒木马查杀实战第019篇:病毒特征码查杀之编程实现

前言上次我们已经简介过了病毒特征码提取的基本方法&#xff0c;那么这次我们就通过编程来实现对于病毒的特征码查杀。定义特征码存储结构为了简单起见。这次我们使用的是setup.exe以及unpacked.exe这两个病毒样本。经过上次的分析&#xff0c;我们对setup.exe样本的特征码提取…

《ASP.NET Core 6框架揭秘》实例演示[22]:如何承载你的后台服务[补充]

借助 .NET提供的服务承载&#xff08;Hosting&#xff09;系统&#xff0c;我们可以将一个或者多个长时间运行的后台服务寄宿或者承载我们创建的应用中。任何需要在后台长时间运行的操作都可以定义成标准化的服务并利用该系统来承载&#xff0c;ASP.NET Core应用最终也体现为这…

ASP.NET Core MVC压缩样式、脚本及总是复制文件到输出目录

前言 在.NET Core之前对于压缩样式文件和脚本我们可能需要借助第三方工具来进行压缩&#xff0c;但在ASP.NET MVC Core中则无需借助第三方工具来完成&#xff0c;本节我们来看看ASP.NET Core MVC为我们提供了哪些方便。 自动压缩样式和脚本 当我们在测试环境中肯定不需要压缩脚…