《程序员》官方BLOG

欢迎热心的读者随时与我们交流,您的支持是我们最大的动力!

用户操作
[即时聊天] [发私信] [加为好友]
《程序员》编辑ID:programmer_editor
1051901次访问,排名24好友0人,关注者72
programmer_editor的文章
原创 186 篇
翻译 1 篇
转载 3 篇
评论 1364 篇
最近评论
gms810:说的不错
davil_dev:我比较喜欢Firefox!
bluesz56788:有具体的出版日期进行发布吗。
kwlong2008:我是做网站建设的,来看看你的空间,不错,欢迎踩我的网站制作空间!
domemy:Linux 环境下的多核调试
— Intel + Totalview 强强联合!
目前,在软件开发行业,各种性能优异的调试工具层出不穷。但是,它们中的绝大部分都只支持windows环境。即使能支持linux平台,操作起来也很不方便。因此,对于长期在linux上编写程序的开发人员来说,如何调试就成了一个令人头痛的问题!Intel软件 和 Total……
文章分类
收藏
    相册
    《程序员》08年封面秀
    06年《程序员》封面秀
    07年《程序员》封面秀
    Logo
    编辑部合影
    调查分析图表
    模版
    文章用图
    新年祝福,名家寄语
    友情链接
    博文视点
    杂志订阅
    杂志订阅
    征文启事
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 《程序员》07年3期文章试读:调试之剑收藏

    新一篇: 《程序员》07年3期文章试读:网银安全大揭秘—摇摇欲坠的达摩克利期剑 | 旧一篇: 《程序员》07年3期文章试读:程序员的武器大家谈之雷军专访

    调试之剑
    文/张银奎

    弗雷德里克·布鲁克斯(Frederick P. Brooks)博士在他那篇著名的《没有银弹——软件工程中的根本和次要问题》一文中,将软件项目比作可怕的人狼(werewolves),并大胆地预言十年内不会找到特别有效的银弹。该论文发表的时间是1986年,如今整整20年过去了,尽管不时有人惊呼找到了神奇的银弹,但是冷静的人们很快发现那只是美好的愿望。

    如果说软件工业中与人狼的战斗还在持续,那么在这些战役中一定会有程序员的身影,笔者也是其中的一个。我的编程生涯是从使用汇编语言编写DOS下的TSR程序开始的。今天DOS操作系统已经成为历史,在那个年代最值得炫耀的TSR技术也早已经过时了。十几年中,OWL、VFW、VDX、ISAPI、Active Movie等技术也被时间淘汰……然而,在这漫长的时间当中,我最看重的是软件调试技术。它是十几年中我学到的最有用、一直受用、而且日久弥新的一项技术。

    从软件工程的角度来讲,软件调试是软件工程的一个重要部分,软件调试过程出现在软件工程的各个阶段。从最初的可行性分析、原型验证、到开发和测试阶段、再到发布后的维护与支持,都有软件调试过程的参与。通常认为,一个完整的软件调试过程由以下几个步骤组成:
    ● 重现故障,通常是在用于调试的系统上重复导致故障的步骤,使要解决的问题出现在被调试的系统中。
    ● 定位根源,即综合利用各种调试工具,使用各种调试手段寻找导致软件故障的根源(root cause)。通常测试人员报告和描述的是软件界面或工作行为中所表现出的异常,或者是与软件需求和功能规约不符的地方,泛指软件缺欠(defect)或者故障(failure)。而这些表面的缺欠总是由于一或多个内在因素所导致的。这些内因要么是代码的行为错误,要么是不行为错误(该作而未作)。
    ● 探索和实现解决方案,即根据寻找到的故障根源、和资源情况、紧迫程度等要求设计和实现解决方案。
    ● 验证方案,在目标环境中测试方案的有效性,又称为回归(regress)测试。如果问题已经解决,那么就可以关闭问题。如果没有解决则回到第3步调整和修改解决方案。
    这些步骤中,定位根源常常是最困难也是最关键的步骤,它是软件调试过程的核心和灵魂。如果没有找到故障根源,那么解决方案便很是隔靴搔痒,或者头痛医脚,白白浪费了时间。
    对软件调试的另一种更通俗的解释是指使用调试工具求解各种软件问题的过程,例如跟踪软件的执行过程,探索软件本身或者与其配套的其它软件或者硬件系统的工作原理等,这些过程的目的有可能是为了去除软件缺欠,也可能不是。

    在了解了软件调试技术的基本概念以后,下面我们来看一下支撑软件调试技术的几种基本机制。
    ● 断点:即当被调试程序执行到某一空间或时间点时将其中断到调试器中。根据中断条件分为如下几种:
    ○  代码断点:当程序执行到指定内存地址的代码时中断到调试器。
    ○  数据断点:当程序访问指定内存地址的数据时中断到调试器。
    ○  I/O断点:当程序访问指定I/O地址的端口时中断到调试器。
    根据断点的设置方法,断点又分为软件断点和硬件断点。软件断点通常是通过向指定的代码位置插入专用的断点指令来实现的,比如IA32 CPU的INT 3指令(机器码为0xCC)就是断点指令。硬件断点通常是通过设置CPU的调试寄存器来设置的。IA32 CPU定义了8个调试寄存器,DR0~DR7,可以最多同时设置4个硬件断点(对于一个调试会话)。通过调试寄存器可以设置以上三种断点中的任一种,但是通过断点指令只可以设置代码断点。

    ● 单步跟踪:即让应用程序按照某单位一步步执行。根据单位,又分几种:
    ○  每次执行一条汇编指令,称为汇编语言一级的单步跟踪。设置IA32 CPU标志寄存器的TF(Trap Flag,即陷阱标志位)位,便可以让CPU每执行完一条指令便产生一个调试异常(INT 1),中断到调试器。
    ○  每次执行源代码(比汇编语言更高级的程序语言,如C/C++)的一条语句,又称为源代码级的单步跟踪。通常高级语言的单步跟踪是通过反复设置CPU的陷阱标志位来实现的,如果当前源代码行还没有执行完,那么调试器重新设置陷阱标志并让程序继续执行,直到该语句结束(EIP指向另一语句)才中断给用户。
    ○  每次执行一个程序分支,又称为分支到分支单步跟踪。设置IA32 CPU的DbgCtl MSR寄存器的BTF(Branch Trap Flag)标志后,便可以启用分支到分支单步跟踪。
    ○  每次执行一个任务(线程),即当一个任务(线程)被调度执行时中断到调试器。IA32架构所定义的任务状态段(TSS)中的T标志为实现这一功能提供了硬件一级的支持,但是很多调试器还有提供这项功能。

    ● 栈回溯(stack backtrace):即通过记录在栈中的函数返回地址显示(追溯)函数调用过程。在将返回地址翻译成函数名时需要有调试符号(debug symbol)的支持。大多数编译器都支持在编译时生成调试符号。微软的调试符号服务器(http://msdl.microsoft.com/download/symbols)提供了大多数Windows系统文件的调试符号,是调试和学习Windows操作系统的宝贵资源。
    ● 调试信息输出(debug output/print):即将程序运行的位置、变量状态等信息输出到调试器、窗口、文件或者其它可以观察到的地方。这种方法的优点是简单方便、不依赖于调试器,但也有明显的缺点,如效率低,安全性差,通常不可以动态开启,且难以管理等。在Windows操作系统中,驱动程序可以使用DbgPrint/DbgPrintEx来输出调试信息,应用程序可以调用OutputDebugString API。
    ● 日志(log):将程序运行的状态信息写入到特定的文件或者数据库中。Windows操作系统提供了记录、观察和管理(删除和备份)日志的功能。Windows Vista新引入了名为Common Log File System(CLFS.SYS)的内核模块,用于进一步加强日志功能。
    ● 事件追踪(event trace):通常用来监视频繁的复杂的软件过程,满足普通日志机制难以胜任的需求。比如监视大信息量的文件操作、网络通信等。ETW(Event Trace for Windows)是Windows操作系统内建的事件追踪机制,Windows内核本身和很多Windows下的软件工具(如Bootvis,TCP/IP View)都使用了该机制。

    在以上机制中,断点和单步跟踪通常必须在有调试器参与的情况下才能使用。调试器(software debugger)是综合提供各种调试功能的软件工具。除了处理断点、单步跟踪、模块映射等调试事件外,调试器通常还提供如下功能:
    ● 观察和编辑被调试程序的内存和数据,如全局变量、局部变量、以及程序的栈和堆等重要数据结构。
    ● 观察和反汇编被调试程序的代码。
    ● 显示线程栈中的函数调用信息。
    ● 管理调试符号。
    ● 控制进程和线程,例如将被调试程序中断到调试器中,和恢复其执行等。
    根据调试器所调试目标程序的工作模式,可以把调试器分为用户态调试器和内核态调试器,前者用于调试用户态下的各种程序(应用程序、系统服务、或者用户态的DLL模块),后者用于调试工作在内核模式的程序,如驱动程序和操作系统的内核部分。WinDbg是微软开发的一个免费调试器,它既可以用作用户态调试器,也可以用作内核态调试器,是调试Windows操作系统下的各种软件的一个强有力工具。我几乎每天都使用WinDbg,它是我的计算机中使用频率最高的软件之一。

    最后,简要地描述一下软件调试技术的几个特征。
    系统性——很多看似简单的调试机制都是依靠系统内的多个部件协同工作而完成的。以软件断点为例,CPU提供了指令支持和硬件级的异常机制,操作系统将异常以调试事件的形式分发给调试器,调试器响应调试事件并与用户交互。如果在做源代码级的调试,那么调试器又需要编译器所产生的调试符号来帮忙。
    全局性——对于一个软件项目,应该在项目的设计和架构阶段就制定出全局的调试支持机制,并贯彻实施。比如,所有模块都应该使用统一的方法来输出调试信息、记录日志、报告错误,并公开统一的接口用做单元测试和故障诊断。这样不仅可以避免重复工作,而且增加了软件的可调适性(debuggability),有利于保证产品的质量和进度。
    困难性——《C语言编程》一书的作者Brian Kernighan曾经说过,“调试天生就比编写代码难上一倍,如果你写出了最聪明的代码,那么你的智商就不足以调试这个代码。”因为,要调试一个程序,就必须深刻理解它的工作原理,不仅要知道how和表层的东西,还要知道why和深层次的内幕。另外,调试需要锲而不舍的探索精神和坚韧的耐力,这也让很多人望而却步。
    综上所述,软件调试技术是与软件开发密不可分的一门技术,其初衷是为了定位和去除软件故障,但因为调试技术所具有的对软件的强大控制力和观察力,其应用早已延伸到了很多其它领域,比如逆向工程、计算机安全等等。

    学习和灵活运用软件调试技术,不仅可以提高程序员的工作效率,而且有利于提升对代码的感知力和控制力,加深对软件和系统的理解。此外,调试技术是解决各种软件难题的一种有效武器。它直击要害、锐不可挡,相对其它间接方法具有明显的优势。
    软件有大美,调试见真功。在寻找银弹的努力还在继续的时候,衷心地希望所有程序员朋友都学会使用调试这把利剑吧,使用它为你披荆斩棘,帮你探索前进。只要你的这把剑依然锋利,那你的软件青春就永远不老。

    发表于 @ 2007年03月21日 12:30:00|评论(loading...)|编辑

    新一篇: 《程序员》07年3期文章试读:网银安全大揭秘—摇摇欲坠的达摩克利期剑 | 旧一篇: 《程序员》07年3期文章试读:程序员的武器大家谈之雷军专访

    评论

    #healer_kx 发表于2007-03-22 17:10:10  IP: 202.108.130.*
    不错不错!
    #bearhunter 发表于2007-05-20 09:41:11  IP: 125.115.13.*
    good
    #PhShentu 发表于2007-06-21 17:18:58  IP: 143.166.255.*
    泛泛而谈,没什么意义~~
    #derek02 发表于2008-07-11 18:39:22  IP: 58.253.107.*
    这只是开端.....
    #qdk0901 发表于2008-09-14 13:46:54  IP: 219.133.248.*
    我觉得很有意义
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © 《程序员》编辑