6-4 debugging

1 什么是bug?什么是debug?
bug:–程序中的一种错误,它导致程序以非预期或非预期的方式执行。
–包含大量错误和/或严重干扰其功能的错误的程序称为错误。
–详细说明程序中错误的报告通常称为错误报告、故障报告、问题报告、故障报告、缺陷报告等。

为什么会出现bug?
–代码错误
–未完成的需求或不够详细
–对用户需求的误解
–设计文档中的逻辑错误
–缺少文档
–没有足够的测试

当防御式编程和测试都没能抵挡bug时,你就不得不进行调试了

什么是debug?
▪ 调试是识别错误的根本原因并加以纠正的过程。
–与测试(最初是检测错误的过程)相反,调试是测试成功的结果。
–在一些项目中,调试占据了整个开发时间的50%。
–对于许多程序员来说,调试是编程中最困难的部分。
▪ 与测试一样,调试并不是提高软件质量的方法,而是诊断缺陷的方法。
–软件质量必须从一开始就内置。构建高质量产品的最佳方法是仔细开发需求,设计良好,并使用高质量的编码实践。
–调试是最后的手段。
在这里插入图片描述
可以看出写程序慢的程序员花了大量的时间在debug上

▪ 调试不是测试,而是测试的结果。
–调试过程从执行测试用例开始。
–评估结果,发现预期结果与实际结果之间缺乏对应性。
–调试过程试图将症状与原因相匹配,从而改正错误。

有时候bug的原因和bug发生的地方可能相隔很远,高耦合加剧了这种情况。有时候bug不是由于错误引起的,而是由于系统,例如舍入误差。

错误可能是由于时间问题造成,可能很难复现,可能是间歇性的,可能是由于分布在不同的处理器造成的…

2 调试过程
包括四步:
▪ 再现:找到一种方法来可靠和方便地按需再现问题。
▪ 诊断/定位:构建假设,并通过执行实验对其进行测试,直到已经确定了错误的根本原因。
▪ 修正:设计并实现修正问题、避免引入回归、维护或提高软件整体质量的变更。
▪ 反思:吸取错误的教训。哪里出了问题?有没有其他同样的问题也需要解决的例子?你能做些什么来确保同样的问题不再发生?
在这里插入图片描述
调试中的查找错误占据了debug的绝大多数时间。

(1) 复现错误
首先找到一个产生失败的小的可重复测试用例:可以从测试用例中寻找;如果该错误是用户报告的,则复现较难;如果错误是线程中出现的,则重现较难。

错误发生时源代码的差异会导致你难以复现bug,我们需要确定哪些因素和bug相关,尽量确保环境和变量一致。包括软件版本,软件运行环境,bug发生时输入的数据等。
(2) 诊断错误
1.插桩:我们可以使用插桩代码,比如日志logging来对代码的行为进行记录和了解。
2.分治:通过排除错误模块来定位bug发生的地方
eg:防狼围栏算法
在这里插入图片描述
3.切片
▪ 切片意味着找到程序中有助于计算特定值的部分。
–当出现故障时(程序中某个特定点的错误值),该值的切片由帮助计算错误值的程序行组成。
–导致错误值的bug位于该片段的某个位置
在这里插入图片描述
4.集中注意力在差异上
可以利用版本控制系统,查找在哪次推送后发生了bug
可以使用git-biset命令来查找bug
在这里插入图片描述
(2) 增量调试将错误的原因定义为成功执行与失败执行之间的差异,然后搜索最小原因以查找问题。

还有其他方面的差异:查找其他方面的差异:软硬件环境、JVM参数配置、输入文件、…

*符号化执行:一种分析程序的方法,以确定哪些输入导致程序的每个部分执行。–解释器跟随程序,假设输入值是符号值,而不是像程序的正常执行那样获取实际输入,这是一种抽象解释。–这样,它就得到了程序中表达式和变量的符号表达式,以及每个条件分支的可能结果的符号约束。
符号执行树–符号执行树中的每个节点提供完整状态和相关信息,如符号调用堆栈或到达节点的路径条件。
在这里插入图片描述
6 调试器Debugger
▪ 调试器——调试器在复杂度和功能上都有很大的不同,从简单的面向命令行的示例到完全集成到图形化IDE中的示例。
–它们允许我们在执行时检查代码、设置断点、单步执行和检查程序状态。
▪ 它在开发生命周期的三个不同点上特别有用:在最初的开发过程中,单步执行代码有助于说服我们,它真正做的事情与我们认为我们正在实现的东西是一致的。
–如果我们有一个关于代码为什么以特定方式运行的理论,我们可以使用调试器来确认或反驳这个理论。
–最后,调试器可以帮助我们探索行为方式根本无法理解的代码。

