记一次头疼的 Debug

代码不规范,师弟两行泪。

这是前天(2019-03-15)的事了。手里的一个项目出了个奇怪的问题:在 Win 7 上一切正常,但是在 Win 10 上 Release 模式下随机出现不能启动的问题。Debug 模式没问题,同时日志中没有异常记录、控制台一片祥和。之前客户那边一直使用 Win 7 的环境,现在要购置新的机器,市场上适合 Win 7 的选择不多了,所以我们这边只能把项目适配到 Win 10。于是我开始着手解决这个问题。

由于 Debug 模式下一切正常,我首先想到的是编译器优化导致的问题。于是我尝试将编译器 /O2 选项更改为 /Od/MD 更改为 /MDd,并将 /GM 改为 /GM-,然后重新生成,问题依旧。基本可以排除编译器的问题了,也就是说是代码自身的问题。

这时候我心里一凉,因为好像遇到了最难调试的情况。项目是老项目,也许之前的人编码习惯比较差?于是我将编译警告等级改到 W3,重新编译,不出所料控制台立刻被几千个 C4096 淹没,而且禁掉 C4096 之后还有一大堆 Warning。难受。

我坐正,嘬了一口咖啡,平复一下心情,决定先把出错位置找到。Release 模式下不能(当然并不是完全不能)设置断点,而且在当前情况下设置断点并没有用,程序根本没有抛出任何异常!只好用 cout 一步步地试。漫长又枯燥的两小时就这样过去了。

最终定位到的出错代码是一段老代码,让我想打人,形如:

class ClassA{
    ENUM m_Type;
    ...
    ClassA* get(ENUM type){
        if(type == m_Type) return this;
        else {...}
    }
    ...
}


ENUM type;
ClassA obj_ClassA();
...
obj_ClassA.get(type);

ClassA 的 get(ENUM type) 方法中对传入参数 type 做了判断,当 typem_Type 相等时直接返回 this,免去后续的初始化。但是问题是,实参 type 没有显式地初始化,也就是说在运行时初始值不确定。

某些情况下,这些没有初始化的值是随机的,事先不能确定;但在一些情况下,依照平台的不同,可能会初始化为 0 或者其它值。因此有一定的机会 typem_Type 从一开始就是相等的,使这个类根本没有经过初始化,导致了后续的 bug。至于为什么没有抛出异常,也是代码不规范导致的。说实话这根本不是一个合格程序员该犯的错误。任何变量都应该手动地初始化,这是基本素养。发现问题之后的 fix 其实很容易。

这次 Debug 给我的启示是在任何时候都要注意代码规范,养成良好的编码习惯。其实这种问题在开发之初就应该查出来,可见这个项目一开始就根本没有合理的 review 和测试环节。几千上万行的代码尚且好说,当项目规模上升到 10 万行左右这种问题找起来就如同大海捞针了。

Comments

添加新评论

已有 5 条评论

有代码检查工具啊。而且有免费的代码检查工具啊。

我写mark的时候也很乱,所以现在在重写
owo表情貌似有些问题啊,被页脚遮住了,要拖动才能看到

熊猫小A 熊猫小A 回复 @BigCoke

GitHub 上也有人提了 issue,已修复。