使用 bisect 解决扩展问题
2021 年 2 月 16 日,作者:Johannes Rieken,@johannesrieken
“就像 git-bisect 一样,但用于 VS Code 扩展。”
Visual Studio Code 的真正强大之处在于其扩展:主题扩展添加颜色和图标,语言扩展实现智能代码补全(IntelliSense)和导航,调试器扩展使您能够运行代码并轻松查找 bug。有些扩展可以播放音乐,有些可以显示股票行情,还有些可以实现跨地点和时区的协作工作。VS Code 的市场托管着超过 28,000 个扩展,用户安装 50 个或更多扩展并不少见。有如此多的扩展,bug 是不可避免的。与其否认,不如我们让故障排除变得容易。
“坏”扩展
我们喜欢扩展,并不真的认为有什么“坏”扩展。然而,就像所有软件一样,扩展也会有 bug 和功能差距。所以,为了便于阅读和制造一点戏剧性,我们来使用“坏扩展”这个词,它指的是一个可能导致崩溃或只是显示不期望行为的扩展。幸运的是,我们在设计 VS Code 时就考虑到了“坏”扩展,因此在单独的进程中运行它们。这种隔离保证了 VS Code 保持运行,光标始终闪烁,并且您可以随时保存您的工作。
为了好玩,并使其更容易演示扩展 bisect,我们创建并发布了Extension Bisect Demo扩展。安装后,每当您到达“bisect”一词时,它都会烦人地重置您的光标。您可以使用此扩展在此博文中进行跟随。
费力地查找“坏”扩展
今天,查找“坏”扩展可能很容易,也可能很难。打开扩展视图(⇧⌘X (Windows, Linux Ctrl+Shift+X)),禁用一个扩展,重新加载窗口(**Developer: Reload Window**),然后检查问题是否仍然存在。如果问题消失了,那么该扩展就是“坏”的,您就完成了。否则,重新启用该扩展,然后用下一个扩展重复此过程。

如果您运气好,第一个扩展就是“坏”的;如果您运气不好,那就是最后一个扩展。用计算机科学的语言来说,这意味着对于 N 个扩展,您最坏需要重复该过程 O(N)(N 阶),平均需要 O(N/2)。因为这个算法是由人类(您)操作的,即使 N 的值很小,也是费力的。这时 **extension bisect** 工具就派上用场了。它在最坏和平均情况下都更好,因为它通过对半分的方式禁用扩展。
欢迎使用 extension bisect
VS Code 中的 Extension bisect 工具受到git bisect命令的启发。对于熟悉 Git 的人来说,这个命令有助于找出仓库中引入问题的提交。
让我们用一个例子:我安装了 24 个扩展,其中第 8 个扩展是“坏”的。我们知道迭代方法需要 8 步。bisect 呢?
下面的视频展示了如何通过 **Help: Start Extension Bisect** 命令启动 extension bisect,然后选择 **Good now** 或 **This is bad**,直到识别出“坏”的扩展。一旦识别出来,您就可以选择为该扩展报告一个问题。

以下是找到“坏”扩展的逐步过程
- Bisect 将 24 个扩展分成两半,每半 12 个,然后禁用第二半的所有 12 个扩展。
- 在这个例子中,第 8 个扩展是“坏”的,所以它在第一半,没有被禁用。情况仍然没有像我们预期的那样工作。因为仍然存在问题,extension bisect 重复此过程,将前 12 个扩展分成两部分:6 个启用,6 个禁用。所有其他扩展也被重新启用。
- 第 8 个扩展现在被禁用。现在一切正常。这意味着 bisect 可以继续处理第二半(扩展 6-11),并将其分成 3 个启用和 3 个禁用的扩展。
- 现在,第 8 个扩展被重新启用,问题又出现了。这意味着 bisect 继续处理第一半。它将它们分成 1 个启用和 2 个禁用的扩展。
- 第 8 个扩展现在被禁用,一切又正常了,bisect 继续处理第二半,将其分成 1 个启用和 1 个禁用的扩展。
- 第 8 个扩展是唯一被禁用的扩展,问题消失了。这意味着我们找到了“坏”的扩展,并且完成了。
更快地排查问题
我们看到,在每一步中,bisect 都将搜索空间减半。现在它在对数时间内运行,导致平均和最坏情况下的性能为 O(log N)。这相当不错,因为它具有良好的可扩展性。对于 24 个扩展,您需要 4 到 5 步来找到一个“坏”的扩展;对于 38 个扩展,只需要多一步。然而,最佳情况更糟,因为使用迭代方法,您可能会运气好,在第一轮就找到“坏”的扩展。
请记住,extension bisect 依赖于您提供正确的反馈。您很容易欺骗它,也欺骗自己,方法是始终回答 **Good now**(将问题归咎于最后一个扩展)或 **This is bad**(这样将找不到扩展)。
另一个有用的见解是,extension bisect 开始时会考虑所有已启用的扩展。这意味着您可以通过在开始之前禁用它并在之后重新启用它,来排除一个已知的“好”扩展。但是,只有当您确信该扩展不是“坏”的扩展时才这样做。
最后,您可能会注意到 bisect 需要额外的步骤(log2(N) + 1)。这是因为它在第一轮开始时禁用了所有扩展。这一步之所以这样做,是因为您遇到的问题可能是由 VS Code 本身引起的,而不是由扩展引起的,我们不希望您不必要地陷入困境。
就是这样。我们希望您永远不需要使用 extension bisect。但是,如果您遇到可能与扩展相关的问题,那么我们希望能够使故障排除变得更容易、更快捷、更愉快。
编程愉快,
Johannes Rieken,VS Code 首席软件工程师 @johannesrieken