The theory of debugging

在使用 console.log 又调试了一晚上之后,我绝望地写下了“The theory of debugging”这个装逼的标题。这现状太**了。一定得坐下来仔细思考一下,debugging 是怎么一个过程,如何能改进这个过程。

各位程序员可能以为需要你来写代码,而不是让用户来用 low code / no code 是因为你比用户更有逻辑思维。其实我觉得程序员的不可或缺在于独立定位问题的能力。普通用户处理不来各种各样神奇的出错消息。而程序员更多的时候是靠着对一个平台,一份代码的熟悉在工作。这种对某些 API 的行为的熟悉是非常容易过时的。比如你对 win32 api 很了解,并不能帮助你更好地写微信小程序。有什么 debugging 的经验是可以跨越时间,经久流传的? 当我们面对陌生代码的时候,如何能快速熟悉起来?

State

debugging 的起点是错误报告。错误报告来源于对可见 state 的观察。最常见的就是界面上某个字段和预期不相符,或者界面某个地方白屏了。

State Inspection 是第一步需要做的事情。除了界面上可见 state,还有一些 state 需要手工获取,包括:

  • 通过 chrome inspector 查看 DOM State
  • 通过 js api 查看界面背后的依赖订阅关系
  • 通过数据库工具查看数据记录
  • 通过 HTTP GET /internal/state 查看服务器内存中的状态

查看更多关联的 state 是为了两个目的:

  • 比对“预期”和“事实”,找到更多值得注意的地方
  • 获得更多的关联上下文,精确定位到订单号,手机号等具有更广泛关联意义的标识符

这一步经常遇到两种问题:

  • 即便就出现在你的开发机上,很多“可不见”state 都不是那么容易提取的。或者缺乏日常唾手可得的提取方式。至少不缓存在你大脑的 L2 缓存里,需要借助 L3 缓存(google)去获得。
  • 很多问题出现在客户的机器上,你并不在场,无法立即执行 state inspection。这就是 core dump 等机制的价值,能够把“犯罪现场”给留下来。

你能够想出你的系统有多少个模块是有状态的吗? 能够立马想出 inspect 每个状态最简单直接的方式吗? 你客户遇到了问题之后,你能够用什么手段拿到这些 state?

Trace

Trace 是指 state 的变化过程。log 是用来记录 Trace 的。如果我们要依赖 log,特别是临时添加 log 来获得某个 state,这说明 state inspection 的机制是严重不足的。

仅仅依靠 State,有的时候我们就可以立即凭直觉找到问题所在。但更多的时候会纳闷为啥 state 变成了这样,中间是哪个环节掉链子了。Trace 包括各种形式的:

  • 手写的文件日志
  • 临时加的 console.log
  • 分布式追踪,zipkin 等
  • 自动加的 function tracing
  • 系统 api 的追踪,strace 等

Trace 通过忠实记录 state 的变化过程,回答以下两类问题:

  • 我不清楚这个过程是咋实现的,通过 Trace 来反推实现
  • 我有一个大概的 idea 这个过程是咋样的,通过检察 trace 来发现异常的地方,从而找到 bug 根源

很多时候我们认为我们很清楚执行的每一步是怎样的。但是调试地时候才惊讶地发现执行过程充满了未知的细节。

这一步经常遇到:

  • Trace 压根就没有记录,代码也不是我写的,或者不知道在哪里。如果你是一个咨询师,只知道客户用的是 Java,你能够带上哪些 toolbox 去查问题?
  • 缺少大容量的存储。日志一刷就没了,无论是前端日志,还是控制台日志,经常一多就滚没了。
  • 缺少查阅和索引,即便是把所有日志都记录下来了。一条条去翻也太慢了。没法和代码直接映射起来,往往在对应的代码里,加上带条件的 console.log 是获取这个 spot 的 trace 的最佳办法。
  • 性能拉跨。各种日志手段都会拖慢执行。
  • 很多问题出现在客户的机器上,你并不在场。无论是前端还是后端,日常能开的日志还是太少了。当然不开日志主要原因还是性能拉跨。

你能够根据现有的 Trace 完整地把执行原理给讲一遍吗? 这个 story telling 的过程也是梳理日志的过程。

给不同的人,看不同程度的细节

即便 State / Trace 都有很完备的机制和手段,仍然不够。因为不同的人,需要的细节程度是不同的。这个是因为分工导致的。框架的维护者需要能够定位到根本原因,但是框架的使用者只需要知道是我业务代码没写对,还是框架出bug了。操作系统的维护者需要能够定位到硬件是不是出bug了,而大部分软件开发人员都默认假设硬件是bug free的。

需要区分细节程度还有一个原因显然是因为性能。像 strace 这样的东西没有办法日常开,但在现在的硬件能力下,rpc 日志已经可以全量记录了。无论是 State 还是 Trace,都要准备多份,一些是必须的,一些是按需的。

对不同的读者,提供的阅读工具也会有不同。越是日常使用的,越要方便易用。甚至可能需要 web 界面。

明确定义“知识边界”。不要假定所有人可以 debug 所有的 stack。总是有一些人需要负责底层,有一些人只要写最上层的业务的。这个“知识边界”怎么能清晰划分。如何能够快速定位到是底层模块的问题,还是业务系统的问题? 这个快速定位是否可以做成自动化无需人参与的?

插件化架构使得这个问题更加复杂。如果仅仅只是 client <-> server 的关系,无论是你用一个硬件,用一个操作系统,用一个框架,用一个后端服务,只有双方的关系。而插件化架构使得更多的“知识边界”的存在。插件之间未必有严格隔离的沙盒,很多非功能性指标都会互相影响。甚至很多功能性的 State 也会彼此修改。这使得“甩锅”变得困难,难以知道是不是一份不是我写的插件引入的问题。

所有以上问题都在于“边界”的划分。如何提供不同粒度的 State / Trace 给处于不同边界内的团队去看。如何提供针对性地 State / Trace 去快速找到该由谁来负责。如果没有好的工具,最终只能是由“人肉”来做 router,先由一个分拣团队去做第一线的排查。

缺的是什么?

很多问题我们都可以归纳为政治问题,而不是技术问题。debugging 也是如此。表象上来说,就是一个纯粹的缺少工具,缺少经验的技术问题。但是更深层次的,仍然是政治问题。对于 debugging 投入不足,找到 bug 之后也不会深度复盘,让 bug 定位更容易排不进日程表才是根本原因。

The theory of debugging

  • 第一步:比对假设的 state 和实际的 state
  • 第二步:比对假设的 trace 和实际的 trace
  • 第三步:确定“边界”,找到甩锅给谁
  • 第四步:如果是自己的问题,自己修了
  • 第五步:复盘 debugging 的过程,找到可改进的地方
  • 第六步:针对复盘结果,开发 debugging 工具,改进 debugging 体验
taowen
我还没有学会写个人说明!
上一篇

多维度架构之实现百万并发

下一篇

mybatis体系结构,终于最后一篇了

你也可能喜欢

评论已经被关闭。

插入图片