注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

秒大刀 博客

好好学习 天天向上

 
 
 

日志

 
 
 
 

读《.NET开发人员调试策略》  

2012-01-10 14:53:03|  分类: 读书 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
[读].NET开发人员调试策略 - 秒大刀 - 秒大刀的城堡
对调试进行了一次较系统的学习


第1章 调试简介
  1. 用调试器来逐步遍历代码太耗费时间,只能作为最后一个手段
  2. 要想修复一个bug,您首先要真正了解它

第2章 调试过程中的6点建议
  1. 在检查bug时,不要忽视在此之前出现的任何bug
  2. 当问题变得比您所料想的要困难时,可以后退一步或者暂停一下,仔细考虑,看自己现在所做的是不是必须的,再找找看有没有更好的方法。不要将注意力放在一些小问题上,应该从全局来分析问题。如果某个细节问题很难解决,看看有没有办法能够避开它
  3. 在没有合理的证据时,不要怀疑问题出在硬件、API或OS上
  4. 确定错误就像陪审团确定被告是否犯罪一样——需要有足够的证据,而不是合理的怀疑
  5. 不要在所有的测试计算机上都安装调试工具。在没有安装开发工具的计算机上测试程序。
  6. 确保能够在客户站点上调试您的产品
  7. 在编写完新的代码后就立即在调试器上验证其是否正常工作是避免在产品中出现bug的最有效方法之一

第3章 制定计划
  1. 调试的关键是要保持一种合乎逻辑的思考状态
  2. 在调试时应该处于两种状态中。要么正在验证一种推测,要么正在收集数据以便提出一种新的推测。无论处于那种状态,都应该在大脑里面有一个清晰明确的目标,而不应该毫无计划的摸索
  3. 如果您长时间一直都在做编码的工作,您会发现经常都能够预测出bug的大概位置,根本不要查看代码
  4. 对于bug,可以提出3种猜测然后验证它们。如果找到问题所在,那就再好不过了;如果没有那就应该停止猜测,转而求助于其他的方法。另一组猜测不一定会比第一组猜测更好。将直觉当做一种工具来使用——但是不要花费太多的时间来猜测,因为直觉虽然很有用,但却不是非常可靠。如果前3个猜测都没有解决问题,那就不应该继续投入,而应该寻求其他的调试方法了
  5. 由于没有源码,我们不得不只依靠逻辑推理的方法来排错,一旦开发人员养成了逻辑推理的思维习惯,解决bug的速度就会变快很多
  6. 假设会带来一些麻烦,一旦我们对其深信不疑,这时就很难否决它
  7. 在做出一个假设时一定要保持清醒的头脑
  8. 提出假设时千万不要犹豫,但决不能完全相信一个假设
  9. 在和传统的COM组件交互(或是编写传统的COM组件)时,如果涉及到BSTR字符串,那么就一定要用SysAllocString API来创建一个真正的BSTR字符串,而不仅仅是分配一个标准字符串
  10. 即使没有系统的源代码,演绎式的推理也是可以进行的
  11. 不要毫无计划的调试。总是要有一个目标。应该总是处于两种状态之下:要么在验证一个推测,要么在收集信息以便提出一个推测。尽量不要回退。尽量做到有条不紊,每一次测试都能排除一个方面,然后推进到下一个方面
  12. 谨慎对待假设。提出假设是完全可行的,只是在被卡住时要能够重新考虑它们。决不要陷得太深,以至于忘记抬起头来思考一下是不是走错了路

第4章 断言调试
  1. 断言和错误处理绝对没有任何关系。断言不是错误处理的替代品,也不是错误处理的补充,更不是特殊的错误处理。它们的根本目的完全不同
  2. 尽量避免使用Trace类的Assert方法。Trace类主要是用于记录日志,而不是断言
  3. 不能在Windows服务、ASP.NET、远程对象中简单的使用标准断言
  4. 您可以根据自己需要定制Debug类和Trace类。这些类只委托给成为System.Diagnostics.TraceListener的帮助类,而不是做断言本身的工作。
  5. 好的断言能在错误开始发生的地方就启动调试器,这样就可以节约大量的调试时间
  6. 您可以编写自己的TraceListener类而随心所欲的定制断言。这是ASP.NET或者Windows服务中使用断言的最好方法

