写点什么

为什么我不怎么用调试器

作者:Justin
  • 2021 年 12 月 16 日
  • 本文字数:1613 字

    阅读完需:约 5 分钟

为什么我不怎么用调试器

直到上大学之前,我都没接触过计算机,没写过程序。我学编程语言的顺序是 FORTRAN、C、C++、Delphi、Python、C#、Java……中间还穿插过一些汇编、JavaScript、Lua 之类的。经常使用调试器是从使用 Borland 的终端界面 IDE Turbo C++ (TC++)开始的,坦率地说,TC++ 的调试器对我理解 C++ 程序是如何运行的非常有意义。通过调试器,可以让程序停在你希望的地方,一行一行地执行语句,看到每一个变量的变化,理解指针、虚函数都是怎么回事,甚至可以在执行过程中修改变量,观察引起的变化。在用 Visual Studio 的那些年,熟练使用调试器修 bug 是自己很得意的技能,条件断点用得很溜。


Turbo C++


但是随着写的程序越来越多,我发现自己不怎么需要调试器单步执行程序,用得更多的是日志、单元测试、静态分析工具。偶尔用到调试器的场景,往往是帮着别人调试自己不熟悉的代码,或者逆向工程。不经意间,调试器离自己越来越远。搜了一下,我并不是异类,不赞成依赖调试器的“老程序员”大有人在。比如 Linux 之父 Linus Torvalds、Go 语言之父 Rob Pike、Python 之父 Guido van Rossum。


按照 ThoughtWorks 中国区 CTO 徐昊的说法,调试属于 Cynefin Framework 认知框架里 Complex 认知行为,也就是所谓未知的未知。


当一个人开始 Debug 的时候,说明他不知道这个代码发生了什么,要打一个断点先看一看,然后再做。作为程序员,或者管理程序员,要记住擅长 Debug 的程序员都不是好程序员。我已经有十年没有用过 Debug 了,因为它是一种低效的认知模式。[left]


我没有 TW 那么绝对。用不用调试器,确实是个人习惯和喜好,并没有优劣之分。只要对你来说,是有效的,就是好方法。


我想讨论的是,为什么我自己不怎么用调试器了呢?回忆起来,是从写得程序规模越来越大,越来越讲究结构设计开始的。与其在不确定怎么运行的一堆代码里,通过单步执行、查看变量,去试着理顺;还不如去思考一下怎么样改善设计,让程序的运行更容易理解,错误更容易被发现。另外,有段时间写图像处理算法,针对一个特例单步调试,其实也看不出来什么,更需要的是设计测试方法,通过大量的样本数据集来发现问题。


Rob Pike 说过:


如果你深入了一个 bug,你会倾向于修复代码里的局部问题;如果你首先考虑 bug 是怎么来的,你通常会找到并修复代码里的高层次问题,从而改善设计并预防此类 bug 在未来再次发生。[left]


他说的“深入” bug,指的就是用调试器去深入调试一个 bug。


我们的确需要好工具,但是调试器能做的只是把程序某次执行的过程,以慢动作的形式展现给开发者。这种方式只适合一些复杂度不高、输入输出明确的小项目。一旦项目规模更大、复杂度更高,至少在下面这几个方面,调试器是难以发挥作用的:


  • bug 出现具有随机性,需要从大量的输入输出数据中发现规律;

  • 在分布式系统中,有些 bug 是跨进程甚至是跨计算节点的,难以通过对单一进程的研究理解系统行为;

  • bug 出现需要满足特定条件,比如网络负载、内存压力、高并发,而这些是很难在调试中复现的。


和单步执行的调试器相比,日志可以让我们看到程序的实时状态,还可以记录下来反复翻看,甚至写分析脚本来理解程序行为。与人为地让程序以慢动作执行相比,日志动态地看待程序执行的过程,是一个在时间尺度上的事件流;而不是执行一步,停下来,静态地去检查各个变量。设计更好的日志记录方式,本身也是理解程序中什么状态最重要的过程。


除了日志之外,单元测试也有效的调试方法,而不仅仅是我们对程序各种功能的验证手段。遇到 bug,就可以通过编写针对目标越来越具体的单元测试,把错误的范围尽可能缩小。一开始可能是针对整个程序的,继而是针对相关模块的,相关类的,相关方法的……直到一个足够小的范围。定位清楚之后,就可以用日志来观察系统行为,发现 bug 发生的具体位置了。不仅可以帮助发现问题,这些单元测试还能确保未来 bug 不会再次发生。


如果我们面对的问题是真正复杂的问题,就无法用简单的工具去应对,只有深入思考才是出路。


阅读公众号原文


无限游戏之路


发布于: 17 小时前阅读数: 10
用户头像

Justin

关注

公众号:无限游戏之路 2018.04.14 加入

TGO 北京会员,乐城堡联合创始人

评论

发布
暂无评论
为什么我不怎么用调试器