一:背景

1. 讲故事

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

二:WinDbg 分析

1. 为什么会崩溃

windbg 有一个强大之处就是带有一个自动化的分析命令 !analyze -v 可以帮助我们快速的分析,输出如下:


0:000> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
******************************************************************************* CONTEXT: (.ecxr)
rax=00007ff95c5a9877 rbx=00007ff959d6d8e0 rcx=0000000000000000
rdx=0000000000000000 rsi=000000e394b98de0 rdi=000000e394b99530
rip=00007ff959c7b699 rsp=000000e394b99510 rbp=000000e394b99d00
r8=0000000000000000 r9=0000000000000007 r10=0000000000000000
r11=0000000000000000 r12=0000022da11451d0 r13=0000000000000000
r14=000000e394b9a9e0 r15=0000000000040ae4
iopl=0 nv up ei pl nz na pe nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000200
KERNELBASE!RaiseException+0x69:
00007ff9`59c7b699 0f1f440000 nop dword ptr [rax+rax]
Resetting default scope EXCEPTION_RECORD: (.exr -1)
ExceptionAddress: 00007ff959c7b699 (KERNELBASE!RaiseException+0x0000000000000069)
ExceptionCode: c000041d
ExceptionFlags: 00000001
NumberParameters: 0 PROCESS_NAME: xxx.Desktop.dll ERROR_CODE: (NTSTATUS) 0xc000041d - <Unable to get error code text> EXCEPTION_CODE_STR: c000041d
...

从卦中可以看到当前的崩溃码是 c000041d,即 An unhandled exception was encountered during a user callback,这个异常码是个统称异常,言外之意就是内部还藏有真实的异常码,那真实的异常码是多少呢?

2. 真实的异常码在哪里

要想知道这个答案,可以切到异常上下文找到 RaiseException 的父函数在图观察,输出如下:


0:000> k 5
# Child-SP RetAddr Call Site
00 000000e3`94b99510 00007ff8`eb52cb19 KERNELBASE!RaiseException+0x69
01 000000e3`94b995f0 00007ff8`eb52cb4b coreclr!NakedThrowHelper2+0x9
02 000000e3`94b99620 00007ff8`eb52cb55 coreclr!NakedThrowHelper_RspAligned+0x1e
03 000000e3`94b99b48 00007ff8`8da3caa3 coreclr!NakedThrowHelper_FixRsp+0x5
04 000000e3`94b99b50 00007ff8`8d5a5e23 Avalonia_Base!Avalonia.Rendering.Composition.Compositor.RequestCompositionUpdate+0x83 0:000> ub 00007ff8`eb52cb19
...
00007ff8`eb52cb14 e857910b00 call coreclr!LinkFrameAndThrow (00007ff8`eb5e5c70) 0:000> uf coreclr!LinkFrameAndThrow
Flow analysis was incomplete, some code may be missing
coreclr!LinkFrameAndThrow [D:\a\_work\1\s\src\coreclr\vm\excep.cpp @ 6934]:
6934 00007ff8`eb5e5c70 4053 push rbx
6934 00007ff8`eb5e5c72 4883ec20 sub rsp,20h
6937 00007ff8`eb5e5c76 488d05bb771f00 lea rax,[coreclr!FaultingExceptionFrame::`vftable' (00007ff8`eb7dd438)]
...
6949 00007ff8`eb5e5cea 448b05c7682800 mov r8d,dword ptr [coreclr!g_SavedExceptionInfo+0x18 (00007ff8`eb86c5b8)]
6949 00007ff8`eb5e5cf1 8b15ad682800 mov edx,dword ptr [coreclr!g_SavedExceptionInfo+0x4 (00007ff8`eb86c5a4)]
6949 00007ff8`eb5e5cf7 8b0da3682800 mov ecx,dword ptr [coreclr!g_SavedExceptionInfo (00007ff8`eb86c5a0)]
6950 00007ff8`eb5e5cfd 4883c420 add rsp,20h
6950 00007ff8`eb5e5d01 5b pop rbx
6949 00007ff8`eb5e5d02 48ff2537581b00 jmp qword ptr [coreclr!_imp_RaiseException (00007ff8`eb79b540)] Branch
...

从卦中可以看到 RaiseException 的参数来自于异常信息全局变量 g_SavedExceptionInfo,这个变量中存放着当前崩溃的真实上下文以及寄存器信息,在 CLR 中的数据结构如下:


struct SavedExceptionInfo
{
EXCEPTION_RECORD m_ExceptionRecord;
CONTEXT m_ExceptionContext;
CrstStatic m_Crst;
}

有了这些之后接下来就可以用 dt 来挖了,输出如下:


