为什么在IDE中进行更好的调试?[关闭]


145

我从事软件开发已有20多年了,使用C,Perl,SQL,Java,PHP,JavaScript和最近的Python进行编程。我从来没有遇到过无法使用一些仔细的思考和适当放置的调试print语句进行调试的问题。

我尊重很多人说我的技术是原始的,在IDE中使用真正的调试器要好得多。但是从我的观察来看,IDE用户似乎并没有使用我的石刀和熊皮来调试,但是调试得比我更快。我真诚地愿意学习正确的工具,但从未展示过使用可视调试器的明显优势。

而且,除了如何设置断点和显示变量内容的基础知识之外,我从未看过任何教程或书籍来展示如何使用IDE进行有效调试。

我想念什么?是什么使IDE调试工具比对诊断print语句的仔细使用有效得多?

您是否可以建议一些资源(教程,书籍,屏幕录像),以显示IDE调试的更好技巧?


甜蜜的答案!非常感谢大家抽出宝贵的时间。非常有启发性。我投了很多票,没有投反对票。

一些值得注意的地方:

  • 调试器可以帮助我进行临时检查或更改变量,代码或运行时环境的任何其他方面,而手动调试需要我停止,编辑和重新执行应用程序(可能需要重新编译)。
  • 调试器可以附加到正在运行的进程或使用故障转储,而对于手动调试,“重现步骤”缺陷是必需的。
  • 调试器可以轻松且以更具可读性的方式显示复杂的数据结构,多线程环境或完整的运行时堆栈。
  • 调试器提供了许多方法来减少执行几乎所有调试任务的时间和重复工作。
  • 视觉调试器和控制台调试器都很有用,并且具有许多共同的功能。
  • 集成到IDE中的可视调试器还使您可以在单个集成开发环境中方便地访问智能编辑和IDE的所有其他功能(因此而得名)。

14
我认为您错误地假设使用调试器需要IDE?无论是否在IDE中使用调试器,调试器都是无价的工具。
codelogic

我同意,这个问题几乎声称您无法在IDE中使用调试器进行调试。您可以在带有或不带有IDE的情况下运行调试器,我确信他知道这一点:)也许他是专门询问可视调试器?
hhafez

是的,可视调试器。我也知道像gdb这样的非可视调试器,但是它们并没有得到相同的支持。
比尔·卡文

我认为问题的主要问题是您将IDE误认为调试器。您询问有关在IDE中进行调试的信息,但将IDE与调试器等同起来,“ non-IDE”似乎意味着不使用调试器。IDE!=调试器。我讨厌IDE,但我喜欢调试器,要回答您的问题,我需要解释一下IDE和调试器的不同之处。就像在问:“是地球转还是我可以买自行车?”
stefanB 2010年

6
@stefanB:对于这个问题,我收到了很多很好的答案,这表明您正在不必要地学步。
Bill Karwin

Answers:


108

IDE调试器将通过代码中的跟踪消息为您提供一些功能的一些示例:

  • 在任何时间点查看调用堆栈,为您提供当前堆栈框架的上下文。
  • 进入无法为了添加跟踪而重新编译的(假设您有权访问调试符号)
  • 在程序运行时更改变量值
  • 编辑并继续-能够在运行时更改代码并立即查看更改结果
  • 能够观察变量,查看变量何时更改
  • 能够跳过或重复代码部分,以查看代码的性能。这使您可以在进行理论更改之前对其进行测试。
  • 实时检查内存内容
  • 引发某些异常时提醒您,即使它们由应用程序处理也是如此。
  • 条件断点 ; 仅在特殊情况下才停止应用程序,以允许您分析堆栈和变量。
  • 在多线程应用程序中查看线程上下文,这可能很难通过跟踪来实现(因为来自不同线程的跟踪将在输出中交错)。

总而言之,打印语句(通常)是静态的,如果原始语句不够详细,则需要重新编译以获取更多信息。IDE消除了此静态障碍,为您提供了动手可得的动态工具包。

当我刚开始编码时,我不明白调试器有什么大不了的,我以为我可以通过跟踪实现任何事情(当然,这是在unix上,而调试器是GDB)。但是一旦学习了如何正确使用图形调试器,就不再需要返回打印语句了。


