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

uoifb46i  于 2022-12-04  发布在  其他
关注(0)|答案(2)|浏览(189)

这是一个通用的问题,旨在帮助那些在程序中遇到问题但不知道如何使用调试器来诊断问题原因的新程序员。
这个问题涵盖了三类更具体的问题:

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

e0uiprwp1#

A debugger is a program that can examine the state of your program while your program is running. The technical means it uses for doing this are not necessary for understanding the basics of using a debugger. You can use a debugger to halt the execution of your program when it reaches a particular place in your code, then examine the values of the variables in the program. You can use a debugger to run your program very slowly, one line of code at a time (called single stepping), while you examine the values of its variables.

Using a debugger is an expected basic skill

A debugger is a very powerful tool for helping diagnose problems with programs. And debuggers are available for all practical programming languages. Therefore, being able to use a debugger is considered a basic skill of any professional or enthusiast programmer. And using a debugger yourself is considered basic work you should do yourself before asking others for help. As this site is for professional and enthusiast programmers, and not a help desk or mentoring site, if you have a question about a problem with a specific program, but have not used a debugger, your question is very likely to be closed and downvoted. If you persist with questions like that, you will eventually be blocked from posting more.

How a debugger can help you

By using a debugger you can discover whether a variable has the wrong value, and where in your program its value changed to the wrong value.
Using single stepping you can also discover whether the control flow is as you expect. For example, whether an if branch executed when you expect it ought to be.

General notes on using a debugger

The specifics of using a debugger depend on the debugger and, to a lesser degree, the programming language you are using.

  • You can attach a debugger to a process already running your program. You might do it if your program is stuck.
  • In practice it is often easier to run your program under the control of a debugger from the very start.
  • You indicate where your program should stop executing by indicating the source-code file and line number of the line at which execution should stop, or by indicating the name of the method/function at which the program should stop (if you want to stop as soon as execution enters the method). The technical means that the debugger uses to cause your program to stop is called a breakpoint and this process is called setting a breakpoint.
  • Most modern debuggers are part of an IDE and provide you with a convenient GUI for examining the source code and variables of your program, with a point-and-click interface for setting breakpoints, running your program, and single stepping it.
  • Using a debugger can be very difficult unless your program executable or bytecode files include debugging symbol information and cross-references to your source code. You might have to compile (or recompile) your program slightly differently to ensure that information is present. If the compiler performs extensive optimizations, those cross-references can become confusing. You might therefore have to recompile your program with optimizations turned off .
o0lyfsai

o0lyfsai2#

我想补充的是,调试器并不总是完美的解决方案,也不应该总是调试的首选解决方案。下面是调试器可能不适合你的几种情况:

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

在所有这些情况下,要么让程序突然停止可能导致最终结果不同,要么手动单步执行以查找导致bug的行太麻烦了。无论bug是错误行为还是崩溃,这都可能发生。例如,如果内存损坏导致崩溃,那么在崩溃发生时,它离内存损坏第一次发生的位置太远,没有留下任何有用的信息。
那么,还有什么选择呢?
最简单的方法就是记录日志和Assert。在程序的不同点添加日志,并将得到的结果与预期的结果进行比较。例如,查看您认为存在bug的函数是否在第一时间被调用。查看方法开始处的变量是否与您所认为的一样。与断点不同的是,有很多日志行没有发生什么特别的事情是可以的。您可以在以后简单地搜索日志。一旦您找到了与预期不同的日志行,就在同一区域添加更多的日志行。将范围越缩小越好,直到它小到可以记录被窃听区域的每一行。
Assert可用于在错误值出现时捕获错误值,而不是在它们对最终用户产生可见影响时捕获错误值。捕获错误值越快,就越接近产生它的行。
重构和单元测试。如果你的程序太大,一次测试一个类或一个函数可能是值得的。给予它输入,看看输出,看看哪些不是你所期望的。能够把bug从整个程序缩小到单个函数,可以在调试时间上有很大的不同。
在内存泄漏或内存践踏的情况下,使用适当的工具,能够分析和检测这些在运行时。能够检测到实际损坏发生的位置是第一步。在这之后,您可以使用日志工作的方式回到哪里引入了不正确的值。
请记住,调试是一个向后的过程。你有最终的结果--一个bug --并找到它之前的原因。这是关于你向后工作的方式,不幸的是,调试器只向前走。这是好的日志和事后分析可以给予你更好的结果。

相关问题