第5章 用日志调试
  1. 对于大多数的bug,还是使用调试器更为方便。日志主要用在不能使用调试器的情况。因为您无法使用调试器调试千里之外的客户机上的进程,所以程序日志是针对客户站点的最有效的调试技术
  2. 如果调试器掩盖了问题,我们可以考虑改为使用日志(反之亦然)
  3. 实例化一个System.Diagnostics.StackTrace对象,调用ToString()即可获得堆栈跟踪
  4. 在记录错误和警告时最好积极主动一点,只要是有一点不正常或者可疑的地方,我们就应该将它记录下来
  5. 不要花费太多时间创始创建最完美的系统
  6. Trace.Indent()和Unindent()可以控制日志的缩进
  7. .NET自带3种调试侦听器:DefaultTraceListener(写入OutputDebugString)、EventLogTraceListener(写入系统事件日志)和TextWriterTraceListener(写入文本文件),可以通过.config配置文件动态的调整监听器,而无需重新编译
  8. System.Diagnostics.TraceSwitch可以通过.config配置文件动态的启动或者关闭日志,而无需重新编译
  9. 将有用的消息写入日志非常重要,不应该用纯粹的调试消息来弄乱事件日志

第6章 ASP.NET调试和SQL调试
  1. 编译时的错误好过运行时错误
  2. 许多脚本编写者偏爱直接在工作机器上手动编辑脚本文件,而不喜欢使用编译器
  3. 远程调试器主要是一个针对调试公司内部问题的工具,一下局限性使其无法提供“真正的”远程调试功能:
    • 因为允许任何人调试程序,所以使用该工具会造成安全漏洞,为了进行调试,您需要向远程计算机上的登录账户赋予各种不同的权限。这就意味着两台计算机必须是在相互信任的域中,但大多数客户不会做这种选择
    • 实时软件系统通常会将机密信息存储在数据库中。客户不会同意让您自由使用调试器,因为这样做您就可以访问他们的机密数据
    • 您也许会售给客户一个不带调试符号的软件 版本,为了使用调试器,还需要再向他们发送一个带调试符号的新版本
    • 客户必须同意在生产机上安装远程调试组件,这许多客户不愿如此。系统管理员会犹豫是否安装非必要的软件,因为每多一个软件都会增加系统出现安全漏洞的风险
  4. ASP.NET提供了TraceContext日志类,会为每个页面的每种视图生成一个单独的日志文件
  5. 可以在组件中通过HttpContext.Current.Trace.Write("Whatever")主动写入TraceContext日志,但在非ASP.NET应用中HttpContext.Current对象会为null
  6. 对非常短的时间间隔计时是很困难的,因为随机因素会影响实际的时间
  7. 选中项目属性中的“Enable SQL Debugging”则可在存储过程中设置断点来进行调试。启用SQL调试的负面影响是导致调试器性能下降。如果这个功能被启用,调试器需要连接到SQL Server,这时程序的运行速度就会变慢

第7章 调试远程客户站点
  1. 观察鸟儿飞翔是学习飞行的好方法,就像看一个魔术师的表演来学习魔术的秘诀一样。当您了解了诀窍在哪里之后,就会发现一些您过去没有注意到的东西,因为那个时候您还不清楚该怎样观察
  2. 在时间充足的情况下,只要能无误的再现一个bug,最终就一定能修正它。但如果不能重现这个问题,那就无法保证了
  3. 即使是最好的再现步骤也不可避免的会忽略某些细节,而亲眼观察bug才是最重要的
  4. 对于非常严重的bug,可以让开发人员参与技术支持工作
  5. 同客户互动是一项全职工作,最好由训练有素的支持人员(而不是开发人员)来负责
  6. 开发人员必须注意不要将他们的电子邮件地址或者电话号码留给客户。出色的技术支持部门可以处理大部分客户的问题,很少需要开发人员的直接参与,因此没有必要将开发人员的联系方式留给客户
  7. 技术支持人员接受过如何与客户交流的训练,他们能够过滤普通的客户问题,而只将确实难以解决的问题留给开发人员

第8章 多线程调试
  1. 如果使用得当,多线程非常有用。但是,您需要准备为获得这些好处而大量增加调试时间
  2. 除非您确定需要使用多线程,否则不要在一开始就编写多线程的代码
  3. 只要可以用图表的形式表示,任何事情都很容易理解
  4. lock(this)确保任何时候不会有两个线程同时修改对象中的任何成员数据
  5. 使用线程优先级中的Highest优先级是非常危险的,因为这个级别比Windows系统线程的优先级还要高,这无疑会使其他线程处于资源匮乏状态。
  6. “很少发生”的线程问题就像是很难对付的定时炸弹,当在客户的机器上运行程序处理一些日常事务时,他们随时都有可能爆炸——应该将这些与线程有关的bug优先处理
  7. 用线程调试窗口可以手工控制线程的执行顺序
  8. C++支持在某个变量值发生改变时触发的条件断点,C#不支持该类型的条件断点
  9. 确保总是以相同的顺序来获取锁,那么死锁就不可能发生,不论程序中有多少个线程和涉及到多少把锁
  10. 如果只有一把锁,程序将确保任何申请这把锁的线程都能最终完成自己的任务,然后再将锁释放,而这又使其他线程能够获得这把锁。这种情况下绝对不会有死锁发生。只使用一把锁,这种策略非常简单,很多程序员都不会考虑它,但是它真的值得您认真考虑。只使用一把锁的缺点是它限制了并发执行的线程个数。如果您需要使性能最佳,那么使用一把锁并不合适。但如果只需要“足够好的”性能,那么通常一把锁就足够了;而且这肯定会缩短您的调试周期
  11. 在调试器中运行代码时,很多与线程有关的bug都不会出现。不过,这时还是会有很多其他的bug出现,而调试器的一些特性在调试线程问题时非常有用。这些特性并不是很完美,但总比没有强