3
动态调试方面很不错。
比尔·卡文

2
比手表更方便,也可以在单步执行代码时将鼠标悬停在变量名上,然后获得该值的工具提示。您甚至可以通过单击工具提示来更改该值。那ruxx0rs。
乔恩·戴维斯

其中的大部分是带有或不带有IDE的交互式调试器的属性。也就是说,GDB可以实现该列表上的所有内容(可能有更改代码的地方)。
dmckee ---前主持人小猫,

1
是的,但是OP询问“是什么使IDE调试工具比对诊断打印语句的周到使用更有效?” 我在比较IDE调试工具与打印语句,而不是IDE调试与控制台调试。
LeopardSkinPillBoxHat 2009年

编辑并继续是一个非常强大的工具。我真的希望有更多的编译器支持它。它可以养成不良习惯吗?当然。甚至源代码控制也可能导致不良的开发实践。E&C使程序员能够更有效地跟踪问题。
darron

34
  • IDE调试器使您可以在运行时更改变量的值。

  • IDE调试器使您可以查看执行开始时不希望看到的变量的值。

  • IDE调试器使您可以查看调用堆栈并检查传递的怪异值的函数状态。(想想这个函数是从数百个地方调用的,您不知道这些奇怪的值来自何处)

  • IDE调试器使您可以根据条件而不是行号有条件地中断代码中任意点的执行。

  • IDE调试器可以让您在发生未处理的异常的情况下检查程序的状态,而不仅仅是浪费时间。


1
@Joe,在比尔的问题中,我认为它们等效的。Bill在谈论printf调试。在这一点上,调试器是否与编辑器以及编译器集成在一起并不重要。
罗伯·肯尼迪

事实是,不是IDE调试器的gdb具有所有这些功能
Tamas Czinege,2009年

问题是关于IDE调试器与打印样式调试,因此我将其保留不变。
递归

是的,这些是调试器的非常有效的优势。我知道控制台调试器中也提供了这些功能,但是它们无疑是我所提问题的良好答案。
比尔·卡温

16

这是您绝对无法使用“打印”语句调试的一件事,即当客户为您带来内存转储并说“您的程序崩溃了,您能告诉我为什么吗?”


5
我很感兴趣,调试器如何解决这个问题?令人敬畏的是:)
TheIronKnuckle

14
  • 在整个代码中打印语句会降低可读性。
  • 仅出于调试目的添加和删除它们非常耗时
  • 调试器跟踪调用堆栈,从而轻松了解您的位置
  • 变量可以即时修改
  • 临时命令可以在执行暂停期间执行,以帮助诊断
  • 可以与打印语句结合使用:Debug.Write(“ ...”)

1
谢谢你的清单。并非所有这些点都是视觉调试的引人注目的优势。例如,我可以在许多语言环境中轻松地打印堆栈跟踪。
比尔·卡文

1
对于#2:有时将调试器连接到服务器可能会更加耗时:)
inkredibl,2009年

9

我认为使用打印语句进行调试是一门失传的艺术,对于每个开发人员来说都很重要。一旦您知道该怎么做,与通过IDE调试相比,某些类型的Bug变得更容易调试。知道此技术的程序员也非常了解将什么有用的信息放入日志消息中(更不用说您最终会最终阅读日志),也可以用于非调试目的。

就是说,您确实应该知道如何使用逐步调试器,因为对于其他类型的错误,它很容易。我将由已经发布的其他出色答案来解释原因:)


同意,并感谢您的观点。仅仅因为高级可视调试器很有用,并不意味着它们是所有任务的最佳选择。就像其他任何工具一样,它们也各有特色。
比尔·卡文

同意这一点,使用打印是一项重要技能。当我只有一个有限的工具集时,可以保存一个文件,然后运行它,这为我节省了很多时间(对于Web开发而言尤其如此)。
inkredibl

2
另外,如果要解决多线程争用条件,则必须使用print。使用断点IDE调试器几乎不可能找到这些错误。
Radu094

1
以我的经验,在多线程情况下添加print语句并没有太大帮助,因为print本身会导致某种线程同步,从而改变了bug的性质。
the_mandrill

我读完第一句话
后就投票了

6

