一:背景

1. 讲故事

上个月在社区写的文章比较少,一直关注的朋友应该知道那段时间被狗咬了以及一些琐事处理,所以手头上也攒了不少需要分享的案例,这段时间比较空闲,逐个给大

家做个分享吧,刚好年后为新版的 .NET高级调试训练营 做案例储备,所以抓紧时间疯狂输出吧!

这次生产事故的dump是训练营里一位朋友给到我的,由于朋友没有分析出来,让我帮忙看看,毕竟我的修车经验相对来说更丰富一些,算是他们背后坚实的保障吧,话不多说上windbg说话。

二:WinDbg分析

1. 为什么会崩溃

由于windbg默认自动定位到崩溃的线程,而崩溃的dump重点是观察它的崩溃前上下文,这里使用 .ecxrk 命令,输出参考如下:


0:000> .ecxr
eax=00000000 ebx=4d6d8360 ecx=00000003 edx=00000000 esi=4d6f0ca0 edi=4d6f0c74
eip=71a567c7 esp=026fd834 ebp=026fd83c iopl=0 nv up di pl nz na po nc
cs=0000 ss=0000 ds=0000 es=0000 fs=0000 gs=0000 efl=00000000
System_Windows_Forms_ni!System.Windows.Forms.ImageList.ImageCollection.SetKeyName+0x1b:
71a567c7 cc int 3 0:000> k
*** Stack trace for last set context - .thread/.cxr resets it
# ChildEBP RetAddr
00 026fd83c 0c2c4e7e System_Windows_Forms_ni!System.Windows.Forms.ImageList.ImageCollection.SetKeyName+0x1b
01 026fe474 0c2c063b xxx!xxx.MainForm.InitializeComponent+0x198e
02 026fe488 095cb9de xxx!xxx.MainForm..ctor+0x5fb
03 026fe4e4 0da5bc7a xxx!xxx.LoginForm.button_OK_Click+0x52e
04 026fe4f8 71a38bdf xxx!xxx.LoginForm.LoginForm_Load+0x9a
05 026fe528 710b325a System_Windows_Forms_ni!System.Windows.Forms.Form.OnLoad+0x2f
...

从卦象看是崩溃在 System.Windows.Forms.ImageList.ImageCollection.SetKeyName 方法上,很显然这个方法属于微软的SDK底层库,不管怎么说是一个托管异常,既然是托管异常我们可以用 !t 观察到具体的崩溃信息。


0:000> !t
ThreadCount: 26
UnstartedThread: 0
BackgroundThread: 12
PendingThread: 0
DeadThread: 13
Hosted Runtime: no
Lock
ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 1 534 0299e700 a6028 Preemptive 4D6F0EFC:00000000 02997bd0 0 STA System.IndexOutOfRangeException 4d6f0ca0
2 2 5b4 029af278 2b228 Preemptive 00000000:00000000 02997bd0 0 MTA (Finalizer)
3 6 ff0 02a60eb0 102a228 Preemptive 00000000:00000000 02997bd0 0 MTA (Threadpool Worker)
... 0:000> !pe
Exception object: 4d6f0ca0
Exception type: System.IndexOutOfRangeException
Message: Index was outside the bounds of the array.
InnerException: <none>
StackTrace (generated):
SP IP Function
026FD834 71A567C7 System_Windows_Forms_ni!System.Windows.Forms.ImageList+ImageCollection.SetKeyName(Int32, System.String)+0x33e157 StackTraceString: <none>
HResult: 80131508

从卦象看非常奇怪,怎么底层库中抛了一个数组索引越界异常?难道是底层的bug?一般来说这些代码都是铜墙铁壁,固若金汤,坚如磐石,稳如泰山,无懈可击,不可能有如此低级的bug。。。

2. 真的是底层库bug吗?

要想寻找答案,可以根据线程栈上的函数寻找底层源码,从源码上寻找答案,修剪后的代码如下:


private void InitializeComponent()
{
this.imageList_btnbg.ImageStream = (System.Windows.Forms.ImageListStreamer)resources.GetObject("imageList_btnbg.ImageStream");
this.imageList_btnbg.TransparentColor = System.Drawing.Color.Transparent;
this.imageList_btnbg.Images.SetKeyName(0, "normal-main.bmp");
this.imageList_btnbg.Images.SetKeyName(1, "focus-main.bmp");
this.imageList_btnbg.Images.SetKeyName(2, "select-main.bmp");
this.imageList_btnbg.Images.SetKeyName(3, "gray-main.bmp");
this.imageList_btnbg.Images.SetKeyName(4, "down_1.png");
this.imageList_btnbg.Images.SetKeyName(5, "down_2.png");
this.imageList_btnbg.Images.SetKeyName(6, "down_3.png");
this.imageList_btnbg.Images.SetKeyName(7, "up_1.png");
this.imageList_btnbg.Images.SetKeyName(8, "up_2.png");
this.imageList_btnbg.Images.SetKeyName(9, "up_3.png");
} public void SetKeyName(int index, string name)
{
if (!IsValidIndex(index))
{
throw new IndexOutOfRangeException();
}
if (imageInfoCollection[index] == null)
{
imageInfoCollection[index] = new ImageInfo();
} ((ImageInfo)imageInfoCollection[index]).Name = name;
} private bool IsValidIndex(int index)
{
if (index >= 0)
{
return index < Count;
}
return false;
} public int Count
{
get
{
if (owner.HandleCreated)
{
return SafeNativeMethods.ImageList_GetImageCount(new HandleRef(owner, owner.Handle));
}
int num = 0;
foreach (Original original in owner.originals)
{
if (original != null)
{
num += original.nImages;
}
}
return num;
}
} [Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRDescription("ImageListHandleCreatedDescr")]
public bool HandleCreated => nativeImageList != null;

