程序员量子力学-海森堡式BUG
2014年4月02日 20:17
今天在阅读《The Pragmatic Programmer》的时候发现书中提到了Heisenbug让我想起来了多年以前在开发中碰到的一个海森堡式的BUG。
海森堡是德国著名的物理学家,量子力学的创始人之一,“哥本哈根学派”的代表人物。
如果大家对这段历史或者物理原理不清楚的话,推荐大家阅读《上帝掷骰子吗》,非常好看的一本关于量子力学历史的科普书。
海森堡对量子力学的一个重要贡献是提出了著名的“不确定性原理”(又称“海森堡测不准原理”),在一个量子力学系统中,一个运动粒子的位置和它的动量不可被同时确定。这是因为要观察就必须用光击中被观察的粒子,通过反射的光波来确定粒子的位置。然而观测所发出的光必然会影响被观测的粒子。
海森堡式的BUG是该物理原理在软件开发中的一个表现,当然这是一个类比,它和量子力学没有半毛钱的关系。举个例子吧,当程序出现了一个bug,程序猿为了找到bug出现的原因,在出现bug的代码处加入了一条打印语句,想要了解在出现bug的时候本地变量和参数的值以便确定bug出现的原因。然而奇迹出现了,当加入打印语句后,bug消失了。哦,买糕的,这条打印语句不就是射向粒子的那束光么?
下面,我来给大家分享一下我遇到的那个Heisenbug。
当时我们团队做一个商用软件从Windows平台到Linux平台的移植,因为该软件早期是以Mac为平台编写的(那是Mac还没有像现在这样风靡),所以我们的移植平没有遇到太大的困难,主要的工作是包括:
- 用QT来实现所有的UI组件
- 支持Linux下的数据库连接
- 移植C++非标准的使用
- Unicode的支持
项目的前期非常顺利,然而就是当项目接近尾声的时候,一个可怕的海森堡式bug出现了,产品的release版本会出现一些随机的错误,这些错误都很奇怪,并没有一个统一的表现。而这些错误在debug版本中完全不会出现。为了解决这个问题,在boss的率领下我们开始加班除虫。在当时公司加班还是很少见的,一方面欧洲公司以人为本,并不鼓励加班;另一方面我们团队,大家能力都很强,不需要加班来解决问题。然而这个bug属于必须要解决的问题,加班在所难免。
可是要解决问题这个问题还真是不容易,因为问题不会出现在debug版本中,所以用gdb加断点单步调试的方式根本没有用。所以调试的方式就只剩下了打日志。经过我一天不断的加入日志,运行测试,加入日志,运行测试…… 最终终于找到了问题的原因。一般情况下,找到问题的原因比解决问题要困难的多,当你发现问题的原因后,解决的方法就像秃子头上的虱子。
原来,这个问题是由于我们生成软件版本的方法所造成的。
为了在build的时候自动的生成软件的版本,我们的天才工程师想了一个好主意,那就是把版本信息写入Linux的可执行文件中(大家可以参考ELF的规范)。我们的软件可以从文件中读出这个版本,让后通过API告诉任何组件或者使用者当前的软件版本是什么。不得不承认这是一个非常有创意的好主意,但是这也是引起错误的主要原因。由于疏忽,在写入版本信息的时候,并没有严格按照规范来写,所以其实版本信息超出了本应写入的位置,也就是说写越界了,破坏了程序数据,所以造成了release版本中的错误。可是为什么debug版本没有问题呢?原因大概是debug版本由于加入了大量的代码和调试信息,版本信息很可能只是覆盖了调试信息而没有影响程序的正常运行。
这么多年过去了,很多细节我已经记不清楚啦,然而对于这个海森堡式的bug,现在想想,我们应该可以做的更好:
-
设计评审
我们鼓励创新,当引入一个新的解决方案,尤其是别人很少或根本没有使用过得方案时,我们必须非常小心。因为没有人碰到同样的问题。认真的设计评审或许可以有所帮助。 -
代码审查
如果当时我们能够认真的进行代码审查,也许我们能发现这个问题。但也许即使进行代码审查,也不一定能发现这个错误。团队中没有几个人能够搞懂ELF的规范 -
单元测试
单元测试能帮助发现这个问题么?也许可以,当时我们并没有就这个功能引入任何的单元测试。
最后引用一个我发现的软件开发中的测不准原理:软件的需求和完成时间不可能同时测准,如果你清楚需求是什么,那么你就不知道什么时候能完成;如果你知道什么时候要做完,那你根本就不知道需求是什么。