代码之家  ›  专栏  ›  技术社区  ›  Fung

调试的标准方法

  •  6
  • Fung  · 技术社区  · 17 年前

    调试问题的标准方法是什么?这似乎是一个相当宽泛的问题,你们中的一些人回答说“这取决于问题”,但我认为我们中的许多人是凭直觉进行调试的,实际上还没有尝试过使用我们的流程。这就是为什么我们说“视情况而定”。

    经过一番思考后,我意识到我的调试方式实际上相当单调。我将首先尝试可靠地复制问题(尤其是在本地计算机上)。然后通过一系列的消除(我认为这是与问题相关的)尝试识别问题。

    所以,我只是想知道你们这些家伙到底做了些什么?如果你必须用文字形式化它,你会说你的调试过程是什么?

    顺便说一句,我们还没有发现我们的问题=)

    11 回复  |  直到 14 年前
        1
  •  7
  •   Greg D    17 年前

    我的方法根据我对手头系统的熟悉程度而有所不同。通常我会这样做:

    1. 如果可能的话,复制故障。
    2. 如果我熟悉这个系统,我可能对根本原因有一个很好的猜测。如果没有,我开始通过软件机械地追溯数据,同时挑战软件所做的基本假设。
    3. 如果问题似乎有一个一致的触发器,我可以使用调试器手动遍历代码,同时挑战代码所做的隐式假设。

    当然,追根溯源是事情变得棘手的地方。在这一点上,拥有一个转储(或者更好地说,一个活动的、中断的流程)是非常宝贵的。

    我认为调试过程中的关键点是挑战预先的概念和假设。 我或同事在该组件中发现错误的次数

        2
  •  4
  •   Jonathan Leffler    10 年前

    考虑得到这本书 Debugging

    • 让它失败
    • 别想了,看吧
    • 分而治之
    • 一次改变一件事
    • 检查插头
    • 见识
    • 如果你没修好,它就没修好

    最后一点与软件行业特别相关。

        3
  •  3
  •   Spike Williams    17 年前

        4
  •  3
  •   Gabi Davar    7 年前

    我在网上选的,或者是我想不起来的书 也许 一直在编码(或…)

    调试101:

    • 复制
    • 逐渐缩小范围
    • 避免调试器

    心理方法:

    • 橡皮鸭调试
    • 不要猜测
    • 了解问题和解决方案
    • 休息一下
    • 考虑多种原因

    防虫方法:

    • 监控你自己的错误注入习惯
    • 尽早引入调试辅助工具
    • 松耦合与信息隐藏
    • 编写回归测试以防止再次发生

    技术方法:

    • 惰性跟踪语句
    • 查阅第三方产品的日志文件
    • 在web上搜索堆栈跟踪
    • 以合约方式引进设计
    • 一笔勾销
    • 间歇性错误
    • 爆炸局部性
    • 介绍虚拟实现和子类
    • 重新编译/重新链接
    • 探讨边界条件和特殊情况
    • 检查版本依赖关系(第三方)
    • 检查最近更改的代码
    • 不要相信错误消息
    • 图形错误
        5
  •  2
  •   Canopus    17 年前

    通过这种方法,大多数多线程同步问题都可以很容易地得到解决。

    此外,您还需要很好地了解正在使用的调试器及其功能。我在Windows应用程序上工作,发现windbg在查找bug方面非常有用。

        6
  •  2
  •   ojblass    17 年前

    将bug简化为最简单的形式通常会使您更好地理解问题,并在必要时增加让他人参与的好处。

    设置一个快速复制场景,以便有效地利用您的时间来测试您选择的任何低磷症。

    创建工具以快速转储环境以进行比较。

    检查系统日志是否存在任何警报。

    查看文件日期和时间戳,以了解问题是否可能是最近引入的。

    在源存储库中查找相关模块中的最新活动。

    应用演绎推理和 Ockham's Razor

        7
  •  1
  •   Jack BeNimble    17 年前

    我也非常喜欢使用消除过程。排除变量极大地简化了调试任务。这通常是第一件应该做的事情。

    另一个真正有效的方法是,如果可能,回滚到上一个工作版本,然后重试。这可能是非常强大的,因为它为您提供了坚实的基础,使您能够更加小心地进行操作。这方面的一个变化是,让代码工作到一个点,使用更少的功能,而不是不使用更多的功能。

    当然,重要的是不要只是尝试。这会增加你的绝望,因为它永远不会起作用。我宁愿跑50次来收集关于这个bug的信息,也不愿疯狂地挥杆,希望它能起作用。

        8
  •  1
  •   dbyron    17 年前

    我发现“调试”的最佳时间是在编写代码时。换句话说,要有防御性。检查返回值,自由地使用断言,使用某种可靠的日志记录机制并记录所有内容。

    为了更直接地回答这个问题,我调试问题的最有效方法是阅读代码。拥有日志可以帮助您快速找到相关代码。没有日志记录?花时间把它放进去。您可能没有找到bug,也可能没有。不过,日志记录可能会帮助您找到另一个bug,最终,一旦您阅读了足够多的代码,您就会发现它……比设置调试器和尝试重现问题、单步执行等更快。

    在调试过程中,我试图思考可能出现的问题。我提出了一个相当随意的分类系统,但它对我来说很有效:所有的bug都可以分为四类。请记住,这里我谈论的是运行时问题,而不是编译器或链接器错误。这四类是:

    • 动态内存分配
    • 堆栈溢出
    • 未初始化变量
    • 逻辑错误

    这些分类对C和C++来说是最有用的,但我希望它们在其他地方也能很好地应用。逻辑错误类别是一个很大的类别(例如,当正确的事情是a<=b时放置a<b),并且可以包括诸如未能在线程之间同步访问之类的事情。

    1. 我是否有演示问题的自动测试?
    2. 更改代码以使测试通过
    3. 确保所有其他测试仍然通过
    4. 登记找零

        9
  •  0
  •   Dmitris    17 年前

    我的调试方法不同,可能是因为我还是初学者。

    当我遇到逻辑错误时,我似乎会添加更多变量来查看哪些值会出现在哪里,然后我会在导致问题的代码段中逐行进行调试。

        10
  •  0
  •   Alex B    17 年前

    复制问题并生成可重复的测试数据集无疑是调试的第一步,也是最重要的一步。

    如果我能识别出一个可重复的bug,我通常会尝试隔离相关的组件,直到找到问题所在。通常,我会花一点时间排除这些情况,这样我就可以明确地说:问题不在组件X(或进程Y,等等)中。

        11
  •  0
  •   AndersK    17 年前

    首先,我尝试复制错误,如果无法复制错误,那么在一个非平凡的程序中,猜测问题基本上是不可能的。

    然后,如果可能,在单独的独立项目中分解代码。这有几个原因:如果原始项目很大,那么第二次调试非常困难,因为它消除或突出显示了关于代码的任何假设。

    我通常总是有另一个VS open的副本,我用它来调试小项目中的部分,并测试我后来添加到主项目中的例程。

    有时,分解一段代码并不容易,因此在这些情况下,我会根据问题的复杂程度使用不同的方法。在大多数情况下,关于数据的假设似乎对我不利,因此我尝试在代码中添加大量断言,以确保我的假设是正确的。我还使用#ifdef禁用代码,直到错误消失。消除对其他模块的依赖性等。。。有点像秃鹰一样在虫子里慢慢盘旋。。

    我想我没有一个真正有意识的方法来做这件事,它变化很大,但一般的原则是消除围绕这个问题的噪音,直到它很明显是什么。希望我没有听起来太困惑:)