在 VSCode 中实现 Jupyter Debug Adapter

简介: 通过本文的介绍,我们可以了解 VSCode 的 Debug Adapter 的实现原理,并以 Jupyter 为例,成功在 VSCode 中实现 Jupyter Debug Adapter,并实现完全的调试能力。得益于 VSCode 灵活的调试注入能力,我们可以方便地为更多的语言,甚至是一些自定义框架实现调试能力,这可能可以为更多的小众语言或框架的开发者带来工作效率上的帮助。

说起 VSCode 中广受好评的功能,必须算上其优秀的调试(debug)功能,它拥有丰富的功能和直观的用户界面。


更为难得的是,VSCode 为这套调试架构实现的插件化机制,使得我们可以很方便地为不同的自定义语言和框架实现调试功能,并具有统一且通用的用户界面。

本文将以 Jupyter 接入 VSCode 调试的功能为例,介绍如何在 VSCode 中实现 Jupyter Debug Adapter。

在 VSCode 中注册 Debug Adapter Protocol

从 VSCode 的相关介绍中我们可以看到,VSCode 的 Debug Adapter 是一个独立的进程,它负责处理 IDE 和调试器之间的通信。而调试功能正是通过 Debug Adapter Protocol(DAP)来实现的,它是一个标准的调试协议,用于在 IDE 和调试器之间进行通信。


而为了给 VSCode 挂载自定义的调试器,我们可以通过 registerDebugAdapterDescriptorFactory 这个 API 来注册我们的 Debug Adapter。由于 Jupyter 是基于 Python 语言的,因此这里的 debugType 选择 python。


之后我们就启动 debugpy 后在 vscode 中设置好 launch.json 入口,调试普通 python 代码了。


如果你想了解更多关于 Debug Adapter 的内容,可以参考 VSCode 官方文档

在 DAP 中,要实现一套完成的 debugger 流程,要求我们需要实现一些基本的功能,如:

  1. initialize:初始化调试器。
  2. setBreakpoints:设置断点。
  3. variables/stackTrace/threads:获取相关变量、调用栈等信息。
  4. stepInto/stepOut/stepOver:单步调试。
  5. break statement:中断调试。


接下来我们就以 Jupyter 为例,看看如果要实现其他语言的 debug 接入,应该做哪些工作。

实现 Jupyter Debug Protocol

通过翻阅 Jupyter 文档,我们可以知道,若要实现调试功能,其连接的 kernel 里必须要支持 Jupyter Debug Protocol,这也是我们主要需要实现的地方。


我们可以通过以下的流程图来了解 debug 从发起到结束的全过程:

  1. Initialize 阶段。IDE 会向 kernel 发送 initialize_request,这个 request 将会帮我们创建一个新的 channel 用来交换调试信息。
  2. Attach 阶段。该请求将会帮我们将建立好的 debug adaptor channel 与当前的 IDE 进行绑定,将在这里负责 debug 交互的全过程。
  3. Configuring breakpoints and exception behavior 阶段。这一部分是将 IDE 中设置的断点等相关信息传递给 kernel,以便 kernel 能够在适当的时候中断。
    其中,setBreakpoints 等将会传递设置断点的行/函数/异常处理等信息,dumpCell 是将单元格内的内容或状态信息传递给 kernel。
    值得注意的是,这里所有的请求 msg_type 将以 debug_event 或者 status 发出而不再是 request
    随着 configureationDone 的出现,标志着客户端配置过程的结束。
  4. Execute request 阶段。这一步与正常的代码执行完全一致,发出代码执行请求,最终收获到执行结果。
    但此时,如果我们设置了 breakpoint,该过程将在执行到断点处时被一个内容为 stoppeddebug_event 给暂时 block。
  5. Pausing and extract context 阶段。
    当 kernel 收到 stoppeddebug_event 后,将会暂停当前的执行,此时会发送一系列channel typecontrolcommand,将当前的各种相关 context 传递给 IDE。
    其中 variables 是获取当前 code 中的变量信息,包括其名称类型等。stackTrace 帮助 IDE 获取当前的调用栈信息。scopes 则是获取函数或变量的作用域信息。threads 则是获取当前处理 debug 功能所处的线程信息。
  6. Dispose 阶段。当所有断点都被跳过后,之前的 execute_request 将被执行完成返回 execute_reply。至此 debug 流程结束。此时我们需要让 IDE 发送 disconnect request 来关闭当前的 debug adaptor channel。如果我们想正确执行完整的 debug 生命周期。无论是否异常结束,都需要执行 disconnect request


了解了 Jupyter Debug Protocol 的全流程后,我们就可以开始着手为 VSCode 的交互实现做准备了。

利用 Jupyterlab 库为沟通 kernel 提供 API 支持

上面我们简要介绍了一下 Jupyter 调试过程的原理,而事实上我们并不需要完全从零开始实现 Jupyter Debug Protocol,因为开源社区里已经有了很多现成的库可以帮助我们实现这一功能。


Jupyterlab 是一个为 Jupyter 打造的第一方开发环境工具库,它提供了丰富的 API 支持,可以帮助我们更方便地与 kernel 进行交互。