0:000> dt coreclr!g_SavedExceptionInfo 00007ff8eb86c5a0
+0x000 m_ExceptionRecord : _EXCEPTION_RECORD
+0x0a0 m_ExceptionContext : _CONTEXT
+0x570 m_Crst : CrstStatic 0:000> dx -r1 (*((coreclr!_EXCEPTION_RECORD *)0x7ff8eb86c5a0))
(*((coreclr!_EXCEPTION_RECORD *)0x7ff8eb86c5a0)) [Type: _EXCEPTION_RECORD]
[+0x000] ExceptionCode : 0xc0000005 [Type: unsigned long]
[+0x004] ExceptionFlags : 0x0 [Type: unsigned long]
[+0x008] ExceptionRecord : 0x0 [Type: _EXCEPTION_RECORD *]
[+0x010] ExceptionAddress : 0x7ff88da3caa3 [Type: void *]
[+0x018] NumberParameters : 0x2 [Type: unsigned long]
[+0x020] ExceptionInformation [Type: unsigned __int64 [15]]

从卦中信息来看当前崩溃的真正原因是 0xc0000005,即 访问违例,同时还记录了崩溃的那个点 RIP=0x7ff88da3caa3

3. 什么逻辑导致的崩溃

这个比较简单,用 !Uuf 都可以试下,输出如下:


