个人简介 Intel亚太研发中心架构师,《软件调试》一书的作者,《程序员》杂志调试之剑栏目作者,高端调试网站(ADVDBG.ORG)的创始人。毕业于上海交通大学信息与控制工程系,长期从事软件开发和研究工作,对IA-32架构、操作系统内核、驱动程序、尤其是软件调试有较深入研究。从2005年开始公开讲授“Windows内核及高级调试”课程,曾在微软的Webcast和各种技术会议上做过《Windows Vista内核演进》、《调试之剑》(2008年中国软件技术大会)、《感受和思考调试器的威力》(CSDN SD2.0大会)、《Windows启动过程》、《如何诊断和调试蓝屏错误》、《Windows体系结构——从操作系统的角度》等。翻译(合译)作品有《观止——微软创建NT和未来的夺命狂奔》、《数据挖掘原理》、《机器学习》、《人工智能:复杂问题求解的结构和策略》等。
MPD大会是中国软件研发管理者自己的年会,是本土操作实践与海外研发经验分享的年会,主要面向以下软件开发管理人士:首席技术官(CTO)、研发总监、产品经理、开发主管、测试经理等与软件研发团队密切相关的领导成员。
1. 我现在是在亚太软件研发团队管理年会,坐在我旁边的是《软件调试》的作者张银奎,张银奎您好。
您好。
2. 请给大家先介绍一下您自己?
好的,我更愿意把自己介绍成为一个老的程序员,其实我是96年大学毕业,之后一直做软件开发。十几年下来,一下子从黑头发做到现在已经白头发了。但是这么多年下来,我还是始终很喜欢技术,只是在做不同的技术角色吧,做过编码,也做过系统测试,现在也做架构师,不管做那个具体的脚色,我都是还是很喜欢做技术的,就说软件这个技术。那其实今天我在上午的时候曾经提到,我好像是为软件而生的,确实是这样,比如说我感觉软件对我太重要了,整个生活中,或者整个生命中,这个软件都起到了很重要的角色,有的时候甚至想,离开了软件,我是不是就还能做什么。那目前其实主要是做一些架构设计,这样的工作,也关注一些软件新的技术方向。
3. 您在08年的时候,曾经出一本《软件调试》,这本书很受大家的欢迎。您今天在这里的演讲是关于这本书的系统的总结呢,还是一种升华,是否融合了这两年对软件技术的研究的新成果?
其实我的书可以认为是我从事软件第一线奋斗13年,差不多13年后的一个技术方面的总结,就是软件调试可以说把整个涉及到我对技术的最好的一些总结,放在那本书里面,就是给同行看的,给程序员看的,主要是给开发者看的。那今天上午这门课呢,可以认为是我十几年的一种思想方法的总结,就说软件到了这么多年之后,除了技术之外的东西,其实还有很多是方法方面的,或者是软件过程方面的。那今天上午讲的可以认为是,我对软件工程或者软件方法学方面的一些感悟,比如说怎么来提高软件的可靠性,可测试性,可调试性。从比较根本的角度来说,提高软件的质量,保证软件的质量,是有关系的。
4. 那么您认为现在软件的调试技术在国内的发展情况如何?它与国外现在还有哪些欠缺的地方?
我觉得最欠缺的是我们的重视程度,在国内我们不缺少技术,技术的天才,尤其我发现80后的一批新成长起来的一些软件工程师,技术方面非常好,他们甚至对底层的理解也非常好。但是这批程序员我感觉是两极分化,一旦说对底层感兴趣的话,他们底层就了解的很深。如果对底层不感兴趣的,基本上只关心顶层。无论如何我觉得他们的技术方面都还是很不错的,但是我觉得最缺的是他们对一种思想上的认识,一种重视,对调试的重视。
其实越是资深的工程师,或者做了很多年软件之后,越感觉到调试的重要;越是比较年轻的一些程序员,可能还没有来得及深刻的思考调试的重要性,所以说回答刚才的问题,国内我觉得最缺少的是说一些年轻程序员在能够在做了一定的技术之后,对技术有一定比较深刻理解之后,回过头来再思考,自己在软件方面怎么能够更上一层楼,怎么从一些方法学上,或者是从一些根本上来解决一些提高自己的水平,使自己更上一个台阶了。因为不重视,那自然就不会做得太好,所以就说我们国内的软件,其实我觉得目前在支持测试、支持调试,支持这种持续改进方面,其实我觉得还是有比较大的差距的,当然我不排除我们国内的有些软件也做得也是挺好的。
5. 现在有一种观点就是说作为一名开发人员,除了有坚实的技术开发技术之外,还有出色的技术调试本领,你如何看待这个观点呢?
这个观点我想引用一句话,那就是我们在国内的这个技术界,我觉得也有一些前辈。比如引用的这句话是毛德昌老师在写《Linux内核源代码情景分析》那本书的,他说过一句话,在那本书里,他说对软件的调试的重视是再重视也不过分的,如何重视也不过分的,我是很同意这一点。我也是觉得我们这些年轻的程序员或者是在技术角度,觉得自己已经有一定技术的程序员,要开始用调试技术来进一步的武装自己,然后通过调试技术来持续地提高自己。这个话题很长,其实对调试,大家一直是持着一种争议的态度,也不排除我们有很资深的,在算法方面,造诣很深的,在软件领域造诣很深的一些,简单说一些牛人,他们在软件方面成就很大,但是老实说他们也有部分人,他们不是太重视调试,总觉得算法是最重要的,其他很多编码对语言的理解是更重要的,对调试不是那么重视。
但是我觉得这个是有历史的背景的,或者是说有这个大的前提的。其实这些人,他们在七八十年代成长起来的这些程序员,或者七八十年代这些老的程序员,他们当时面对的这个软件环境和我们今天还是差异非常大的。举个不太恰当的例子,求伯君前辈当初写WPS的时候,大部分模块都是用汇编语言写的,整个WPS这个软件,也很小,只有不到一张软盘,就可以装的下,这样的范围。那当时来说,就说软件很小的情况下,那么软件的复杂度比今天来说是低很多的,那这种对于低复杂度的这样的软件来说,我同意不太重视软件调试也可以把这样的软件做得比较好。但是今天已经不一样了,今天的我们的软件动辄几十兆,上百兆,对于一个典型的系统都是什么,几G的这样的一个系统。那对于这样一个复杂的系统,我认为是用传统的这种文档或者理解源代码已经很难理解透了,就是说今天软件的复杂度,超出了文档可以描述的能力。
所以这个时候,我就觉得软件调试变得更加重要,软件调试的好处就是说直击要害,你想理解那里,那里是个断点,你想理解哪个模块,哪个模块单独看一下它有哪些函数;想深入到某个函数内部可以做一些追踪,所以就说你可以直接在一个真实的上下文里面直接定位到你关心的那一部分,这个是读文档和读源代码都没办法达到的。因为读源代码我们可以看源代码,但是源代码尤其是今天的典型的源代码,都是几百兆的源代码,那像Windows操作系统可能都是上G的源代码,至于这样的源代码,你如果直接读源代码的话,是很难找到你比较关心的,或者找到一个函数。
尤其是今天C++写的代码,其实在读源代码的难度会更大,因为很复杂的这种多态、继承这种关系,你读的方法和实际执行的这个方法其实是有很大的差异的,我的意思是说,只有你在实际调试的时候,你才能直接理解到是一个真实的上下文,很具体的上下文。这时候你可以看函数之间的调用关系,一些类之间的这种相互的协作的关系,当时的这种具体的整个软件的一个真实的运行环境,使你可以理解到一个很真实的一个系统,可以说很快速的理解一个真实的运行情况。所以我就觉得今天的程序员我的建议是说,慢慢的要用调试来武装自己,来适应这种日益复杂的软件环境。
6. 有人也说过这样的看法,就是软件开发实际上就是软件调试,因为一个项目或者一个产品的研发过程,也是不断地调试的过程,您怎么看他的这个想法?
这句话我觉得要稍微纠正一下,如果这样说出去可能很多人要拍砖。因为软件开发毕竟是,还是涉及到很多方法,要涉及算法,要实现算法,最后才是调试,这个我更同意国内超级解霸的作者,梁肇新先生说的那句话,他说好的软件都是调试出来的,也就是说这句话和刚才那句话是有相近的意思,但是还是不太一样。我更同意这个梁肇新博士说的这句话,就好的软件都是调试出来的,意思是说,一个程序员写完代码,你不经过调试,最后代码就丢出去了,或者以后就发布到产品里面去了,这事情可能是很冒险的一件事情。尤其就像我前面讲到的,今天的软件和以前的软件不一样了,今天的软件实际的运行环境太复杂,也就是说我们典型的一个这样的一个进程,那进程内其实在实际运行的时候和开发者的这个环境是有很大差异的,里面可能有很多第三方的模块会插入到这个进程里面。
那其实说会有很多预料不到的情况,所以就说,一定要最好是在一个比较真实的环境下,做一些调试,做一些跟踪,这时候你才知道自己的代码实际的运行是怎么运行的,在一个实际的情况下指令运行。来进一步精化自己的设计,精化自己的这个算法的实现,然后增强自己算法,加入更多的这种容错的机制,灵活性。所以说确实我很同意梁肇新博士说的话,好的软件是调试出来的,每个程序员应该认真调试自己的代码,应该成为软件开发的一个重要部分。
7. 对于软件测试和软件调试,您认为这二者的关系是怎么样的,软件测试人员是否都应该具备软件调试的技能呢?
这个问题很好。坦率说,测试和调试肯定是不一样的,测试主要的目标还是为了发现瑕疵,调试的目标是为了定位瑕疵的根源,简单来说,二者分别是这样的。但是他们总的来说目标又是一样的,都是为了提高软件的质量,通常都和软件瑕疵有关。当然软件调试除了对付瑕疵之外,还有更多的应用,那我们暂时不谈。从对抗瑕疵的角度来说,调试和测试是很密切的两个过程。调试呢,主要针对是从发现一个失败或者发现一个症状,不正常的症状,简单的说,凡是跟软件的规约、产品规约不一致的行为都可以认为是认为是不当的,认为是瑕疵,这是测试工程师把这些不当找出来,这些和软件规约的差异找出来。调试的主要目标是什么?把找出来的这些问题进一步定位,定位到到底是代码里面哪里有Fault,就是根源哪里有不当。所以测试可能更注重的外在的一个特征,而调试是什么?从这个不当的特征找它的根源。
那么你的后半部的问题是说测试工程师是不是要了解调试,这也是我很想谈的一个话题。比如说一个测试的工程师,做到一定程度,那我们应该继续干什么,继续增强什么,其实测试我觉得是一个很有研究或者很有发展空间的一个职业。我们不要觉得说,测试都是一些谁都可以做,然后做也没有太大的发展空间,我不这么认为。我觉得测试是可以做得很资深,很资深。那坦率说呢,我也曾经做过这个系统测试的工程师,那也做了大约有一年多时间,这一年多时间里,其实我是做了很多调试的工作。那其实测试我前面说了,测试主要是发现问题,但发现问题对一个复杂的系统,这个问题属于哪个模块的,或者属于哪一方的,这个最好是做一个初步的定位的。你比如说,对一个系统出来一个问题,这个问题可能是某一个软件的,也可能是和硬件相关的,也可能是操作系统本身的,那这个时候最好是有个初步定位。
所以说测试工程师如果能够懂一点调试技巧的话,做一个初步定位,那这就会大大的加快这个问题的解决速度。一下子就可以定位到,这是某个模块,这是某个ISV的,这是某个硬件厂商的,那一下子就可以,大大的缩短这个问题的解决速度。进一步说其实整个测试,你要想今天的测试也变得越来越复杂,你如果不了解软件的底层,很难测出一些深层的Bug,那调试恰恰可以帮助我们了解这个软件的内部机制,如果你会一些调试的技巧,了解一下软件内部机制,然后再有针对性的做测试,那这个时候的测试效率就不一样了,可以测试出来一些很深层问题。所以说我始终觉得测试总是只有开始没有结束,没有说一个测试说我测试好了,我这个产品就可以说百分之百没有瑕疵了,没有这种情况,所以测试无止境。测试工程师达到一定水平,我觉得要通过调试来什么来了解软件的深层,然后使这个测试更有针对性,提高测试的效率。
8. 国内的开发人员普遍没有接受过系统的调试的培训,特别是出现了Bug,他们不知道如何去定位追踪,那你认为如何来对这些开发人员对他们的调试的技能进行培养呢?
这是一个很长久的问题,我觉得这也不光是国内的一个问题,我觉得整个软件教育都是存在这样一个问题。据我调查下来,世界范围内还没有哪所大学开一门课叫软件调试,但是我觉得是很有必要的。因为整个教育是滞后于实践的,这是一定的。那因为正是我前面谈到一个大背景,我们以前总的来说,软件相对于来说简单,整个大家更注重算法,更注重一些设计,没有考虑到软件复杂之后调试的这种方法学和这些系统的问题。
对于我们整个大学教育基本上对这个没有关注这一块,所以就导致一个其实是国际范围内的所有软件工程师调试一块都相对薄弱,包括我的很多外国同事,其实他们也很薄弱。其实很多时候他们对于这些程序员来说,他们也是遇到一个问题无从下手,很多时候也是用一些很原始的方法,反复的重现,反复的写一些Log,这样来找,效率是比较低的。我觉得要解决这个问题,一定是一个逐步放大的过程,那么首先是我们一些比较资深的工程师,他们意识到这一点,来带动身边的人来逐渐的重视软件调试的技术,然后慢慢的我们做更多的培训来提醒大家来逐步的重视这一方面,来逐渐地改变他们。
如果分享一些具体的经验,我觉得就是说,对于这些年轻的程序员,年轻的工程师,一定要养成调试的习惯,就像我们前面说的,好的软件都是调试出来的,大家一定要是随时调试,每天只要是写代码,就调试,调试器不离手。我本人是这样,今天还是这样,基本上调试器不离开我的每台电脑,我用过的每台电脑,基本上它们上面都有调试器,我随时会遇到问题、分析问题、了解问题。关键是很多东西都是一个熟练的过程,你如果每天用,那自然就会很好。所以说,如果在看这个录像的软件同行,如果还没有重视调试器,还没有把调试器作为一个这种拳不离手,曲不离口的这样的一个工具,那我希望从今天开始做起。那可以这样自己去做了,就不怕学不会,就不怕那总归是一点一点的,总是在进步。调试的技术,确实是需要一定时间才能上手的,因为他是需要理解很多底层的东西,理解到全方位的东西。
9. 软件调试涉及的面其实是非常广的,比如说语言、操作系统,甚至编译器,你都要有所了解,但现在国内普遍有一种比较浮躁的风气,很多人不愿意去研究底层的东西,你对此有什么建议吗?
对,这确实是一个很现实的问题,因为我们不一定是每个程序员都面临着现实的生活中的种种压力和种种困难,尤其是刚毕业的年轻程序员,要学习的东西太多,各方面社会的压力也很多,时间很紧张,基本上手头急需要用的技术学起来很还吃力。但是我觉得这是一个学习的方法学的问题,我是一直在主张,一种从上到下的学习方法,我们大多数工程师,年轻工程师学习方法是从上到下,先学习顶层,学这个语言,那个语言,学这个编程技术,那个编程技术,但是很难有时间顾及底层,那我觉得不妨逐渐的换一下思路,换一下角度,我觉得底层的东西相对来说还是少,或者说底层的东西有更好的稳定性。
举个例子来说,其实调试的这些技术难是难,但是它基本上是很稳定,很多基础基本上是三四年没有改变过,而且就是那么一点点。但是如果你把这一点点学了,那其实你一下子,就是懂得了很多东西,所以从下到上的这种学习方法的好处就是说,你学的东西会少,学的东西有更好的生命期。底层的东西,其实包括操作系统,内核一层的东西,也基本上是十几年不变。像这些东西,你是学的时候,刚开始是要花点力气,但是一旦学会了,那你的受益就是十几年,不像顶层的技术,我们顶层的技术可能它的生命力相对来说就短一些,一个技术,比如这个技术这几年很热,但是过几年之后可能就是说,它的变化就很大,所以要重新学的东西比较多。所以我的建议就是说,大家还是要先把顶层的,手头急需的还是要学,否则会影响具体的工作,那你一旦有空,那你就是说最好是再从底层学一学,那从下而上,一旦把整个打通,就像你前面提到的,软件调试涉及到CPU、操作系统、编译器,最后到软件本身,一旦整个这个环节,整个这个Stack打通了,以后就会变得得心应手。
10. 在您的专著《软件调试》中曾介绍过软件可调试性的内涵、意义、原则和方法等等,那您能否向我们的读者详细介绍一下可调试性所涉及的方方面面呢?
可调试性这个是从芯片设计领域引用过来的一个概念,或者一种思想方法,其实它都是针对这种高复杂度,但是低可见度的这样的一些过程,像芯片,它很典型的,它高复杂度、低可见度。正是因为它有高复杂度,低可见度,所以我们必须内置一些机制来支持测试,支持调试,这种可调试性,就是引用到软件领域其实也是这样一种思想,软件今天也变得具有了这样特征,高复杂度,低可见度。所谓低可见度,就是说我们很多时候只看到了软件上面的一个UI,一个呈现,软件下边到底是怎么工作的,内部机制怎么工作,和操作系统怎么交互的,和硬件怎么交互的,这些机制是对于顶层来说是很低的可见度。正是由于这种高复杂度,低可见度,是不得不叫我们内部要设计一些设施,这些设施来帮助我们增加它的可见度,增加它的可观察性,可配置性,可控性,这样我们可以在顶层很好的控制它,它出了问题,我们知道看哪里。
其实前面提到基本上就是说可调试性的一些核心内涵,我们在设计早期,就容入这种思想,在我们的设计里面就包含了后期对调试和测试的支持,不像是我们现在是在什么?全部都实现好了,发现了问题,然后我们再去改设计,这个是相当于在早期就做预防,我经常说未雨绸缪这句话,相当于是我们在设计软件的时候就未雨绸缪,埋下了一些设施,然后出问题的时候来找。我经常打一个比喻,就像我们整个大楼里的这些防火设施,我们在设计这个大楼的时候,是不知道它那一天着火的,但是我们一定要设计好这些消防通道,这些安全设施,这些警铃。这些调试设施也是,我们在设计这些设施的时候,不知道这个软件什么时候崩溃,它崩溃在哪里,所以我们相当于是一个什么,布下天罗地网,埋下这种各种设施,我们有很多探点,有很多错误搜集,错误报告的机制,那一旦说它错误哪里有,软件以后那里出了问题很容易就发现它。
11. 谢谢您接受我们的采访。
谢谢。