记一次 .NET 某工控电池检测系统 卡死分析

这篇具有很好参考价值的文章主要介绍了记一次 .NET 某工控电池检测系统 卡死分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一:背景

1. 讲故事

前几天有位朋友找到我,说他的窗体程序有卡死现象,让我帮忙看下怎么回事,解决这种问题就需要在卡死的时候抓一个dump下来,拿到dump之后就可以分析了。

二:为什么会卡死

1. 观察主线程

窗体程序的卡死,需要观察主线程此时正在做什么,可以用 !clrstack 命令观察。


0:000:x86> !clrstack
OS Thread Id: 0x4a08 (0)
Child SP       IP Call Site
012fe784 0000002b [HelperMethodFrame_1OBJ: 012fe784] System.Threading.WaitHandle.WaitOneNative(System.Runtime.InteropServices.SafeHandle, UInt32, Boolean, Boolean)
012fe868 7115d952 System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\waithandle.cs @ 243]
012fe880 7115d919 System.Threading.WaitHandle.WaitOne(Int32, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\waithandle.cs @ 194]
012fe894 711e89bf System.Threading.WaitHandle.WaitOne(Int32) [f:\dd\ndp\clr\src\BCL\system\threading\waithandle.cs @ 220]
012fe89c 6fb186b8 System.Threading.ReaderWriterLockSlim.WaitOnEvent(System.Threading.EventWaitHandle, UInt32 ByRef, TimeoutTracker, EnterLockType)
012fe8e0 6fb17892 System.Threading.ReaderWriterLockSlim.TryEnterReadLockCore(TimeoutTracker)
012fe920 6fb17562 System.Threading.ReaderWriterLockSlim.TryEnterReadLock(TimeoutTracker)
012fe94c 0325f49f xxx.QuyitpjK0dXKR6IyqH(System.Object)
012fe964 0325ee8a xxx.RWAutoLock..ctor(System.Threading.ReaderWriterLockSlim, Boolean)
...

从卦中的线程栈数据来看,貌似是卡在一个读写锁TryEnterReadLock 上,根据读写锁的规则,必然有人执行了一个 WriteLock 并且出不来,接下来就是寻找持有这个 lock 的线程。

2. 到底谁在持有

如果是 lock ,相信很多朋友都知道用 !syncblk 命令,那读写锁用什么命令呢?说实话我也搞不清楚,只能先挖挖 ReaderWriterLockSlim 类本身,看看有没有什么新发现。


0:000:x86> !DumpObj /d 03526f38
Name:        System.Threading.ReaderWriterLockSlim
MethodTable: 6f947428
EEClass:     6f9a92dc
Size:        72(0x48) bytes
File:        C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
70da878c  40004aa       38       System.Boolean  1 instance        0 _fIsReentrant
6f92fa28  40004ab       3c ...LockSlim+SpinLock  1 instance 03526f74 _spinLock
70dfba4c  40004ac       1c        System.UInt32  1 instance       20 _numWriteWaiters
70dfba4c  40004ad       20        System.UInt32  1 instance        1 _numReadWaiters
70dfba4c  40004ae       24        System.UInt32  1 instance        0 _numWriteUpgradeWaiters
70dfba4c  40004af       28        System.UInt32  1 instance        0 _numUpgradeWaiters
6f93d764  40004b0       39          System.Byte  1 instance        0 _waiterStates
70da42a8  40004b1       2c         System.Int32  1 instance       -1 _upgradeLockOwnerId
70da42a8  40004b2       30         System.Int32  1 instance       11 _writeLockOwnerId
70da6924  40004b3        c ...g.EventWaitHandle  0 instance 034844d0 _writeEvent
70da6924  40004b4       10 ...g.EventWaitHandle  0 instance 042a69c8 _readEvent
70da6924  40004b5       14 ...g.EventWaitHandle  0 instance 00000000 _upgradeEvent
70da6924  40004b6       18 ...g.EventWaitHandle  0 instance 00000000 _waitUpgradeEvent
70da150c  40004b8        4         System.Int64  1 instance 367 _lockID
70da878c  40004ba       3a       System.Boolean  1 instance        0 _fUpgradeThreadHoldingRead
70dfba4c  40004bc       34        System.UInt32  1 instance 3221225472 _owners
70da878c  40004c2       3b       System.Boolean  1 instance        0 _fDisposed
70da42a8  40004a9      4dc         System.Int32  1   static        4 ProcessorCount
70da150c  40004b7      4d4         System.Int64  1   static 1882 s_nextLockID
6f942b7c  40004b9        0 ...ReaderWriterCount  0 TLstatic  t_rwc

