记一次 .NET某环境监测系统 崩溃分析

打印 上一主题 下一主题

主题 642|帖子 642|积分 1926

一:配景

1. 讲故事

前些天有位朋友找到我,说他们的程序崩溃了,也自己分析了下初步结果,让我帮忙再确认下,既然让我确认,那就开始dump分析之旅吧。
二:WinDbg 分析

1. 为什么会崩溃

windbg 有一个强大之处就是带有一个自动化的分析命令 !analyze -v 可以资助我们快速的分析,输出如下:
  1. 0:000> !analyze -v
  2. *******************************************************************************
  3. *                                                                             *
  4. *                        Exception Analysis                                   *
  5. *                                                                             *
  6. *******************************************************************************
  7. CONTEXT:  (.ecxr)
  8. rax=00007ff95c5a9877 rbx=00007ff959d6d8e0 rcx=0000000000000000
  9. rdx=0000000000000000 rsi=000000e394b98de0 rdi=000000e394b99530
  10. rip=00007ff959c7b699 rsp=000000e394b99510 rbp=000000e394b99d00
  11. r8=0000000000000000  r9=0000000000000007 r10=0000000000000000
  12. r11=0000000000000000 r12=0000022da11451d0 r13=0000000000000000
  13. r14=000000e394b9a9e0 r15=0000000000040ae4
  14. iopl=0         nv up ei pl nz na pe nc
  15. cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000200
  16. KERNELBASE!RaiseException+0x69:
  17. 00007ff9`59c7b699 0f1f440000      nop     dword ptr [rax+rax]
  18. Resetting default scope
  19. EXCEPTION_RECORD:  (.exr -1)
  20. ExceptionAddress: 00007ff959c7b699 (KERNELBASE!RaiseException+0x0000000000000069)
  21.    ExceptionCode: c000041d
  22.   ExceptionFlags: 00000001
  23. NumberParameters: 0
  24. PROCESS_NAME:  xxx.Desktop.dll
  25. ERROR_CODE: (NTSTATUS) 0xc000041d - <Unable to get error code text>
  26. EXCEPTION_CODE_STR:  c000041d
  27. ...
复制代码
从卦中可以看到当前的崩溃码是 c000041d,即 An unhandled exception was encountered during a user callback,这个异常码是个统称异常,言外之意就是内部还藏有真实的异常码,那真实的异常码是多少呢?
2. 真实的异常码在哪里

要想知道这个答案,可以切到异常上下文找到 RaiseException 的父函数在图观察,输出如下:
  1. 0:000> k 5
  2. # Child-SP          RetAddr               Call Site
  3. 00 000000e3`94b99510 00007ff8`eb52cb19     KERNELBASE!RaiseException+0x69
  4. 01 000000e3`94b995f0 00007ff8`eb52cb4b     coreclr!NakedThrowHelper2+0x9
  5. 02 000000e3`94b99620 00007ff8`eb52cb55     coreclr!NakedThrowHelper_RspAligned+0x1e
  6. 03 000000e3`94b99b48 00007ff8`8da3caa3     coreclr!NakedThrowHelper_FixRsp+0x5
  7. 04 000000e3`94b99b50 00007ff8`8d5a5e23     Avalonia_Base!Avalonia.Rendering.Composition.Compositor.RequestCompositionUpdate+0x83
  8. 0:000> ub 00007ff8`eb52cb19
  9. ...
  10. 00007ff8`eb52cb14 e857910b00      call    coreclr!LinkFrameAndThrow (00007ff8`eb5e5c70)
  11. 0:000> uf coreclr!LinkFrameAndThrow
  12. Flow analysis was incomplete, some code may be missing
  13. coreclr!LinkFrameAndThrow [D:\a\_work\1\s\src\coreclr\vm\excep.cpp @ 6934]:
  14. 6934 00007ff8`eb5e5c70 4053            push    rbx
  15. 6934 00007ff8`eb5e5c72 4883ec20        sub     rsp,20h
  16. 6937 00007ff8`eb5e5c76 488d05bb771f00  lea     rax,[coreclr!FaultingExceptionFrame::`vftable' (00007ff8`eb7dd438)]
  17. ...
  18. 6949 00007ff8`eb5e5cea 448b05c7682800  mov     r8d,dword ptr [coreclr!g_SavedExceptionInfo+0x18 (00007ff8`eb86c5b8)]
  19. 6949 00007ff8`eb5e5cf1 8b15ad682800    mov     edx,dword ptr [coreclr!g_SavedExceptionInfo+0x4 (00007ff8`eb86c5a4)]
  20. 6949 00007ff8`eb5e5cf7 8b0da3682800    mov     ecx,dword ptr [coreclr!g_SavedExceptionInfo (00007ff8`eb86c5a0)]
  21. 6950 00007ff8`eb5e5cfd 4883c420        add     rsp,20h
  22. 6950 00007ff8`eb5e5d01 5b              pop     rbx
  23. 6949 00007ff8`eb5e5d02 48ff2537581b00  jmp     qword ptr [coreclr!_imp_RaiseException (00007ff8`eb79b540)]  Branch
  24. ...
复制代码
从卦中可以看到 RaiseException 的参数来自于异常信息全局变量 g_SavedExceptionInfo,这个变量中存放着当前崩溃的真实上下文以及寄存器信息,在 CLR 中的数据布局如下:
  1. struct SavedExceptionInfo
  2. {
  3.     EXCEPTION_RECORD m_ExceptionRecord;
  4.     CONTEXT m_ExceptionContext;
  5.     CrstStatic m_Crst;
  6. }
