您的位置:知识库 » 程序人生

那些让人睡不着觉的bug,你有没有遭遇过?

作者: 张铁蕾  发布时间: 2018-07-22 22:39  阅读: 8883 次  推荐: 47   原文链接   [收藏]  

  我先讲一个小故事,以前在外企工作时的一个亲身经历。

  当时我所在的team,负责手机上多媒体Library方面的开发。有一天,一个具有最高等级的bug被转到了我的手上。这个bug非常诡异,光是重现它就需要花很长时间。在公司内部的issue追踪系统上,测试人员描述了详尽的重现步骤,大概意思是说,用某个指定产品线的手机去播放一段《黑客帝国》的视频,大概播放到半个小时左右的时候,程序就会突然崩溃。

  你可以想象,造成崩溃问题的可能原因实在太多了,比如某个局部算法的实现发生地址越界了,或者多线程执行的时序混乱了,再或者传给解码器的参数传递错了,等等,诸如此类。问题可能出在上层应用,也可能出在中间的Library,或者编解码器有问题,甚至是底下的内核或硬件不稳定(我当时所在的公司是一家手机制造商,软件和硬件都是自己设计),总之,你能想到的或想不到的都有可能。

  事实上,对于这个问题的分析也是按照从上至下的顺序进行的。首先,既然播放会崩溃,那至少看起来是播放器的问题啊,OK,issue会先转给播放器的开发团队。播放器的开发团队经过分析之后发现,并不是他们的代码引发的问题,接下来他们把分析结果附在issue的处理历史上,并把issue转给下一个团队处理。那应该转给哪个团队呢?就要看上一个团队的分析结果了。他们在分析的过程中,会追踪到最终崩溃的地方是发生在他们引用的哪个代码模块中,然后就有专门的人负责找到维护相关模块的团队。就这样,这个bug从上层开始,经过层层流转,终于有一天来到了我的手上。

  一个团队被分发到这样一个最高等级的bug,就意味着必须停下正在进行的一些工作,立即分出人手来处理它。这就像一个烫手的山芋,谁也不想让它在自己的团队里待上太长时间。我经过大半天的分析,终于证明了崩溃的精确位置并不在我们负责维护的代码区域里,而是在我们调用的更下层的一个模块中。OK,在系统中填上分析结果,附上分析日志,再起草一封总结邮件,我的处理工作就此愉快地结束。但是,bug依然存在!

  有人会好奇,这个bug后来怎么样了?它就这样在issue追踪系统上转了个把月,最后由于对应的产品线被cancel了(也就是那个产品线被砍掉了),自然所有相关的bug也就没必要再去解决了。这个bug就这样不了了之了......

  我所说的这家外企,曾经以完善的工作流程和质量管理体系而著名。不管是开发新特性,还是解bug,都是靠流程去推动。公司员工众多,并且分布在全球范围,这样的一套内部管理流程自然是必不可少的。假设当时那个bug,如果最后不是被cancel了,那么它会不会在流程的推动下最终被解决呢?我觉得,会,一定会。只要时间足够长,最终它肯定会被转到真正能够解决它的人手里。只不过,这里的问题是,整体运转的效率太低下了。

  与传统的IT公司不同,互联网公司一般被认为是运转效率更高的。但有些bug其实更加难解,因为互联网产品运行的环境更加复杂多变。面对一些难解决的问题,比如对于某些用户报出来的但我们自己却无法重现的问题,我们有时候会碰到这样一幕:后端的同学查完,宣称后端没有发现问题;然后客户端或前端同学查完,也宣称没有发现问题。最后,大家也不能一直耗在这一个问题上,后面还有数不清的开发需求在排队,于是,问题也同样不了了之了。等过了一个月,两个月,甚至是一年,有些「老大难」的问题依然存在。

  这显然不是我们希望看到的结果。那么问题到底出在哪呢?

  首先,没有人能了解全貌。像我开头讲的外企中的那个例子,每个团队基本只了解自己负责的模块,没有人知道问题真正出在哪。这时候最理想的情况是,公司有一些元老级的技术专家,他们可能在公司初创的时候就在,随着公司一起成长,既懂业务又懂技术,能够从上层一直分析到底层,最终把问题解决掉,或者至少分析到足够的细节再转给真正能解决问题的人。但事实往往事与愿违,公司就算有一些元老的员工,他们也往往过早地脱离了技术。他们通常很忙,忙着开各种各样的会(当然开会并不是一个贬义词)......那实际中我们如果没有这样了解全盘的人该怎么办呢?这就需要责任心极强的人,能够把解决问题的各方串起来。

  其次,缺少足够的分析问题的手段和工具。对于知道如何重现的问题,一般来说都比较容易解决,工程师通过调试,一步步跟踪,总能找到问题所在。但对于那些不好重现的问题,往往令人一筹莫展,因为我们不知道问题发生时的真实情况,也就是抓不到「现场」。

  记得刚开始出来创业那会儿,我们的服务器发生了一件奇怪的事。每隔一两天,就会有台Web服务器莫名地死掉。当时的报警机制也不太完善,问题发生时又多是在深夜,等问题出现时去看的时候,服务器已经登录不了了,于是只能重启解决,而重启之后问题也就消失了。通过一些监控工具去观察,只能看到机器重启前CPU暴涨,跑到了100%,可能是由于用的是虚拟机的缘故,那个时候机器就陷入「假死」了。经过反复追踪,终于有一次抓到「现场」了,在CPU跑满之前把流量从出问题的机器上卸了下来,结果那台机器的CPU竟依然居高不下。最后使用jstack分析了半天,发现有一些线程出现了死循环(仔细看才能看出来),原来是有一个HashMap被用在了多线程的环境下,结果内部的数据结构发生混乱了,在JDK内部对Map进行遍历操作的时候出现了死循环,最后把CPU跑满了。本来是个线程安全问题,表现出来却是一个性能问题。现在回想起来,如果当时有更完善的监控工具,就能尽早地发现问题;如果对程序的栈结构和jstack工具有更深的了解,就能更快地分析出问题原因。

  另外,对于互联网产品上经常出现的那种用户侧有问题,而我们却无法重现的情况,技术同学感觉到解决困难的原因,也往往是供他分析的「资料」不足。

  第三,也是最重要的,我们需要的是锲而不舍的精神。顽固的bug就像狡猾的猎物,它会激起出色猎手的兴趣