使用 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 的值很小,这也是一项繁重的工作。这就是扩展 bisect 工具派上用场的地方。它通过将扩展减半来禁用,因此在最坏和平均情况下表现都要好得多。
欢迎使用扩展 bisect
VS Code 中的扩展 bisect 工具灵感来源于 git bisect 命令。对于熟悉 Git 的人来说,这个命令有助于找出仓库中是哪次提交引入了问题。
让我们举个例子:我安装了 24 个扩展,其中第 8 个扩展是“不良”的。我们知道迭代方法需要 8 个步骤。那么 bisect 呢?
下面的视频展示了如何通过 Help: Start Extension Bisect 命令启动扩展 bisect,然后选择 Good now(现在正常)或 This is bad(有问题),直到找到“不良”扩展。一旦识别出,你还可以选择报告该扩展的问题。

以下是查找“不良”扩展的逐步过程
- Bisect 将 24 个扩展分成两半,每半 12 个,并禁用第二半的所有 12 个扩展。
- 在这个示例中,第 8 个扩展是“不良”的,所以它位于第一半,不会被禁用。问题依然存在,未达到预期效果。因为问题还在,扩展 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 个扩展,也只需多走 1 步。然而,最佳情况比迭代法要差,因为使用迭代方法时,你可能会走运在第一轮就找到“不良”扩展。
请记住,扩展 bisect 依赖于你提供正确的反馈。如果你总是回答 Good now(会将问题归咎于最后一个扩展)或 This is bad(将无法找到任何扩展),很容易误导它,也误导你自己。
另一个有用的见解是,扩展 bisect 在开始时会考虑当前所有已启用的扩展。这意味着你可以在开始 bisect 之前禁用已知的“正常”扩展,并在结束后重新启用它,从而将其排除在外。但是,只有当你确信该扩展不是“不良”扩展时才这样做。
最后,你可能会注意到 bisect 多走了一步(log2(N) + 1)。这是因为它在第一轮开始时禁用了所有扩展。这一步之所以存在,是因为你遇到的问题可能由 VS Code 本身引起,而不是由扩展引起,我们不希望让你无谓地陷入排查的泥潭。
就是这样。我们希望你永远不需要使用扩展 bisect。但是,如果你确实遇到了可能与扩展相关的问题,那么我们希望它能让故障排查变得更简单、更快速、更愉快。
编程愉快,
Johannes Rieken,VS Code 首席软件工程师 @johannesrieken