0:000> !U 0x7ff88da3caa3
Normal JIT generated code
Avalonia.Rendering.Composition.Compositor.RequestCompositionUpdate(System.Action)
ilAddr is 0000022DC65AE2D4 pImport is 00000238EE6FECA0
Begin 00007FF88DA3CA20, size 96
...
00007ff8`8da3ca9b 488bce mov rcx,rsi
00007ff8`8da3ca9e e8cdeaa5fe call 00007ff8`8c49b570 (Avalonia.Rendering.Composition.Compositor.RequestCompositionBatchCommitAsync(), mdToken: 00000000060009D9)
>>> 00007ff8`8da3caa3 488b4008 mov rax,qword ptr [rax+8]
00007ff8`8da3caa7 8b4008 mov eax,dword ptr [rax+8]
... 0:000> dt coreclr!g_SavedExceptionInfo 00007ff8eb86c5a0
+0x000 m_ExceptionRecord : _EXCEPTION_RECORD
+0x0a0 m_ExceptionContext : _CONTEXT
+0x570 m_Crst : CrstStatic 0:000> dx -r1 (*((coreclr!_CONTEXT *)0x7ff8eb86c640))
...
[+0x078] Rax : 0x0 [Type: unsigned __int64]
...

从卦中的汇编代码看,崩溃的原因是Avalonia 框架的 RequestCompositionBatchCommitAsync 返回 null 导致的,即 rax=0,这个 Avalonia 不就是那个跨平台的WPF吗,有点意思了,接下来到源码中确认下到底是什么变量。

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

到这里基本就搞清楚了,这是 Avalonia 的一个bug,最后我们看下 Avalonia 的版本,发现这个版本是非常新的,输出如下:


0:000> lmvm Avalonia_Base
...
Timestamp: A0BE2821 (This is a reproducible build file hash, not a timestamp)
CheckSum: 001CDA05
ImageSize: 001D4000
File version: 11.1.0.0
Product version: 11.1.0.0
File flags: 0 (Mask 3F)
File OS: 4 Unknown Win32
File type: 2.0 Dll
File date: 00000000.00000000
Translations: 0000.04b0
Information from resource tables:
CompanyName: Avalonia Team
ProductName: Avalonia
InternalName: Avalonia.Base.dll
OriginalFilename: Avalonia.Base.dll
ProductVersion: 11.1.0+2a8ea17985fd739234fa0d93c3437948535d35c4
FileVersion: 11.1.0.0
FileDescription: Avalonia.Base
LegalCopyright: Copyright 2013-2024 The AvaloniaUI Project

4. 如何解决呢

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

三:总结

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

记一次 .NET某环境监测系统 崩溃分析的更多相关文章

  1. 记一次 .NET 某企业 ERP网站系统 崩溃分析

    一:背景 1. 讲故事 前段时间收到了一个朋友的求助,说他的ERP网站系统会出现偶发性崩溃,找了好久也没找到是什么原因,让我帮忙看下,其实崩溃好说,用 procdump 自动抓一个就好,拿到 dump ...

  2. 记一次 .NET 某医疗住院系统 崩溃分析

    一:背景 1. 讲故事 最近收到了两起程序崩溃的dump,查了下都是经典的 double free 造成的,蛮有意思,这里就抽一篇出来分享一下经验供后面的学习者避坑吧. 二:WinDbg 分析 1. ...

  3. 记一次 Windows10 内存压缩模块 崩溃分析

    一:背景 1. 讲故事 在给各位朋友免费分析 .NET程序 各种故障的同时,往往也会收到各种其他类型的dump,比如:Windows 崩溃,C++ 崩溃,Mono 崩溃,真的是啥都有,由于基础知识的相 ...

  4. 记一次 .NET 某设备监控系统 死锁分析

    一:背景 1. 讲故事 上周看了一位训练营朋友的dump,据朋友说他的程序卡死了,看完之后发现是一例经典的死锁问题,蛮有意思,这个案例算是学习 .NET高级调试 入门级的案例,这里和大家分享一下. 二 ...

  5. 记一次系统崩溃事件【Mac版】

    事件:Mac系统崩溃,导致电脑数据丢失,以及数据安全备份措施的不到位的教训! 解决措施: 1.开机后按:Command+R 按开机键 ,进入Mac 实用工具, 选择磁盘工具.由于没有备份直接抹掉磁盘. ...

  6. linux Kernell crash dump------kdump 的安装设置+Linux系统崩溃的修复解决过程+mysql+kvm

    http://www.ibm.com/developerworks/cn/linux/l-cn-dumpanalyse/https://www.kernel.org/pub/linux/utils/k ...

  7. RHEL6误安装RHEL7的包导致glibc被升级后系统崩溃处理方法

    RHEL6误使用了RHEL7的光盘源,安装了某个RPM包之后,导致glibc被升级,进而导致系统崩溃.   [root@rhel65 ~]# yum install ftp Loaded plugin ...

  8. Windows系统崩溃后快速恢复Oracle数据库的妙招

    Windows系统崩溃后快速恢复Oracle数据库,以下是操作步骤 假设oracle数据安装在d:\\oracle文件夹中,数据库名称orcl 1>将崩溃的数据库安装目录"d:\\or ...

  9. 如何解决ChemDraw引起的系统崩溃

    运行ChemDraw应用程序时,一般不会引起系统崩溃,但在使用CS software产品可能会引发计算机崩溃.为了方便广大用户的使用,本教程将教授大家如何解决ChemDraw运行中引起的系统崩溃. 当 ...

  10. 内存转储文件调试系统崩溃bug

    百度百科:内存转储文件 内存转储是用于系统崩溃时,将内存中的数据转储保存在转储文件中,供给有关人员进行排错分析用途.而它所保存生成的文件就叫做内存转储文件. 内存转储文件也被称作虚拟内存,它是用硬盘里 ...

随机推荐

  1. SpringMVC原理(1)-文件上传请求

    我们文件上传接口只需要在方法参数上写MultipartFile类,mvc就可以帮我们把上传的文件封装为这个类的对 象供我们非常方便的操作,那它是怎么做的呢?我们一起来看看 我们发的请求默认都是由Dis ...

  2. tcp_tw_reuse、tcp_tw_recycle、tcp_fin_timeout参数介绍

    参数介绍 net.ipv4.tcp_tw_reuse = 1 表示开启重用.允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭: net.ipv4.tcp_tw_rec ...

  3. 3562-Qt工程编译说明

  4. 【资料分享】基于TI Sitara系列AM3352/AM3354/AM3359核心板规格书

    1 核心板简介 创龙科技SOM-TL335x-S是一款基于TI Sitara系列AM3352/AM3354/AM3359 ARM Cortex-A8高性能低功耗处理器设计的低成本工业级核心板,通过邮票 ...

  5. 贝塞尔曲线原理、推导及Matlab实现

    贝塞尔曲线原理.推导及Matlab实现 贝塞尔曲线原理.推导及Matlab实现 一.简介 贝塞尔曲线提出 在数学的数值分析领域中,贝塞尔曲线(English:Bézier curve)是计算机图形学中 ...

  6. GUI测试稳定性的关键技术

    标签(空格分隔): GUI测试稳定性 GUI测试稳定性的关键技术 GUI 自动化测试稳定性,最典型的表现形式就是,同样的测试用例在同样的环境上,时而测试通过,时而测试失败. 这也是影响 GUI 测试健 ...

  7. redis-sort排序

    sort命令可以对列表(lpush rpush).集合(sadd srem).有序集合(zadd)进行排序 1.复习一下三种数据结构(1)列表 lpush  key val1 val2 val3 .. ...

  8. IDEA之调试或运行的小助手日志插件Grep Console:不同颜色区分,查看日志看方便【工欲善其事必先利其器】

    简介 Grep Console是一款方便开发者对idea控制台输出日志进行个性化管理的插件.这个插件还是很用的,在满屏的日志中,迅速找到自己关注的内容,调试程序的绝佳工具. 功能特性 Grep Con ...

  9. [oeasy]python0110 屏幕点阵字体_3x5_5x7_雅达利字库

    动视 桥牌 想用 7 x 5 描述黑红梅方 还是比较难的     ​   添加图片注释,不超过 140 字(可选)   而且最下面的动视logo是 修改后的字体 还挺有动感   这个时代 图像库 和 ...

  10. oeasy教您玩转vim - 63 - # window分屏

    ​ 窗口window 回忆上次 我们这次了解了缓冲区buffer ls可以查看buffer 如下是buffer缓冲的一些flag + 有修改未保存内容 - 可修改标签关闭 = 只读缓冲区 a 活跃缓冲 ...