结合源码分析,发现上面的 _writeLockOwnerId=11 就是持有锁的线程ID,找到持有线程就好办了,把这个 managedid=11 转成 dbgid 再观察。


0:000:x86> !t

  13   11 47bc 0a0702c0   1029220 Preemptive  00000000:00000000 01425ed0 0     MTA (Threadpool Worker) 

0:013:x86> !clrstack
OS Thread Id: 0x47bc (13)
Child SP       IP Call Site
07e4f1ac 0000002b [InlinedCallFrame: 07e4f1ac] 
07e4f1a4 09e38597 DomainBoundILStubClass.IL_STUB_PInvoke(IntPtr)
07e4f1ac 09e38334 [InlinedCallFrame: 07e4f1ac] System.Data.SQLite.UnsafeNativeMethods.sqlite3_step(IntPtr)
07e4f1dc 09e38334 System.Data.SQLite.SQLite3.Step(System.Data.SQLite.SQLiteStatement)
07e4f228 09e36fe8 System.Data.SQLite.SQLiteDataReader.NextResult()
07e4f250 09e36ceb System.Data.SQLite.SQLiteDataReader..ctor(System.Data.SQLite.SQLiteCommand, System.Data.CommandBehavior)
07e4f270 09e367ce System.Data.SQLite.SQLiteCommand.ExecuteReader(System.Data.CommandBehavior)
07e4f284 09e36732 System.Data.SQLite.SQLiteCommand.ExecuteNonQuery(System.Data.CommandBehavior)
07e4f2b0 09e366e6 System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
07e4f2bc 09e350dc SqlSugar.AdoProvider.ExecuteCommand(System.String, SqlSugar.SugarParameter[])
07e4f388 13189518 SqlSugar.InsertableProvider`1[[System.__Canon, mscorlib]].ExecuteCommand()
07e4f420 0181ac4a xxx.OperateLog+d__8.MoveNext()
...

0:013:x86> k
CvRegToMachine(x86) conversion failure for 0x14f
X86MachineInfo::SetVal: unknown register 0 requested
 # ChildEBP RetAddr      
00 07e4ede0 76c9ad10     ntdll_76ed0000!NtFlushBuffersFile+0xc
01 07e4ede0 6b27af8c     KERNELBASE!FlushFileBuffers+0x30
WARNING: Stack unwind information not available. Following frames may be wrong.
02 07e4edf0 6b270256     SQLite_Interop!SI768767362ea03a94+0xf73c
03 07e4ee1c 6b267938     SQLite_Interop!SI768767362ea03a94+0x4a06
04 07e4ee38 6b2599e1     SQLite_Interop!SI83d1cf4976f57337+0x84c8
05 07e4ee80 6b25902b     SQLite_Interop!SIa3401e98cbad673e+0x3201
06 07e4ee98 6b25258c     SQLite_Interop!SIa3401e98cbad673e+0x284b
07 07e4f168 6b255a05     SQLite_Interop!SI327cfc7a6b1fd1fb+0x633c
08 07e4f19c 09e38597     SQLite_Interop!SI9c6d7cd7b7d38055+0x255

结合卦中的读写信息,大概知道了原来是用写锁来写sqlite,后者卡在缓冲区刷新函数 NtFlushBuffersFile 上,方法签名如下:


NTSTATUS NtFlushBuffersFile(
  HANDLE  FileHandle,
  IO_STATUS_BLOCK *IoStatusBlock
);

有些朋友可能想看一下到底怎么写的,那就简单的反编译一下代码:

记一次 .NET 某工控电池检测系统 卡死分析

到这里基本就搞清楚了,由于 13号 线程持有了 写锁,导致主线程要用读锁操作 sqlite 时进行了长时间等待。

解决办法就比较简单了,主线程尽可能的只做UI更新的操作,不要让他触发各类锁,否则就有等锁的概率发生。

3. NtFlushBuffersFile 怎么了

有些朋友可能要问为什么 NtFlushBuffersFile 函数会卡死不返回,要想找到这个答案,需要看下反汇编。


0:013:x86> uf ntdll_76ed0000!NtFlushBuffersFile
ntdll_76ed0000!NtFlushBuffersFile:
76f41ad0 b84b000000      mov     eax,4Bh
76f41ad5 ba7071f576      mov     edx,offset ntdll_76ed0000!Wow64SystemServiceCall (76f57170)
76f41ada ffd2            call    edx
76f41adc c20800          ret     8

0:013:x86> u 76F57170h
ntdll_76ed0000!Wow64SystemServiceCall:
76f57170 ff252892ff76    jmp     dword ptr [ntdll_76ed0000!Wow64Transition (76ff9228)]

0:013:x86> u 76ec7000
wow64cpu!KiFastSystemCall:
76ec7000 ea0970ec763300  jmp     0033:76EC7009
76ec7007 0000            add     byte ptr [eax],al
76ec7009 41              inc     ecx
76ec700a ffa7f8000000    jmp     dword ptr [edi+0F8h]

从汇编代码看,NtFlushBuffersFile 通过 KiFastSystemCall 进入内核态了,用户态dump是没法看内核态的,所以也无法继续追究下去。

不过也可以看下这个线程过往的 GetLastError() 值,可能有些收获,使用 !gle 命令。


0:013:x86> !gle
LastErrorValue: (Win32) 0x26 (38) - <Unable to get error code text>
LastStatusValue: (NTSTATUS) 0xc0000008 - <Unable to get error code text>

根据上面的状态码,去msdn上搜一下具体信息。

记一次 .NET 某工控电池检测系统 卡死分析

记一次 .NET 某工控电池检测系统 卡死分析

从错误说明看,可能是这个sqlite文件有什么问题,又是句柄无效,又是读到头了,怀疑是操作sqlite 的时候出现了文件损坏

现在回头看看,如果想对 Sqlite 进行并发读写,开启下 Write-Ahead Logging 模式应该就可以了,不需要在程序里面进行读写控制。

记一次 .NET 某工控电池检测系统 卡死分析

所以最终的建议就是:

  • 开启WAL模式
  • 删掉读写控制

三:总结

这次卡死事故还是挺有意思的,熟悉了下 ReaderWriterLockSlim 又对 sqlite 有了一个新的认识。文章来源地址https://www.toymoban.com/news/detail-746341.html

记一次 .NET 某工控电池检测系统 卡死分析

到了这里,关于记一次 .NET 某工控电池检测系统 卡死分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 记一次 .NET 某新能源材料检测系统 崩溃分析

    上周有位朋友找到我,说他的程序经常会偶发性崩溃,一直没找到原因,自己也抓了dump 也没分析出个所以然,让我帮忙看下怎么回事,那既然有 dump,那就开始分析呗。 一直跟踪我这个系列的朋友应该知道分析崩溃第一个命令就是 !analyze -v ,让windbg帮我们自动化异常分析。

    2024年02月05日
    浏览(60)
  • 记一次 .NET 某券商论坛系统 卡死分析

    前几个月有位朋友找到我,说他们的的web程序没有响应了,而且监控发现线程数特别高,内存也特别大,让我帮忙看一下怎么回事,现在回过头来几经波折,回味价值太浓了。 这个程序内存高,线程高,无响应,尼玛是一个复合态问题,那怎么入手呢?按经验推测,大概率是

    2024年02月05日
    浏览(63)
  • 记一次 .NET 某药材管理系统 卡死分析

    前段时间有位朋友找到我,说他们在查询报表的时候发现程序的稳定性会受到影响,但服务器的内存,CPU都是正常的,让我帮忙看下怎么回事,问了下程序的稳定性指的是什么?指的是卡死,那既然是卡死,就抓一个卡死的dump吧。 不同的程序类型分析卡死的思路是不一样的

    2024年02月09日
    浏览(57)
  • 记一次 .NET某医疗器械清洗系统 卡死分析

    前段时间协助训练营里的一位朋友分析了一个程序卡死的问题,回过头来看这个案例比较经典,这篇稍微整理一下供后来者少踩坑吧。 因为是窗体程序,理所当然就是看主线程此时正在做什么? 可以用 ~0s ; k 看一下便知。 从线程栈来看,当前的方法卡在 win32u!NtUserPeekMessage 上

    2023年04月18日
    浏览(54)
  • 记一次 .NET某工控 宇宙射线 导致程序崩溃分析

    为什么要提 宇宙射线 , 太阳耀斑 导致的程序崩溃呢?主要是昨天在知乎上看了这篇文章:莫非我遇到了传说中的bug? ,由于 rip 中的0x41变成了0x61出现了bit位翻转导致程序崩溃,截图如下: 下面的评论大多是说由于 宇宙射线 ,这个太玄乎了,说实话看到这个 传说bug 的提法

    2024年02月04日
    浏览(44)
  • 记一次 .NET 某医院门诊软件 卡死分析

    前几天有位朋友找到我,说他们的软件在客户那边卡死了,让我帮忙看下是怎么回事?我就让朋友在程序卡死的时候通过 任务管理器 抓一个 dump 下来,虽然默认抓的是 wow64 ,不过用 soswow64.dll 转还是可以的,参考命令如下: 接下来就可以分析了哈。 首先用 !t 简单看一下主

    2024年02月04日
    浏览(47)
  • 记一次 .NET 某拍摄监控软件 卡死分析

    今天本来想写一篇 非托管泄露 的生产事故分析,但想着昨天就上了一篇非托管文章,连着写也没什么意思,换个口味吧,刚好前些天有位朋友也找到我,说他们的拍摄监控软件卡死了,让我帮忙分析下为什么会卡死,听到这种软件,让我不禁想起了前些天 在程序员桌子上安

    2024年02月08日
    浏览(43)
  • 记一次 .NET某MES自动化桌面程序 卡死分析

    前些天有位朋友在微信上找到我,说他们的客户端程序卡死了,让我帮忙看下是什么原因导致的?dump也拿到了手,既然有了dump就开始正式分析吧。 客户端的程序卡死比较好找原因,入手点就是主线程,看下它此时正在做什么,可以用 k 命令。 从卦中信息看,代码正在托管层

    2024年01月16日
    浏览(45)
  • 新能源汽车电池包自动三维尺寸检测系统蓝光光学平面度测量仪-CASAIM

    电池包是新能源汽车核心能量源,为整车提供驱动电能。作为新能源汽车的核心部件,其品质直接决定了整车性能。 由于电池包的生产工艺相对复杂,传统的测量工具不仅测量工序复杂、精度不足,还会或多或少接触到电池表面形成瑕疵,因此高精度测量电池包提升质量品质

    2024年02月07日
    浏览(53)
  • 记一次 .NET某防伪验证系统 崩溃分析

    昨晚给训练营里面的一位朋友分析了一个程序崩溃的故障,因为看小伙子昨天在群里问了一天也没搞定,干脆自己亲自上阵吧,抓取的dump也是我极力推荐的用 procdump 注册 AEDebug 的方式,省去了很多沟通成本。 windbg有一个非常强大的点就是当你双击打开后,会自动帮你切换到

    2024年03月28日
    浏览(63)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包