最近正为老板的一个项目头疼,这个项目已经做了两年,功能基本已经实现了,但是使用过程中总是报错。因为项目拖的时间比较长,经过两届师兄师姐的开发,代码风格和思路各异,也没有留下任何开发文档和需求分析,代码也没有注释和说明,格式也不统一,看的真是很头疼和极其痛苦,调试起来头都要爆了。看到这篇文章,作者的乐观心态和调试心得对自己来说也是一种鼓励。转过来与那些和我有相同处境的人共勉,希望自己早日完成项目!
原文:http://www.blogjava.net/BlueDavy/archive/2008/11/18/241245.html调试代码是一种乐趣
近 来连续调试了好几天的代码,乐趣无穷,:),在纯净的人和机器对话的时间中,充分的和机器不断的交流,最终共同实现功能,和同事说:“我喜爱调试代码胜过 了写代码”,怎么说呢,我觉得调试代码能够充分让你将所掌握的知识发挥出来,考察自己解决问题的能力以及学习知识的能力,在这篇blog中来闲聊下调试代 码。
调试代码是一种编码所需的基本能力,相信没有多少人写出来的代码能够是没有bug的,虽然传闻是有过这样的人,因此普通的我们只能通过调试代码来查找和修复代码中的问题,需要调试代码的场景有很多种,在这些场景中,也会有很多种不同的调试技巧可采用。
最典型的需要调试代码的场景是单元测试的场景,在单元测试时会碰到代码的执行不符合预期或抛出意料外的异常,在碰到抛出意料外的异常时,通常现在的高级语 言都会提示是由于哪行代码造成的异常,于是首先的做法都是去看看对应的那行代码是什么个状况,然后评估大概是什么原因造成的,如果在这种推测情况下无法判 断问题在哪了的话,在没有支持程序调试时,通常只能是在原始代码中输出一堆的信息到console,例如java中就是 System.out.println或System.out.err,于是运行,看看console中一堆的信息,然后慢慢的来推测问题,当然,这也是 一种可选的方案,甚至在某些场景中是一种不错的方案,但在各种IDE支持程序调试后,更多的时候调试都可以通过IDE来进行,发明这个的人真的太伟大了, 虽然我不知道是谁,但是还是想膜拜下的,:),有些看似很小的功能,往往非常的重要,于是现在的我们可以幸福的设个断点,然后开始逐行跟踪、跳行跟踪、跟 踪进入函数内部、跳出函数、跟踪变量甚至修改变量等等N多种的方式幸福的调试着代码,看着代码在运行时的状态,很容易的就让我们发现代码中的问题,这个绝 对是节省了非常非常多的时间,所以我说我很佩服那些号称用记事本写代码的高手们,难道他们的代码都是零bug的?要么就是出了bug后也可以一眼判定问题 所在的?那实在太强了点,对于这样的高手,确实可以不需要IDE这种现代化的武器,对于我而言,用记事本写就像停留在原始时代,而IDE差不多应该到帝王 时代了,:),开玩笑,调侃下用记事本写代码的高手而言,这是单元测试中的场景和通常采用的技巧。
还有需要调试的场景通常会是集成测试场景,通常,集成测试会复杂很多,于是要用到的调试技巧会复杂一些,就像下面这样的两种场景:
1、并发程序
并发程序向来就是最最复杂的,没有人能知道在运行时到底是怎么个执行顺序,否则就不叫并发了,:),于是,在并发程序中,N多种人脑无法想象的复杂场景就 出现了,毕竟人脑的思考应该不支持并发的吧,至少我的貌似支持不了,也许是我笨,呵呵,而且通常并发程序中的错误是不一定能每次都重现的,这是最麻烦的, 至于借助IDE调试,同样是不行的,因为并发程序由于断点的进入可能完全被打乱,于是,对于并发程序,通常能采用的方法,比较靠谱的方法,我觉得还是打日 志,当然,你可以选择继续System.out.println、System.out.err,或者采用更加高级和优雅点的log.debug这样的方 法,然后就看着时间戳来慢慢的运用自己的大脑来思考复杂的并发的问题,:),这绝对是一种挑战,但因此也会带来充分的乐趣,于是慢慢的享受这个过程吧。
多说一句,还好现在java有了更高精度的时间戳:System.nanoTime,用System.currentTimeInMills根本就没法分析并发程序,因为它的精度不够。
2、所依赖的程序有问题
这种场景嘛,相对而言就复杂很多了,因为通常这个时候能做的多数是通知所依赖的程序方去查找问题,但如果手头有所依赖的程序的代码的话,多数可以采取跟入 其源码的方式,尽管不一定能修复其源码,但对于查找出问题还是会提供很大的帮助,例如跟踪框架代码、jdk代码等等,对于访问的远程程序而言,则不太相 同,java嘛,还好,可以支持远程调试,我相信现在的大部分语言都支持的,远程调试那是相当的重要呀,于是我们就可以在本地调试着远程某台服务器上执行 时的bug,:),偷着乐吧。
最后一种最痛苦的大家最想调试的场景,就莫过于生产环境了,估计有N多人都想直接在生产环境中调试,看看生产环境中的问题是怎么产生的,但生产环境嘛,是 不太可能拿来调试玩的,而有些时候线下要模拟也不是什么简单的事,说到这,又要天马行空的瞎扯下了,记得在云风的blog上以前有写过一篇游戏中对于出错 场景的记录以及回放的功能,这功能听着是相当的帅呀,好,继续回到正题,在自己的代码还没有如此强大的错误记录和回放功能时,也许能做的选择就是在代码中 多写一点log.debug了,在生产环境有问题时,则打开相应的日志的debug项,然后继续靠人肉分析了,这个时候还是体现了log.debug的强 大作用滴,根据这点可以看出,在代码中还是有必要写些合适的log.debug的,除了为了自己外,对于其他使用的人调试bug也是可以給予很大的帮助 的。
嗯,没想到稍微扯了下,也写了不少,从这稍微写的内容中,其实也能看出,调试可不是闹着玩的事,绝对需要相当完整的知识体系,这也难怪张银奎的《软件调试》写了三年,而且那么的厚,看来还是值得看看的,:),小广告,大家别在意。
而且这里还没说到,通常需要调试的时候,多数都是出问题的时候,那么这个时候还会面临很大的压力,怎么样在紧急的时间内还冷静的做好人肉分析的工作,这可 是相当的考验抗压能力、技术基本功、逻辑分析能力和学习能力的过程,想想,要能在短时间内查出问题的原因,通常需要对代码运行的场景做出冷静的分析,进而 需要具备从头到尾的知识,例如所采用的框架、java包、甚至到JVM、操作系统、硬件,实在不行的话,还得临时学习下这些知识,这样才能在短时间内解 决,因此想来想去,越来越觉得如果在面试的时候让面试者现场调试段程序貌似还是挺靠谱的,更有助于全方位的考察,貌似只有很少数的几家公司会这么干,当 然,这有客观条件的原因,但要克服貌似也不是很难。
调试代码是一种编码所需的基本能力,相信没有多少人写出来的代码能够是没有bug的,虽然传闻是有过这样的人,因此普通的我们只能通过调试代码来查找和修复代码中的问题,需要调试代码的场景有很多种,在这些场景中,也会有很多种不同的调试技巧可采用。
最典型的需要调试代码的场景是单元测试的场景,在单元测试时会碰到代码的执行不符合预期或抛出意料外的异常,在碰到抛出意料外的异常时,通常现在的高级语 言都会提示是由于哪行代码造成的异常,于是首先的做法都是去看看对应的那行代码是什么个状况,然后评估大概是什么原因造成的,如果在这种推测情况下无法判 断问题在哪了的话,在没有支持程序调试时,通常只能是在原始代码中输出一堆的信息到console,例如java中就是 System.out.println或System.out.err,于是运行,看看console中一堆的信息,然后慢慢的来推测问题,当然,这也是 一种可选的方案,甚至在某些场景中是一种不错的方案,但在各种IDE支持程序调试后,更多的时候调试都可以通过IDE来进行,发明这个的人真的太伟大了, 虽然我不知道是谁,但是还是想膜拜下的,:),有些看似很小的功能,往往非常的重要,于是现在的我们可以幸福的设个断点,然后开始逐行跟踪、跳行跟踪、跟 踪进入函数内部、跳出函数、跟踪变量甚至修改变量等等N多种的方式幸福的调试着代码,看着代码在运行时的状态,很容易的就让我们发现代码中的问题,这个绝 对是节省了非常非常多的时间,所以我说我很佩服那些号称用记事本写代码的高手们,难道他们的代码都是零bug的?要么就是出了bug后也可以一眼判定问题 所在的?那实在太强了点,对于这样的高手,确实可以不需要IDE这种现代化的武器,对于我而言,用记事本写就像停留在原始时代,而IDE差不多应该到帝王 时代了,:),开玩笑,调侃下用记事本写代码的高手而言,这是单元测试中的场景和通常采用的技巧。
还有需要调试的场景通常会是集成测试场景,通常,集成测试会复杂很多,于是要用到的调试技巧会复杂一些,就像下面这样的两种场景:
1、并发程序
并发程序向来就是最最复杂的,没有人能知道在运行时到底是怎么个执行顺序,否则就不叫并发了,:),于是,在并发程序中,N多种人脑无法想象的复杂场景就 出现了,毕竟人脑的思考应该不支持并发的吧,至少我的貌似支持不了,也许是我笨,呵呵,而且通常并发程序中的错误是不一定能每次都重现的,这是最麻烦的, 至于借助IDE调试,同样是不行的,因为并发程序由于断点的进入可能完全被打乱,于是,对于并发程序,通常能采用的方法,比较靠谱的方法,我觉得还是打日 志,当然,你可以选择继续System.out.println、System.out.err,或者采用更加高级和优雅点的log.debug这样的方 法,然后就看着时间戳来慢慢的运用自己的大脑来思考复杂的并发的问题,:),这绝对是一种挑战,但因此也会带来充分的乐趣,于是慢慢的享受这个过程吧。
多说一句,还好现在java有了更高精度的时间戳:System.nanoTime,用System.currentTimeInMills根本就没法分析并发程序,因为它的精度不够。
2、所依赖的程序有问题
这种场景嘛,相对而言就复杂很多了,因为通常这个时候能做的多数是通知所依赖的程序方去查找问题,但如果手头有所依赖的程序的代码的话,多数可以采取跟入 其源码的方式,尽管不一定能修复其源码,但对于查找出问题还是会提供很大的帮助,例如跟踪框架代码、jdk代码等等,对于访问的远程程序而言,则不太相 同,java嘛,还好,可以支持远程调试,我相信现在的大部分语言都支持的,远程调试那是相当的重要呀,于是我们就可以在本地调试着远程某台服务器上执行 时的bug,:),偷着乐吧。
最后一种最痛苦的大家最想调试的场景,就莫过于生产环境了,估计有N多人都想直接在生产环境中调试,看看生产环境中的问题是怎么产生的,但生产环境嘛,是 不太可能拿来调试玩的,而有些时候线下要模拟也不是什么简单的事,说到这,又要天马行空的瞎扯下了,记得在云风的blog上以前有写过一篇游戏中对于出错 场景的记录以及回放的功能,这功能听着是相当的帅呀,好,继续回到正题,在自己的代码还没有如此强大的错误记录和回放功能时,也许能做的选择就是在代码中 多写一点log.debug了,在生产环境有问题时,则打开相应的日志的debug项,然后继续靠人肉分析了,这个时候还是体现了log.debug的强 大作用滴,根据这点可以看出,在代码中还是有必要写些合适的log.debug的,除了为了自己外,对于其他使用的人调试bug也是可以給予很大的帮助 的。
嗯,没想到稍微扯了下,也写了不少,从这稍微写的内容中,其实也能看出,调试可不是闹着玩的事,绝对需要相当完整的知识体系,这也难怪张银奎的《软件调试》写了三年,而且那么的厚,看来还是值得看看的,:),小广告,大家别在意。
而且这里还没说到,通常需要调试的时候,多数都是出问题的时候,那么这个时候还会面临很大的压力,怎么样在紧急的时间内还冷静的做好人肉分析的工作,这可 是相当的考验抗压能力、技术基本功、逻辑分析能力和学习能力的过程,想想,要能在短时间内查出问题的原因,通常需要对代码运行的场景做出冷静的分析,进而 需要具备从头到尾的知识,例如所采用的框架、java包、甚至到JVM、操作系统、硬件,实在不行的话,还得临时学习下这些知识,这样才能在短时间内解 决,因此想来想去,越来越觉得如果在面试的时候让面试者现场调试段程序貌似还是挺靠谱的,更有助于全方位的考察,貌似只有很少数的几家公司会这么干,当 然,这有客观条件的原因,但要克服貌似也不是很难。