什么是最佳的通用调试技巧?

Posted

技术标签:

【中文标题】什么是最佳的通用调试技巧?【英文标题】:What are the best general practice debugging tricks? 【发布时间】:2011-05-27 06:54:33 【问题描述】:

什么是最佳的常规调试技巧?不是与特定平台、语言等相关联的那些,而是那些你发现你使用的那些,无论你在做什么和使用什么系统。

【问题讨论】:

这个问题在这里可能更合适:programmers.stackexchange.com 【参考方案1】:

我将首先提供一个我认为很棒的:

难以发现的错误通常发生在相对罕见的情况下。毕竟,如果程序在您每次运行时都重新启动系统,您通常会修复该错误。其中一些罕见的情况是由执行以前从未执行过的代码路径引起的。当您的程序第一次启动时,您通常会创建许多对象。如果这些构造函数抛出异常,你有一个代码路径,但它们曾经失败过吗?

如果没有,你有一个等待发生的事故。当特定的构造函数失败时,您的程序实际上会做什么?只要你没有测试过代码路径,你就没有完全测试过你的程序。确保您练习了每个代码路径。如果你有一个 "if () ... else ..." 并且你总是执行 "else" 部分,那么 "if" 部分是一个等待发生的错误。

有一个非常简单的方法来处理这个问题,并且这样做在您第一次运行代码时会有所帮助。使用陷阱的美妙之处在于它可以立即帮助您,因此你会很快发现你在编写代码时会自动将它们放入其中。

这由两个方法组成,trap() 和trap(bool)——我已经包含了 Java、C++ 和汇编程序示例,但为了说明,我将在这里使用 C#。陷阱的作用是在您击中一个陷阱时将您放入调试器。首先是示例,然后是为什么它如此有用

// Open an XML file
XmlReader reader;
if (string.IsNullOrEmpty(username)) 
    Trap.Trap();
    reader = XmlReader.Create(filename, xmlSettings);
 else 
    Trap.Trap(!hasDomain);
    Trap.Trap(hasDomain);
    XmlUrlResolver resolver = new XmlZipResolver();
    resolver.Credentials = hasDomain ? new NetworkCredential(user, password, domain) : new NetworkCredential(user, password);
    xmlSettings.XmlResolver = resolver;
    reader = XmlReader.Create(filename, xmlSettings);

即时回报

为什么从一开始就有很大的帮助?因为它可以帮助您一次性完成代码的每个部分。在上面的代码中,您可能首先在没有用户名的情况下进行测试,因此您将在第一个陷阱中陷入调试器。您标记它,然后跨过创建。您可以在确保 xml 正确之后单步执行几行代码,然后再执行。

注意:我标记我击中的陷阱的方法是将 ** 放在该行的开头。当我完成调试会话时,一个简单的编译将显示我添加 * 的每个地方。因为我将 * 添加到同一行,所以行号保持不变并且调试器正确匹配您的源代码行(如果您在点击它时删除带有陷阱的行,调试器将在任何地方关闭一行在那个文件中)。

好的,您已经在此模块上工作了几天,一切正常,您决定尝试使用需要凭据的 XML 文件。当您这样做时,调试器将停止在 else 的顶部。然后,您将逐步执行打开带有凭据的 XML 文件的代码。这种方法的美妙之处在于你不需要记住你没有走过这 4 行代码。你不需要去找那 4 行代码。它只是将您放在调试器中。 我认为这是我使用的最强大的调试工具之一,因为我现在可以轻松单步执行我的代码的每一行 - 并从这种做法中找到足够多的问题以使其非常值得。

二次收益

这种做法还提供了第二个巨大的回报。最初删除的所有陷阱要么是非常常见的代码路径,要么是由于单元测试而专门命中的路径。因此,当您“完成”一个模块并且编写了所有测试时,任何剩余的陷阱都是仍有待编写的测试的指标。您现在有一种非常简单的方法来确定需要编写的剩余单元测试。

并且陷阱已实现,因此它们仅在调试版本上运行。因此,您可以发布带有陷阱的代码(我们不应该这样做,但我们都这样做)并且它不会影响发布版本。

可在One of the Most Powerful Debugging Practices 获得 C#、Java、C++ 和汇编程序的陷阱代码

【讨论】:

【参考方案2】:

简单良好的老式日志记录是有史以来最强大的调试技术。如果一种语言支持条件编译,那么所有的日志记录和断言都可以从发布版本中排除。如果没有 - 您仍然可以在外部使用预处理器。

结构良好的日志记录和全面的断言也有助于更好地自我记录您的代码。

如果您明智地使用日志记录和断言,您将永远不需要交互式调试器。

【讨论】:

【参考方案3】:

我认为没有一两件事是最好的,而是你通过经验随着时间的推移建立起来的过程。我个人确保进行单元测试、出色的交互式调试器和打印输出。

如果我没有针对某个错误的单元测试,我会添加一个有效确定错误的单元测试,然后对其进行跟踪。将单元测试放在一起的过程是探索性的,可以帮助您找到导致错误发生的确切情况。

【讨论】:

以上是关于什么是最佳的通用调试技巧?的主要内容,如果未能解决你的问题,请参考以下文章

BASH 的调试技巧

关于eclise中的调试技巧

Java常用调试技巧(转)

101个MySQL调试和优化技巧

101个MySQL调试和优化技巧

实用调试的技巧,VS编译器常用调试详解