从我的头顶上:

  1. 调试复杂的对象 -调试器使您可以深入了解对象的内部。例如,如果您的对象具有一组复杂对象的数组,则打印语句仅能使您受益。
  2. 跨过代码的能力 -调试器还将使您可以跳过不想执行的过去的代码。没错,您也可以手动执行此操作,但是您需要注入更多的代码。

但是我现在可以使用“手动”调试来完成这两项工作……无论是注入代码还是弄清迷宫般的一系列IDE菜单选项。
比尔·卡温

是的你可以。但是你真的想要吗?您询问了使用IDE进行调试更好的原因,而不是仅由IDE提供的原因。
凯文·庞

很公平。假设人们学会了使用这些功能的菜单功能,那么接下来它们比每次编写新的调试代码都容易。
比尔·卡温

1
@BillKarwin不确定其他IDE,但是在Visual Studio中没有用于跳过代码执行的“菜单项”。您可以将当前执行点“拖动”到新行。放置断点也一样容易(单击所需的行号。除了在VS中打开“监视”窗口外,我认为我没有去过菜单的任何事情,只需要完成一次(或者如果您的窗口布局丢失,或者您关闭监视窗口)
格兰特·彼得斯

感谢@GrantPeters的提示!
比尔·卡温

4

作为在IDE中进行调试的替代方法,您可以尝试使用带有php库的出色的Google Chrome扩展PHP控制台,该 允许:

  • 在Chrome JavaScript控制台和通知弹出窗口中查看错误和异常。
  • 转储任何类型变量。
  • 远程执行PHP代码。
  • 通过密码保护访问。
  • 根据请求对控制台控制台进行分组。
  • 在文本编辑器中跳转至错误文件:行。
  • 将错误/调试数据复制到剪贴板(适用于测试人员)。

3

我从事开发工作已有近20年的时间,但是我发现使用IDE /调试器可以:

  • 查看我可能不认为要包含在打印声明中的所有内容
  • 逐步检查代码是否与我认为的路径匹配
  • 将变量设置为特定值以使代码具有特定分支

好点!当然,这些可以减少“编辑,重新编译,运行”的重复。
比尔·卡温

2

使用IDE的原因之一可能是现代IDE支持的不只是简单的断点。例如,Visual Studio提供以下高级调试功能:

  • 定义条件断点(仅在满足条件时才中断,或仅在执行断点处的语句的第n次时中断)
  • 在未处理的异常或要抛出(特定)异常事件时中断
  • 调试时更改变量
  • 通过设置要执行的下一行来重复一段代码
  • 等等

另外,使用调试器时,调试完成后,无需删除所有打印语句。


这些就是很好的例子。我想我从未见过展示如何使用它们的体面的教程或文章。另外,我很少使用VS或其他Microsoft解决方案。
比尔·卡文

删除调试代码的琐事是有效的,尽管我经常可以执行“ svn revert”来摆脱它。
比尔·卡文

#ifdef DEBUG printf(“ variable any now现在是%d \ n”,var); fflush(stdout); #endif
Arthur Kalliokoski 2010年

@ M4N,仅执行版本还原就非常容易
Pacerier,2015年

2

令我惊讶的是我没在另一个答案中看到这两种调试方法并不互斥

printf即使使用标准调试器(无论是否基于IDE),调试也可以很好地进行。特别是使用日志记录框架,因此您可以将全部或大部分内容保留在已发布的产品中,以帮助诊断客户问题。

正如这里所有其他答案中所述,关于标准调试器的关键好处是,它使您可以更轻松地检查(并可能更改)程序状态的详细信息。您无需预先知道可能要看的内容-都可以随时获得(或多或少)。


另一个答案包括非互斥性的观点,但这是很容易理解的。
比尔·卡温

2

以我的经验,简单的打印输出具有一个巨大的优势,而这似乎没有人提及。

IDE调试器的问题在于所有事情都是实时发生的。您在某个时间停止该程序,然后一次执行一个步骤,如果突然想看看以前发生了什么,就不可能返回。这与我们大脑的运作方式完全不一致。大脑收集信息,并逐渐形成一个观点。这样做可能需要多次迭代事件,但是一旦跨过特定点,就无法返回。

与此相反,选定的一系列打印输出/记录为您提供了“时间事件的空间投影”。它为您提供了所发生事件的完整故事,并且只需向上和向下滚动,您就可以非常轻松地返回和第四次返回。这样可以轻松回答“ A发生在B发生之前”的问题。它可以使您看到甚至不想要的图案。

所以以我的经验。IDE和调试器是出色的工具,可以解决单个调用堆栈中出现问题时的简单问题,并可以在发生特定故障时探索计算机的当前状态。

但是,当我们处理涉及逐渐变化的状态的更困难的问题时。例如,一种算法破坏了数据结构,反过来又导致另一种算法失败。或者,如果我们要回答“这种情况多久发生一次”,“按照我想像的顺序和方式发生,就可以发生”。等等。那么“旧的失败”日志记录/打印技术具有明显的优势。

最好的办法是在最适合的情况下使用这两种技术,例如使用日志记录/打印输出来查找一些错误,然后在需要更详细地研究当前状态的断点处暂停。

也有混合方法。例如,当您执行console.log(object)时,您会在日志中获得一个数据结构窗口小部件,您可以对其进行扩展和更详细地进行研究。与“死”文本日志相比,这是许多明显的优势。


1

因为使用print语句调试多线程应用程序将使您大吃一惊。是的,您仍然可以使用print语句来完成此操作,但是您将需要很多语句,并且需要从语句中分解出顺序打印来模拟多线程执行,这将需要很长时间。

不幸的是,人脑只有单线程。


是的,可以在打印字符串的前面加上线程号或其他内容,或者按线程将输出格式化为列(如果没有太多)。但我明白你的意思。
比尔·卡文

1
需要记住的另一件事是,有时print()语句会同步线程。我曾经看到一个问题,其中启用了调试日志的应用程序可以正常运行,但是禁用它们后,它立即崩溃了,我们发现的问题是该应用程序正在打印同步,从而导致其正常运行。
inkredibl

1
实际上,根据我的经验,好的日志库和一些格式巧妙的打印(非同步)状态程序有时是调试(和理解)一些致命的多线程错误的唯一方法。断点(以及编译器添加的其他调试符号)可以将错误的环境更改为无法找到/复制/理解竞争条件的程度。
Radu094

1

由于您要求提供指向书的指针...就Windows调试而言,约翰·罗宾斯(John Robbins)提供了几本有关Windows调试的好书:

为Microsoft .NET和Microsoft Windows调试应用程序

请注意,最新版本(Debugging Microsoft .NET 2.0 Applications)仅是.NET,因此,如果要进行本机代码调试(包括.NET和本机),则可能需要一个较旧的版本(如第一个链接)。


感谢您的书籍​​提示!我很少在Microsoft平台上使用开发,但是我很欣赏这些参考。
比尔·卡温

1

我个人认为答案很简单:“集成的调试器/ IDE可以快速为您提供大量不同的信息,而无需打入命令。这些信息往往会摆在您面前,而您又不会告诉它要做什么。给你看

轻松中的信息能够被检索是什么使他们更好的不仅仅是命令行调试,或“printf”上调试。


这是一个很好的总结或概括性陈述,与提供许多其他答案的特定示例相匹配。
比尔·卡温

干杯比尔。我觉得争论功能vs功能是没有意义的,因为大多数时候您都可以在两种类型的调试器中执行所需的操作。集成的只是使过程变得简单得多(如果做得好,例如VS)。
OJ。

1

调试器相对于printf的优势(注意不是IDE调试器,而是任何调试器

  1. 可以设置观察点。这是我发现内存损坏的最喜欢的方法之一

  2. 可以调试目前无法重新编译的二进制文件

  3. 可以调试需要很长时间才能重新编译的二进制文件

  4. 可以随时更改变量

  5. 可以即时调用函数

  6. 不存在调试状态菜单未刷新的问题,因此无法准确地调试时序问题

  7. 调试器可帮助进行核心转储,而打印语句则不会


1

这是我在VS.NET调试窗口上最常用的:

  • 调用堆栈,这也是弄清楚别人代码的好方法
  • 当地人和手表。
  • 立即窗口,基本上是一个C#控制台,还允许我更改变量内容,初始化内容等。
  • 能够跳过一行,将下一条语句设置为在其他地方执行。
  • 将鼠标悬停在变量上并具有工具提示可以向我显示其值的能力。

总而言之,它为我提供了我执行代码状态的360度视图,而不仅仅是一个小窗口。

从来没有找到一本教这种东西的书,但话又说回来,这似乎很简单,几乎就是所见即所得。


+1,至少可以解决我的问题中与资源有关的部分以了解更多信息。
比尔·卡温

0
  • 调试器可以附加到正在运行的进程

  • 通常更容易从调试器调试线程代码


0

使用IDE调试器,无论何时暂停执行,您都可以看到当前作用域中所有变量的值(一直到调用堆栈)。

打印语句可能很棒,但是在任何给定位置将大量信息转储到屏幕上都会产生整体很多的print语句。

另外,许多IDE调试器允许您键入并评估方法,并在暂停时评估成员,这进一步增加了您必须执行的打印语句数量。

我确实觉得调试器对于某些语言要比对其他语言更好。

我的普遍看法是,IDE调试器对于托管语言(如Java或C#)绝对非常好,对C ++相当有用,对脚本语言(如Python)不是很有用(但是可能我只是没有尝试过任何脚本语言的调试器)。

当我进行Java开发时,我绝对喜欢IntelliJ IDEA中的调试器。使用Python时,我只使用打印语句。


是的,我同意动态语言可以让您跳过重新编译步骤。您可以添加其他诊断打印并继续。我可以想象,如果您必须在每次这样的编辑后重新编译,动态调试器可以为您节省更多时间。
比尔·卡文

0

如上文所述:Debugger!= IDE。

gdb和(过去)TurboDebugger(独立)对于它们支持的语言都可以正常工作,谢谢。(或什至是更老的技术:Clipper调试器本身链接到xBase可执行文件中)-这些都不要求IDE

同样,尽管C / ++编码很少见,但printf语句有时会掩盖您要查找的错误!(例如,堆栈上的自动变量中的初始化问题或内存分配/对齐)

最后,正如其他人所述,您可以同时使用两者。一些实时问题几乎需要打印,或者至少需要明智的“ * video_dbg =(is_good?'+':'-');”。到视频存储器的某个地方。我的年龄正在显示,这是在DOS下:-)

TMTOWTDI


0

除了其他张贴者所说的很多内容外,我真的很喜欢与计算机一起单行浏览,因为这迫使我一次只考虑一行。通常,我只是在单击“下一行”按钮时就不得不查看该错误,而根本不查看变量值。但是,比尔,我认为我的回答不会对您有帮助,因为您可能已经具备了这项技能。

就学习资源而言,我什么也没用-我只是探索所有菜单和选项。


0

这是来自真正程序员的真正问题吗?

任何人甚至花费5分钟的时间使用打印语句进行调试以及使用IDE进行调试-它将不加要求地向他/她发出OCCUR!


这是一个有趣的问题,来自一个绰号为“ Annon”的人。
比尔·卡温

你们中间的他是最聪明的人,他知道他的智慧真的一文不值。(苏格拉底)
雅克·德·霍格

0

我同时使用了打印和IDE进行调试,而我宁愿使用IDE进行调试。对我而言,唯一无法解决的情况是在时间紧迫的情况下(例如调试在线游戏),在这种情况下,您会用打印语句填充代码,然后在出现严重错误之后查看日志文件。然后,如果仍然无法解决问题,请添加更多打印件并重复。


0

只是想提到控制台调试器vs printf和IDE中vs调试器的有用功能。

您可以附加到远程应用程序(显然是在DEBUG模式下编译的),并检查其状态,以使用POSIX将调试器输出转储到文件中 tee实用程序。与printf相比,您可以选择在运行时将状态输出到何处。

当我调试在攻击性环境中部署的Adobe Flash应用程序时,它对我有很大帮助。您只需要定义一些操作,即可在每个断点上打印所需的状态,使用来启动控制台调试器fdb | tee output.log,并遍历一些断点。之后,您可以打印日志并通过对不同断点的状态进行全面比较来分析信息。

不幸的是,此功能[登录文件]在GUI调试器中很少可用,从而使开发人员可以比较对象的状态。

顺便说一句,我的观点是,在启动调试器之前,应该先计划要在哪里调试什么。


谢谢!但是我不确定我是否同意GUI调试器缺少远程调试功能。实际上,大多数确实具有此功能,但是设置起来有点麻烦。无论如何,我想知道您是否已签出DTrace-听起来像您想要的那样。
Bill Karwin

@Bill Karwin:我的意思是能够登录到文件=)
newtover 2010年

0

另一件事是,如果您加入了一个新的旧项目,但是没人真正知道代码是如何做的,那么您就无法通过回显变量/对象/ ...来进行调试。b / c您根本不知道什么代码是完全执行。

在我的工作中,我正好遇到这种情况,可视化XDebuging可以帮助我了解正在发生的事情以及发生的一切。

最好的祝福

拉斐尔


0

除了已经提到的许多内容之外,调试器相对于printf的最重要优点之一是,使用printf语句假定您知道该错误驻留在哪个函数中。在许多情况下,您不需要这样做,因此必须进行一些猜测并将print语句添加到许多其他函数中才能进行本地化。该错误可能存在于框架代码中,或者与您认为的位置相去甚远。在调试器中,设置断点来检查代码的不同区域和不同时间点的状态要容易得多。

同样,一个不错的调试器将允许您通过将条件和操作附加到断点来进行printf样式的调试,这样您仍然可以保留printf调试的好处,而无需修改代码。


0

在错误日志和外壳程序访问不可用的环境(例如共享主机)中,IDE中的调试非常有用。在这种情况下,具有远程调试器的IDE是唯一允许您执行诸如view stderrstdout。之类的简单操作的工具。


0

使用打印语句的问题是它会使您的代码混乱。IE,您有一个包含10个部分的函数,并且您知道它会在某处崩溃,但是您不确定在哪里。因此,您添加了10条额外的打印语句以查明错误的位置。找到并解决了您的错误之后,现在必须通过删除所有这些打印语句来进行清理。也许您会那样做。也许您会忘记,它将最终投入生产,并且用户控制台将充满调试打印。


0

哇,我喜欢这个问题吗?我从来不敢摆姿势

人们似乎只是有不同的工作方式。对我来说,最有效的方法是:

  • 对代码有扎实的思维模式,包括内存管理
  • 使用检测(如打印语句)来跟踪正在发生的事情。

我已经获得了超过40年的实际编程经验,每天都在C ++和Python的非平凡的技术和科学应用程序中工作,而且我的个人经验是调试器对我毫无帮助。

我不是说那很好。我不是说那很糟糕。我只想分享。


1
您为什么认为这是一个好答案?您认为这对Stackoverflow是个好问题吗?
DavidG

我花了相当长的时间才接受某些人对调试器的优化工作。我想帮助其他具有相同心态的人早日达到目标。至于这个问题,我认为这是有帮助的,因为它公开了禁忌……
雅克·德·胡格

我希望我的首要问题能够帮助您得出正确的结论,但不幸的是,事实并非如此。这个问题是脱题的,因为它主要是基于意见的,您甚至可以看到它现在已经关闭(您的答案使问题跳到了首页,并且引起了足够的关注,使人们意识到这不太适合SO)。
DavidG

您是否知道任何(高质量)论坛(或SO中的标签)都可以很好地提出这样的问题?
雅克·德·胡格

恐怕SO网络中没有地方允许提出意见问题。
DavidG

-2

不只是调试。IDE可通过多种方式帮助您更快地构建更好的软件:

  • 重构工具
  • intellisense使api更容易被发现,或提醒您熟悉项目的准确拼写/大小写(如果您使用同一系统已有15年之久,则用处不大,但这很少见)
  • 通过自动填充变量和类名来节省键入时间
  • 在开始编译之前就发现某些类型的错误
  • 自动跳转到变量/方法/类声明/定义,即使它们不在同一文件或文件夹中。
  • 打破未处理和已处理的异常

我可以继续。


2
呃...这不是问题。
vmarquez,2009年

2
这些也都是IDE的好功能,但是其中大多数都与所谓的“智能编辑”有关。我了解智能编辑器的价值,但是视觉调试正是我要问的问题。
比尔·卡温

尽管我确实知道,将调试器与智能编辑器和所有其他功能集成在一起是有价值的。
比尔·卡温
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.