第9章 Bug跟踪程序
  1. 对于任何成功的项目,都必须有一个规范将所有已知的bug统一记录在一个地方
  2. 所有的bug都被排列在整个开发小组都能看到的地方——这样就不会再有bug被遗漏掉
  3. 在一个地方总结所有的bug——这就是很多小组使用bug跟踪系统的全部范围了
  4. 常见的工作流程中的一个问题是:人们无法意识到问题什么时候落到自己的职责范围之内
  5. 只有发现bug的人才能将这个bug用就关闭,其他人可以建议将这个bug关闭,但是只有发现bug的人同意以后,您才能够确信这个问题真正得到了处理
  6. 团队应该鼓励每一位成员在填写问题报告之前先进行搜索,查看是不是已经有了相同的问题报告。防止出现重复的问题报告可以节约每个人的时间
  7. 鼓励将自己的推理和假设作为注释添加到每一份的问题报告中,以便队友能够简单的检查您的逻辑
  8. 对于一个小组领导(或者是一个有野心的开发人员)来说,浏览所有的问题报告是一种总览项目并寻找跨组交流机会的好方法
  9. 测试人员和开发人员是站在同一边的——大家都想发布好的产品
  10. 一个优秀的测试人员从来都不会完全采纳开发人员描述的bug原因。测试人员在编写测试用例时应该加入一些判断、常识和随机性
  11. 只要有可能,开发人员就应该尽量在问题报告中传递更多的信息来帮助测试人员
  12. 尝试在一份问题报告中解决两个问题通常会造成混淆,并导致错误的假设。每个重现步骤不同的reopen的问题报告都应该被分拆成一个单独的问题报告

第10章 源代码管理调试
  1. 源代码管理的主要作用就是防止多个开发人员之间相互覆盖代码的修改
  2. 将所有的代码保存到本机上,但是却没有备份,这是很危险的
  3. 就算是出于防止意外删除文件的考虑,独立工作的开发人员也应该经常签入他们所做的修改
  4. 在代码冻结期中限制修改。在发布之前的最后几天,您应该宣布一次“代码冻结(code freeze)”,除了不得不修正的主要bug以外,代码将不再变动。任何对代码的修改都可能会引入新的bug,而这些bug可能会比您修正的bug还要严重。在发布前的一到两周内,除了是特别重要的bug,否则不要修改代码
  5. 一定要写代码签入注释。当您因为一个bug而查看修改历史时,应该可以读到每一次签入的注释并能迅速判断出某次签入是否与这个bug有关系
  6. 在签入代码之前先diff一次
  7. 某些开发阶段,在一个问题上增加额外的开发人员只会使工作进展缓慢,而不是更快。所以您不能将所有闲下来的开发人员都分配到项目中去
  8. 开分支所造成的一个问题是有两个版本的文件需要进行维护。在一个分支中修改过的bug也要在另一个分支中修改,而这很快就会变得单调乏味。您可以暂时在两个分支上并行的工作,但是应该有一个“退出策略”,以便最后可以恢复成为一个分支
  9. 合并代码时要非常小心。应该查看两个版本的文件比较,以确保获得适当的修改。合并本身就是一个非常冒险的方法,所以在整个过程中无比保持警惕。但是,如果不进行必要的合并,生产效率将会降低,所以这种冒险是值得的。只是一定要多加小心!

正文之后
  1. 只要人们还在编写新代码,调试过程将会继续渗透到整个开发周期的大部分阶段
  2. 大部分调试工作仍然是模式识别(pattern recognition)——当您以前见到过类似的bug时,解决当前的bug就会容易得多

2012-1-31
    通过App.config配置文件设置TraceListener可实现灵活的日志输出控制,这能很好的避免在每个应用中都DIY日志系统或者引入log4net

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <trace autoflush="true" indentsize="4">
      <listeners>
        <add name="myListener"
          type="System.Diagnostics.TextWriterTraceListener"
          initializeData="TextWriterOutput.log" />
        <remove name="Default" />
      </listeners>
    </trace>
  </system.diagnostics>
</configuration>

  评论这张
 
阅读(1517)| 评论(2)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018