一:背景

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. Johnson全源最短路:负权化正权,最后减去势能差

    Johnson全源最短路:负权化正权,最后减去势能差 (1)建虚点0,add(0,i,0),跑st=0的单源最短路hi (2)e[i].w+=h[u]-h[v] ​ Q:为何这样不会得到错误答案? ​ ...

  2. python之执行shell命令

    python 执行shell命令,且执行完后将shell端的输出返回 subprocess import subprocess # 要执行的命令 command = "ls -lrt&quo ...

  3. CAD Plus 移动端使用帮助

    Mac端使用帮助 English help 如果您有疑问或需要帮助请发送邮件至 3167292926@qq.com 1. 权限要求 1.1 获取位置信息 使用文件管理功能时显示网络信息需要获取位置信息 ...

  4. Edge(Chrome)浏览器插件WordSaver的演示,可以查英语生词并记录生词及所在句子,导出为anki格式

    Edge(Chrome)浏览器插件WordSaver的演示,可以查英语生词并记录生词及所在句子,导出为anki格式 Bilibili哔哩哔哩视频

  5. delphi12 Android Edit SDK安装

    安装 delphi 12.1 后,编译 FMX Android 程序失败! 查找原因,SDK配置全是叹号! 之前用过SDK Manager.exe,直接打开即可以选择下载,但现在发现没有了,查找资料如 ...

  6. 高性能计算-gemm串行计算优化(3)

    目标:Darknet 源码cpu矩阵乘法函数 gemm_nn 优化.参数说明:lda A的列数; ldb B的列数; ldc C的列数; M C的行数; K A的列数 测试方法:Darknet源码,m ...

  7. python通过实例方法名字的字符串调用方法

    目录 方式1 - 反射 hasattr 方法 判断当前实例中是否有着字符串能映射到的属性或者方法, 一般会在 getattr 之前作为判断防止报错 getattr 方法 获取到当前实例中传入字符串映射 ...

  8. golang之项目部署

    介绍 Go 语言可以使用内置命令行工具 go build 编译生成可执行文件.自 Go1.5 版本开始实现自举后,交叉编译也很方便,只需使用 GOOS.GOARCH 环境变量指定目标平台和架构. 部署 ...

  9. python常用模块汇总

    os模块 os.remove() 删除文件 os.unlink() 删除文件 os.rename() 重命名文件 os.listdir() 列出指定目录下所有文件 os.chdir() 改变当前工作目 ...

  10. 使用 yum 安装 nginx

    rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm ...