昨晚介绍了
仍使用昨天的方法,重现问题,仍然是百发百中,而且仍然是崩溃到相同的地方,也就是System_Windows_Forms_ni模块的如下方法:
System.Windows.Forms.PropertyGridInternal.PropertyGridView+GridViewEdit.ProcessDialogKey(System.Windows.Forms.Keys)
.Net下的名字都很长,我们取ProcessDialogKey的首字母将其简称为PDK方法。
从汇编角度看,崩溃在如下地址的MOV指令:
7b1a3ea3 8b01 mov eax,dword ptr [ecx] ds:002b:00000000=????????
翻译为C/C++,这条指令相当于
a = * this
也就是使用指针的时候,指针的目标地址为空。
使用SOS的ip2md命令可以找到这条指令的方法描述(MD)
0:000> !ip2md @eip
MethodDesc: 7a844330
把MD的地址交给SOS的反汇编命令,可以得到这个方法的完整汇编清单。刚好一个屏幕可以显示到崩溃的位置。
感谢设计x86汇编的前辈们为我们贡献了如此通俗易懂的机器语言。
注意崩溃点附近的指令,即:
7b1a3e96 ff15fc8d747a call dword ptr [System_Windows_Forms_ni+0x38dfc (7a748dfc)] (System.Windows.Forms.PropertyGridInternal.PropertyGridView.get_SelectedGridEntry(), mdToken: 06004edb)
7b1a3e9c 8bc8 mov ecx,eax
7b1a3e9e ba05000000 mov edx,5
7b1a3ea3 8b01 mov eax,dword ptr [ecx]
上面的call指令是调用get_SelectedGridEntry()方法,第二条是把返回值赋给ecx,接下来的edx = 5 暂时无关,而后便是崩溃的引用空指针指令。
天啊,居然如此的没有任何悬念,如果用C++表示,那么上述指令相当于:
pEntry = xxGridView.get_SelectedGridEntry();
pEntry->xxxMethod();
就是这样一次调用,没有判断返回值。
尊敬的各位读者和同行们,这个错误是不是有点LOW呢?关于是否一定要判断返回值是有些灵活性的,但是这样的get_SelectedGridEntry太有可能返回空了吧,所以这里不加个判断,你说应该么?写到这里挺想把这个函数的实现和老版本比较一下,看看这个代码有多久了。按理说应该是不长时间的,不然这样的问题不知触发了多少个爆炸啊!
且慢,我们还是拿出这个方法的完整IL看一下吧。还是一个屏幕刚好显示到崩溃的地方。
截取崩溃点附近的IL:
IL_0040: ldarg.0
IL_0041: ldfld GridViewEdit::psheet
IL_0046: callvirt System.Windows.Forms.PropertyGridInternal.Proper::get_SelectedGridEntry
IL_004b: callvirt System.Windows.Forms.PropertyGridInternal.GridEn::OnValueReturnKey
两个callvirt连在一起,也是真真切切的没有判断返回值。
看来问题很确定了,就是在这个PDK方法里,调用get_SelectedGridEntry获取选中项后,没有预料到它会返回空。
不过,也有人可能会说,在编辑的情况下,get_SelectedGridEntry是不应该返回空的。
在笔者试图跟踪get_SelectedGridEntry函数时,发现在出问题的0号线程中发生了一串异常:
(72a8.855c): C++ EH exception - code e06d7363 (first chance)
(72a8.855c): CLR exception - code e0434352 (first chance)
(72a8.855c): Unknown exception - code 000006ba (first chance)
而且,在执行到上面分析的崩溃点之前,还发生了依次非法访问:
出问题的方法是:
System.Windows.Forms.PropertyGrid.OnPropertyValueSet
执行经过是:
从前面的IL代码来看,PDK方法是先调用UnfocusSelection,再调用get_SelectedGridEntry和OnValueReturnKey,看来是调用UnfocusSelection时就埋下了隐患,导致后来的get_SelectedGridEntry返回空,这是可能的原因,不能是bug的理由(^_^)。
上了调试器后,还发现,进程中有很多C++异常。
天啊,微软的工程师都不用WinDBG了么?
开玩笑,当然不是,认识好几位WinDBG高手在微软工作呢。但是从今天观察到的现象来看,至少.Net团队的一些工程师不常用windbg啊。
概而言之,问题还不只一个,只不过压死骆驼的最后一根稻草是没有判断get_SelectedGridEntry的返回值。
上一篇文章发出后,不少朋友说微软已经很少有测试设计工程师(SDET)了。看来微软也是受“新测试方法论”影响的。看到其它很多公司没有专门的测试工程师,“我为什么不节约成本呢?”最近仔细学习了很多关于莱布尼茨的资料,现在不由得想起他说的一句话:不发生作用的东西是不会存在的。每个人都在影响别人,也在被影响。
那么本文所讨论的如此明显的BUG是否与取消SDET有关呢?我认为是有的。至少测试的投入少了啊。《大学》里有句经典的话:“其所厚者薄,而其所薄者厚,未之有也”意思是说,如果在一件事用的功夫多,收获反而少;用的功夫少,收获反而多,这是从来没有过的。我们的前辈如此幽默,好美的回旋体。
也有朋友说,现在是云时代了,所有用户都是测试团队,用不着自己花钱测试了。这就更值得思考了。退很多步说,至少对于微软这样的公司,对于Visual Studio这样的产品,有如此明显的BUG,还是不应该的,要口诛笔伐一下的。
时代在变,软件也在变,哪些东西是该坚守的,哪些又是该让步的呢?“人生的境界在于高度,软件的境界在于深度”,深刻认识软件,严谨对待每一行代码是我们该坚守的。
(封面照片:2018年10月与格友们在庐山秀峰)
***********************************************************
正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生
欢迎访问http://001001.org/gedu/了解软件调试高级研习班的最新信息
或者关注格友公众号