仔细通读卦中的代码逻辑,看样子是 IsValidIndex()=false 导致的手工 IndexOutOfRangeException 异常,而 IsValidIndex()=false 是由于 index < Count 的条件成立,后面的 Count 是取自 ImageList_GetImageCount 或者 owner.originals 值。

代码逻辑我们是分析清楚了,接下来就是看汇编来分析下这个dump的现状,入手点就是从 index 值入手,即对 InitializeComponent() 方法进行反汇编。


0:000> !clrstack
OS Thread Id: 0x534 (0)
Child SP IP Call Site
026fd784 771316bc [HelperMethodFrame: 026fd784]
026fd834 71a567c7 System.Windows.Forms.ImageList+ImageCollection.SetKeyName(Int32, System.String)
026fd848 0c2c4e7e xxx.MainForm.InitializeComponent()
026fe47c 0c2c063b xxx.MainForm..ctor()
026fe490 095cb9de xxx.LoginForm.button_OK_Click(System.Object, System.EventArgs)
... 0:000> !U /d 0c2c4e7e
Normal JIT generated code
xxx.MainForm.InitializeComponent()
Begin 0c2c34f0, size 5ded
...
0c2c4e62 e8d9b5d864 call System_Windows_Forms_ni+0x160440 (71050440) (System.Windows.Forms.ImageList.get_Images(), mdToken: 06002599)
0c2c4e67 898514f5ffff mov dword ptr [ebp-0AECh],eax
0c2c4e6d ff35f0a07f05 push dword ptr ds:[57FA0F0h] ("normal-main.bmp")
0c2c4e73 8bc8 mov ecx,eax
0c2c4e75 33d2 xor edx,edx
0c2c4e77 3909 cmp dword ptr [ecx],ecx
0c2c4e79 e8f2374565 call System_Windows_Forms_ni!System.Windows.Forms.ImageList.ImageCollection.SetKeyName (71718670)
>>> 0c2c4e7e 8b8e74020000 mov ecx,dword ptr [esi+274h]
...

从卦象看,尼玛。。。执行第一个 SetKeyName(0, "normal-main.bmp"); 就异常啦,这就说明那个 Count=0,无语了,为什么 Count=0 呢? 接下来寻找Count数据源ImageCollection 集合,可以从线程栈中寻找,使用 !dso 命令即可。

0:000> !dso
OS Thread Id: 0x534 (0)
ESP/REG Object Name
026FD774 4d6f0c74 System.Windows.Forms.ImageList+ImageCollection
026FD778 4d6f0ca0 System.IndexOutOfRangeException
... 0:000> !do 4d6f0c74
Name: System.Windows.Forms.ImageList+ImageCollection
MethodTable: 71120ff0
EEClass: 70f230ec
Size: 20(0x14) bytes
File: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll
Fields:
MT Field Offset Type VT Attr Value Name
7111ecc0 4003916 4 ...s.Forms.ImageList 0 instance 4d6d97b0 owner
72d909dc 4003917 8 ...ections.ArrayList 0 instance 4d6f0c88 imageInfoCollection
72d8df5c 4003918 c System.Int32 1 instance -1 lastAccessedIndex
0:000> !DumpObj /d 4d6d97b0
Name: System.Windows.Forms.ImageList
...
Fields:
MT Field Offset Type VT Attr Value Name
71121b0c 4001013 10 ...t+NativeImageList 0 instance 4d6f0c40 nativeImageList
728e15a0 4001019 1c ...Collections.IList 0 instance 00000000 originals

根据卦中的 nativeImageListoriginals 再配合源代码,应该就是祸首 SafeNativeMethods.ImageList_GetImageCount 方法返回 0 导致的,先观察一下它的签名。


[DllImport("comctl32.dll")]
public static extern int ImageList_GetImageCount(HandleRef himl);

从签名看这是C++写的外部方法,这就沃草了。。。我总不能用 ida 去捋这里面的逻辑吧。。。到这里貌似已经快要撞到南墙了。。。有点慌了。

3. 天要绝人之路吗

经过短暂的恍恍惚惚之后,我突然灵光一现,尼玛这是32bit的内存地址,是不是2G的空间不够用哦?刚好 ImageList_GetImageCount 是一个关于图片的UI控件,用了底层的COM资源,会不会真的是空间不足导致的?有了这个想法之后赶紧 !address -summary 观察提交内存。