复制代码
有了这些之后接下来就可以用 dt 来挖了,输出如下:
  1. 0:000> dt coreclr!g_SavedExceptionInfo 00007ff8eb86c5a0
  2.    +0x000 m_ExceptionRecord : _EXCEPTION_RECORD
  3.    +0x0a0 m_ExceptionContext : _CONTEXT
  4.    +0x570 m_Crst           : CrstStatic
  5. 0:000> dx -r1 (*((coreclr!_EXCEPTION_RECORD *)0x7ff8eb86c5a0))
  6. (*((coreclr!_EXCEPTION_RECORD *)0x7ff8eb86c5a0))                 [Type: _EXCEPTION_RECORD]
  7.     [+0x000] ExceptionCode    : 0xc0000005 [Type: unsigned long]
  8.     [+0x004] ExceptionFlags   : 0x0 [Type: unsigned long]
  9.     [+0x008] ExceptionRecord  : 0x0 [Type: _EXCEPTION_RECORD *]
  10.     [+0x010] ExceptionAddress : 0x7ff88da3caa3 [Type: void *]
  11.     [+0x018] NumberParameters : 0x2 [Type: unsigned long]
  12.     [+0x020] ExceptionInformation [Type: unsigned __int64 [15]]
复制代码
从卦中信息来看当前崩溃的真正缘故起因是 0xc0000005,即 访问违例,同时还记录了崩溃的那个点 RIP=0x7ff88da3caa3。
3. 什么逻辑导致的崩溃

这个比较简单,用 !U 和 uf 都可以试下,输出如下:
  1. 0:000> !U 0x7ff88da3caa3
  2. Normal JIT generated code
  3. Avalonia.Rendering.Composition.Compositor.RequestCompositionUpdate(System.Action)
  4. ilAddr is 0000022DC65AE2D4 pImport is 00000238EE6FECA0
  5. Begin 00007FF88DA3CA20, size 96
  6. ...
  7. 00007ff8`8da3ca9b 488bce          mov     rcx,rsi
  8. 00007ff8`8da3ca9e e8cdeaa5fe      call    00007ff8`8c49b570 (Avalonia.Rendering.Composition.Compositor.RequestCompositionBatchCommitAsync(), mdToken: 00000000060009D9)
  9. >>> 00007ff8`8da3caa3 488b4008        mov     rax,qword ptr [rax+8]
  10. 00007ff8`8da3caa7 8b4008          mov     eax,dword ptr [rax+8]
  11. ...
  12. 0:000> dt coreclr!g_SavedExceptionInfo 00007ff8eb86c5a0
  13.    +0x000 m_ExceptionRecord : _EXCEPTION_RECORD
  14.    +0x0a0 m_ExceptionContext : _CONTEXT
  15.    +0x570 m_Crst           : CrstStatic
  16. 0:000> dx -r1 (*((coreclr!_CONTEXT *)0x7ff8eb86c640))
  17. ...
  18.     [+0x078] Rax              : 0x0 [Type: unsigned __int64]
  19. ...
复制代码
从卦中的汇编代码看,崩溃的缘故起因是Avalonia 框架的 RequestCompositionBatchCommitAsync 返回 null 导致的,即 rax=0,这个 Avalonia 不就是那个跨平台的WPF吗,有点意思了,接下来到源码中确认下到底是什么变量。

从代码逻辑上看 _nextCommit 是一个类变量而不是方法局部变量,在并发较高的情况下假如有其他方法将_nextCommit=null的话确实存在这种情况,为了验证想法在类中搜索,真的有方法会设置 null,截图如下:

到这里基本就搞清楚了,这是 Avalonia 的一个bug,末了我们看下 Avalonia 的版本,发现这个版本是非常新的,输出如下:
  1. 0:000> lmvm Avalonia_Base
  2.     ...
  3.     Timestamp:        A0BE2821 (This is a reproducible build file hash, not a timestamp)
  4.     CheckSum:         001CDA05
  5.     ImageSize:        001D4000
  6.     File version:     11.1.0.0
  7.     Product version:  11.1.0.0
  8.     File flags:       0 (Mask 3F)
  9.     File OS:          4 Unknown Win32
  10.     File type:        2.0 Dll
  11.     File date:        00000000.00000000
  12.     Translations:     0000.04b0
  13.     Information from resource tables:
  14.         CompanyName:      Avalonia Team
  15.         ProductName:      Avalonia
  16.         InternalName:     Avalonia.Base.dll
  17.         OriginalFilename: Avalonia.Base.dll
  18.         ProductVersion:   11.1.0+2a8ea17985fd739234fa0d93c3437948535d35c4
  19.         FileVersion:      11.1.0.0
  20.         FileDescription:  Avalonia.Base
  21.         LegalCopyright:   Copyright 2013-2024 © The AvaloniaUI Project
复制代码
4. 如何解决呢

知道了这是 Avalonia 的bug,并且 Avalonia 也是非常新的版本,升级这条路就堵死了,只能提交个issue 给官方:https://github.com/AvaloniaUI/Avalonia 来解决吧。
三:总结

这次生产事故挖了点新东西,有点好奇的是如今工控行业也开始用 Avalonia 替代 WPF 了吗? 不过现阶段稳定性和 WPF 是没法比的,等待将来更结实的版本吧。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

天空闲话

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表