在 Jupyterlab 中,我们可以通过 jupyterlab/debugger 这个插件来实现对 Jupyter Debug Protocol 的支持。它提供了一套完整的调试功能,包括设置断点、单步调试、查看变量等。


因此,我们真正需要实现的触发事件与交互逻辑也就变得更为清晰了。只需要处理好 debugging 的这几个实现即可:

  1. debugging 的开始与终止事件
  2. 断点的设置与清除
  3. 代码执行
  4. 变量、调用栈等的查看
  5. 单步调试(包括stepInto/stepOut/stepOver


因此,我们可以得出基于 jupyterlab 的调试器实现的基本流程:

实现 Debugging Manager 完成对调试器的管理

到了最终代码实现的阶段了。我们需要实现一个 Debugging Manager,用于通过registerDebugAdapterDescriptorFactory 注册给 VSCode,管理调试器的启动、停止、断点设置等操作。


具体的工程实现方案可以有很多,具体就不展开了,这里只 po 一下 vscode-jupyter 的实现方案:

vscode-jupyter 通过额外增加了 KernelDebugAdapter 类实现了 debug_event 消息的收发,通过 DebugCellController 类来管理单元格的 debug 执行信息,实现了不同消息走不同 Controller 的分离。

总结

通过本文的介绍,我们可以了解 VSCode 的 Debug Adapter 的实现原理,并以 Jupyter 为例,成功在 VSCode 中实现 Jupyter Debug Adapter,并实现完全的调试能力。

得益于 VSCode 灵活的调试注入能力,我们可以方便地为更多的语言,甚至是一些自定义框架实现调试能力,这可能可以为更多的小众语言或框架的开发者带来工作效率上的帮助。

相关文章
|
Web App开发 前端开发 JavaScript
VSCode如何设置Vue前端的debug调试
VSCode如何设置Vue前端的debug调试
870 0
|
4月前
|
机器学习/深度学习 IDE 数据挖掘
使用VScode的几点感受,对比Pycharm、Jupyter优劣势
使用VScode的几点感受,对比Pycharm、Jupyter优劣势
|
4月前
|
JavaScript 前端开发 数据可视化
Jupyter Notebook如何调试?JupyterLab作为DeBug调试工具及调试教程
文章提供了JupyterLab debugger工具的安装和使用教程,包括如何在没有conda环境或已有conda环境下安装所需的软件包,如jupyterlab、Node.js、ptvsd和jupyterlab的debugger插件,以及如何使用debugger进行程序调试。同时,文章还列出了一些常见的安装问题及其解决办法。
1005 1
|
6月前
|
Python
解决VSCode中Debug和运行路径不一致的
在VSCode调试时,如果程序运行路径不正确,可通过配置`launch.json`文件的`CWD`参数来解决。在`launch.json`中添加或修改`configurations`,例如设置`"cwd": "${fileDirname}"`,确保调试和运行时路径一致。这样可以避免因路径问题影响调试。记得`"name"`、`"type"`、`"request"`等其他关键参数也要正确配置。我是木头左,希望对你有所帮助!
解决VSCode中Debug和运行路径不一致的
|
7月前
|
C++
VS code debug c代码 配置
VS code debug c代码 配置
70 0
|
7月前
|
Linux C++
vscode中配置cmake及debug使用
vscode中配置cmake及debug使用
933 0
|
JSON 数据格式 C++
VS Code debug调试时无法查看变量内容【已解决】
VS Code debug调试时无法查看变量内容【已解决】
VS Code debug调试时无法查看变量内容【已解决】
|
JSON 编译器 Go
VS CODE一些常见配置操作(快捷键设置、C/C++的debug、代码路径配置)
VS CODE一些常见配置操作(快捷键设置、C/C++的debug、代码路径配置)
738 1
VS CODE一些常见配置操作(快捷键设置、C/C++的debug、代码路径配置)
|
JavaScript 前端开发 程序员
推荐三个神级VSCode插件[jupyter中写javascript,替代typora的markdown编辑器,记录敲代码时间的插件]
使用过Python的朋友应该都知道Jupyter-Notebook,因为它对新手朋友学习Python可谓是绝佳的工具,对Python老手来说使用它来写一些小Demo或者绘图分析都是非常不错的工具,我们都知道,最近,我开始了深入学习JavaScript的路途,偶尔为敲一下js来验证书中的说法以及自己的想法,同时可能会记一下笔记,然后可能还需要刷Leetcode
777 0
|
Web App开发 JavaScript 前端开发
VS Code 折腾记 - (17) Debug for Chrome(附加模式[支持浏览器插件的使用])
很早以前就撸过一篇帖子,也是说这个的,但是那时候只是用最傻瓜化的直白的姿势. 新窗口模式,而且配置文件用的是临时文件,拓展这些更是不能用. 用起来感觉不是很好,但时隔一年多,不管是vscode还是当初的插件都完善了很多. 研究了下附加模式,并让拓展也能正常使用的姿势(挺简单的). 用到的插件VSCode插件:
492 0