0:000> !address -summary
...
--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_COMMIT 1933 6e768000 ( 1.726 GB) 93.52% 86.30%
MEM_FREE 631 9e01000 ( 158.004 MB) 7.72%
MEM_RESERVE 607 7a87000 ( 122.527 MB) 6.48% 5.98%
...

尼玛。。。卦象中的 MEM_COMMIT=1.72G, %ofBusy= 93.52% 早已超过了1.2G的临界值,终于真相大白。。。

解决办法就比较简单了,开启大地址,让程序吃 4G 的内存,后来朋友反馈这个问题已不再出来。。。

三:总结

分析完这个dump之后其实我挺感慨的,人生也如此dump一样,在真相和假象之间不断的交织穿梭,有些人走出来了,有些人永远留在了里面。。。

记一次 .NET某汗液测试机系统 崩溃分析的更多相关文章

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

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

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

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

  3. 记一次 .NET 某医疗器械 程序崩溃分析

    一:背景 1.讲故事 前段时间有位朋友在微信上找到我,说他的程序偶发性崩溃,让我帮忙看下怎么回事,上面给的压力比较大,对于这种偶发性崩溃,比较好的办法就是利用 AEDebug 在程序崩溃的时候自动抽一 ...

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

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

  5. 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 ...

  6. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(34)-文章发布系统①-简要分析

    原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(34)-文章发布系统①-简要分析 系列目录 最新比较闲,为了学习下Android的开发构建ASP.NET ...

  7. Tomcat系统架构分析

    Tomcat系统架构分析 关于这边blog呢,实际开发中并不会用到,但是我觉得还是很有必要认真的写一下.毕竟我们每天在本地撸码的时候使用的就是tomcat来做web服务器.一个常识就是说我们本地在to ...

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

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

  9. xx系统属性分析

    在本周的课程学习当中,我们简单了解到系统的一些属性,同时在课下也对<大型网站技术架构:核心原理与案例分析>进行了初步的阅读. 在书籍中我看到了许多其他的知识,也对课堂学习的知识有了巩固,现 ...

  10. Linux系统IO分析工具之iotstat常用参数介绍

    Linux系统IO分析工具之iotstat常用参数介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 1>.安装iostat [root@flume115 ~]# yum - ...

随机推荐

  1. HDFS 重要机制之 checkpoint

    核心概念 hdfs checkpoint 机制对于 namenode 元数据的保护至关重要, 是否正常完成检查点是评估 hdfs 集群健康度和风险的重要指标 editslog : 对 hdfs 操作的 ...

  2. C语言实战项目——学生试卷分数统计

    1.题目要求 作为教师,考试以后对试卷进行分析和研究是必须做的一项工作,假定某学校要求老师在考试之后填写的一个表格,并要求教师根据考试分数分布情况画出直方图.下面就来解决这个实际问题. 2.题目分析 ...

  3. STM32单片机 32.768Khz和8Khz晶振外部电容容值选取问题

    一.前言 绘制STM32C8T6最小系统的晶振电路的时候,看到了并联在晶振两端的电容,就好奇具体的容值该怎么选取,故有了这篇博客. 二.外部晶振电路

  4. 计算机网络基础/进制转换/企业级子网IP划分

    数制的含义 数制:计数的方法,指用一组固定的符号和统一的规则来表示数值的方法 数位:指数字符号在一个数中所处的位置 基数:指在某种进位计数制中,数位上所能使用的数字符号的个数 位权:指在某种进位计数制 ...

  5. Cmocka 单元测试配置与使用

    0. 前言 在 Windows 环境下配置 Cmocka 单元测试,并使用该框架进行单元测试. 1. Cmocka介绍 Cmocka 是一个 C 单元测试框架,支持 mock objects(打桩). ...

  6. Docker镜像管理之Harbor

    github: https://github.com/goharbor/harbor 官网:https://goharbor.io/docs/2.5.0/ [安装] 1. 查看是否达到安装条件 2.根 ...

  7. word 文档签章控件生成的签章批量删除

    某个签章工具的word插件缺少批量插入签章的功能.同时,发现在投标工具中可以使用导出生成pdf时批量签章的功能.现在需要移除先前手动操作生成的多个签章,有如下发现-- 1.对少量签章,可以先选中签章右 ...

  8. 销讯通-CRM系统的功能远远不止于用来打卡

    在信息化的过程中,CRM系统其实很多企业都在用,最开始的设想是很好的,大家用着之后发现它可能最终只会沦为一个上班打卡考核或者是最基础的一个签到工具了,没有发挥它应有的一个功能. 最基础的一个诉求 我们 ...

  9. 使用ProWindow时,控制按钮状态的说明

    在Pro SDK中,提供了一个默认的窗口基类,ProWindow Class,提供了基础的窗体样式,可供扩展和调用. 有网友问我,在使用时,会发现窗体右上角的控制按钮,有时会没有按照自己的预期显示. ...

  10. 二进制安装Kubernetes(k8s)v1.32.0

    二进制安装Kubernetes(k8s)v1.32.0 介绍 https://github.com/cby-chen/Kubernetes 开源不易,帮忙点个star,谢谢了 kubernetes(k ...