什么是调试器,它如何帮助我诊断问题?

时间:2014-08-19 13:49:51

标签: debugging language-agnostic

这是一个通用的问题,以帮助那些遇到程序问题的新程序员,但不知道如何使用调试程序来诊断问题的原因。

这个问题涉及两类更具体的问题:

  • 当我运行程序时,它不会产生我期望输入的输出。
  • 当我运行程序时,它会崩溃并给我一个堆栈跟踪。我有examined the stack trace,但我仍然不知道问题的原因,因为堆栈跟踪不能为我提供足够的信息。
  • 当我运行程序时,由于分段错误(SEGV)而崩溃。

降价示例:[What is a debugger and how can it help me diagnose problems?](https://stackoverflow.com/q/25385173)

2 个答案:

答案 0 :(得分:31)

调试器是一个程序,可以在程序运行时检查程序的状态。 technical means it uses for doing this对于理解如何使用调试器的基础知识并不重要。您可以使用调试器在程序到达代码中的特定位置时暂停程序的执行,然后检查程序中变量的值。您可以使用调试器非常缓慢地运行程序,一次一行代码(称为单步执行),同时检查其变量的值。

使用调试器是预期的基本技能

调试器是一个非常强大的工具,可帮助诊断程序问题。调试器适用于所有实用的编程语言。因此,能够使用调试器被认为是任何专业或发烧友程序员的基本技能。使用调试器你自己被认为是基本工作你应该自己自己,然后再向别人寻求帮助。由于此站点适用于专业和发烧友程序员,而不是帮助台或指导站点,如果您对特定程序的问题有疑问,但未使用调试器,则您的问题很可能会被关闭和投票。如果你坚持这样的问题,你最终将被阻止发布更多。

调试器如何帮助您

通过使用调试器,您可以发现变量是否具有错误的值,以及程序中的值是否更改为错误的值。

使用单步执行,您还可以发现控制流是否符合您的预期。例如,是否应该按预期执行if分支。

使用调试器的一般说明

使用调试器的细节取决于调试器,在较小程度上取决于您使用的编程语言。

  • 您可以调试程序附加到已运行程序的进程。如果您的程序被卡住,您可能会这样做。

  • 在实践中,从一开始就在调试器的控制下运行程序通常更容易。

  • 通过指示执行应停止的行的源代码文件和行号,或通过指示方法/函数的名称来指示 程序应该停止执行的位置程序应该停止的程序(如果你想在执行进入方法时立即停止)。调试器用于使程序停止的技术手段称为断点,此过程称为设置断点

  • 大多数modern debuggers are part of an IDE并为您提供方便的GUI,用于检查程序的源代码和变量,使用点击式界面设置断点,运行程序并单步执行。

  • 除非您的程序可执行文件或字节码文件包含调试符号信息,否则使用调试器可能非常困难。您可能需要compile (or recompile) your program slightly differently才能确保信息存在。

答案 1 :(得分:23)

我想补充一点,调试器并不总是完美的解决方案,并且不应该始终是调试的首选解决方案。以下是一些调试器可能不适合您的情况:

  • 你的程序失败的部分真的很大(模块化很差,也许?)你并不确定从哪里开始逐步执​​行代码。单步执行所有操作可能会非常耗时。
  • 您的程序使用了大量的回调和其他非线性流控制方法,这使得调试器在您单步执行时会感到困惑。
  • 您的程序是多线程的。或者更糟糕的是,你的问题是由竞争条件引起的。
  • 包含错误的代码在错误出现之前运行多次。这在主循环中尤其成问题,或者更糟糕的是,在物理引擎中,问题可能是数值问题。在这种情况下,即使设置一个断点,也只会让你多次击中它,而不会出现错误。
  • 您的程序必须实时运行。对于连接到网络的程序来说,这是一个大问题。如果您在网络代码中设置断点,则另一端不会等待您单步执行,它只是暂时停止。依赖于系统时钟的程序,例如,有框架跳过的游戏,也不会好得多。
  • 您的程序执行某种形式的破坏性操作,例如写入文件或发送电子邮件,并且您希望限制需要运行它的次数。
  • 您可以确定您的错误是由到达功能X的错误值引起的,但您不知道这些值来自何处。必须一次又一次地执行该程序,将断点设置得越来越远,这可能是一个巨大的麻烦。特别是如果从整个程序的许多地方调用函数X.

在所有这些情况下,要么让程序突然停止可能导致最终结果不同,要么手动单步搜索导致错误的一行是太麻烦了。无论您的错误是不正确的行为还是崩溃,这同样会发生。例如,如果内存损坏导致崩溃,那么在崩溃发生时,它距离首次发生内存损坏的位置太远,并且不会留下任何有用的信息。

那么,有哪些替代方案?

最简单的就是记录和断言。在不同的时间点将日志添加到您的程序中,并将您获得的内容与您期望的内容进行比较。例如,看看你认为那里的错误函数是否首先被调用。查看方法开头的变量是否与您认为的一样。与断点不同,可以有许多没有特殊情况的日志行。您可以随后搜索日志。一旦您点击了与您预期不同的日志行,请在同一区域添加更多内容。将它缩小得越来越远,直到它足够小以便能够记录出被破坏区域中的每一条线。

断言可用于在错误值发生时捕获它们,而不是一旦它们对最终用户产生可见效果。您捕获的值越快,越接近产生它的线。

重构和单元测试。如果你的程序太大,那么一次测试一个类或一个函数可能是值得的。给它输入,看看输出,看看哪些不是你期望的。能够将整个程序中的错误缩小到单个函数可以在调试时间上产生巨大的差异。

如果发生内存泄漏或内存占用,请使用能够在运行时分析和检测这些内容的相应工具。能够检测实际腐败发生的位置是第一步。在此之后,您可以使用日志返回错误值的位置。

请记住,调试是一个倒退的过程。你有最终结果 - 一个错误 - 并找到它之前的原因。它是关于向后工作的方式,不幸的是,调试器只是向前迈进。这是良好的日志记录和事后分析可以为您提供更好的结果。