7: 向他人学习
▪ 有时,这个bug会涉及到一种广泛使用的技术(例如,编译器,正在使用的库或框架),在这种情况下,可能会有其他人在您面前遇到同样的问题。
▪ 在这种情况下,对网络的一点研究可以带来好处。也许有人在论坛上问了一个关于同一类故障模块的问题,或者写了一篇博客文章描述了他们陷入的陷阱,结果发现正是你发现自己陷入的陷阱。

3 调试工具

暴力调试:最常见,受欢迎
优点:不需要太多思考
缺点:效率低下,成功率低
分类::使用内存转储进行调试;
根据“在程序中分散打印语句”的常见建议进行调试,使用println打印
使用自动调试工具进行调试。

(1) 事后调试:内存转储
▪ 内存转储:硬盘上的一种文件,包含某一特定时间进程内存内容的副本,当进程因某种内部错误或信号而中止时产生。
▪ 当程序中止时,可以进行内存转储,以便在崩溃时检查程序的状态。
(2) 事后调试:堆栈跟踪
通过调用可抛出类的printStackTrace方法,可以访问堆栈跟踪的文本描述。
一种更灵活的方法是getStackTrace方法,它生成一个StackTraceElement对象数组,您可以在程序中对其进行分析。
StackTraceElement类有方法来获取执行代码行的文件名和行号以及类和方法名。toString方法生成一个包含所有这些信息的格式化字符串
(3) Printf调试
在失败的程序中分散语句以显示变量值比转储好,因为它不是静态的,并且显示程序的动态性。
▪ 打印调试(或跟踪)是监视(实时或记录的)跟踪语句或打印语句的行为,这些语句指示进程的执行流。
▪ 由于在C中使用printf函数,这有时称为printf调试。
▪ 它是使用printf()或系统输出打印(例如,)语句。
▪ 很简单但很有效。
一旦软件对外发布,所有用于debug的print语 句都要去除或禁用。

(4) Logging日志
日志可以定向到不同的处理程序,可以控制台输出或者写入文件
默认情况下日志记录由配置文件控制
在这里插入图片描述
▪ 对于简单的日志记录,使用全局记录器并调用其info方法
如果不想使用全局的logger,可以创建自己的logger
设置日志级别:高于某级别的才会被记录在这里插入图片描述
默认情况下日志会输出到控制台
使用FileHandler将日志记录到文件中
SockerHandler将日志发送到指定主机和端口
StreamHandler:将格式化记录写入OutputStream的简单处理程序。
ConsoleHandler:一个简单的处理程序,用于将格式化记录写入系统错误
FileHandler:处理程序将格式化的日志记录写入单个文件或一组日志文件。
SocketHandler:将格式化日志记录写入远程TCP端口。
▪ MemoryHandler:处理程序缓冲内存中的日志记录。
SimpleFormatter:编写简短的“可读”日志记录摘要。▪ XMLFormatter:写入详细的XML结构化信息。

外部日志记录
▪ 通过拦截软件与其他地方之间的通信量,可以从软件外部获取有用信息(外部日志记录)。
▪ 可以在客户端和服务器之间插入代理。如果您正在使用的协议不存在代理,或者您找不到配置代理以拦截流量的方法,则可以考虑使用网络分析器来捕获所有网络流量

(5) 编译器警告消息
▪ 最简单和最有效的调试工具之一是您自己的编译器。
▪ 将编译器的警告级别设置为可能的最高、最挑剔的级别,并修复代码,使其不会产生任何编译器警告。
▪ 假设编写编译器的人比你更了解你的语言。–如果他们警告你一些事情,通常意味着你有机会学习新的语言。–努力理解警告的真正含义。

有些编译器将警告设置为错误,能使得开发者更加认真的对待警告。
一个团队之间需要统一警告和错误的标准

(6) 调试器:断点
▪ 调试器是使程序员能够监视程序的执行、停止程序、重新启动程序、设置断点和更改内存中的值的软件工具。
▪ 单步执行源代码-断点-单步执行-恢复操作-临时断点
▪ 检查变量
▪ 发布“所有点公告”以更改变量–监视点结合了断点和变量检查的概念。
▪ 在调用堆栈上下移动

▪ 继续执行-继续调试器将运行到下一个断点并暂停。
▪ 调试一步一步地进行–步骤转移将只执行行并转到下一行。如果一个方法将在这一行中被调用,调试器将不会调试到该方法的代码中,并将该方法作为一个完整的步骤完全执行。
–Step Into将只执行行并转到下一行。如果在这一行中要调用一个方法,下一步是进入该方法并继续逐步调试。
–Step Return如果调试器正在一步一步地调试方法,“Step Return”将允许调试器运行整个方法(直到该方法结束),直到它作为一个完整步骤返回为止。
▪ 观察点结合了断点和变量检查的概念。
▪ 最基本的形式指示调试器在指定变量的值发生更改时暂停程序的执行。▪ Eclipse中的条件断点:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值