现在可用!阅读8月份的新功能和修复。

调试器扩展

VisualStudioCode的调试体系结构允许扩展作者轻松地将现有调试器集成到VSCode中,同时具有与所有调试器相同的用户界面。

VS代码附带一个内置调试器扩展节点.js调试器扩展,这是VS Code支持的许多调试器功能的绝佳展示:

VS代码调试功能

此屏幕截图显示以下调试功能:

  1. 调试配置管理。
  2. 启动/停止和步进的调试操作。
  3. 源、函数、条件、内联断点和日志点。
  4. 堆栈跟踪,包括多线程和多进程支持。
  5. 在视图和悬停中浏览复杂的数据结构。
  6. 源中以悬停或内联形式显示的变量值。
  7. 管理手表表情。
  8. 用于自动完成交互式评估的调试控制台。

本文档将帮助您创建调试器扩展,使任何调试器都可以与VS代码一起工作。

VS代码的调试体系结构

VS代码基于我们引入的与调试器后端通信的抽象协议,实现了一个通用(与语言无关)调试器UI。因为调试器通常不实现该协议,所以需要一些中介来“调整”调试器以适应该协议。此中介通常是与调试器通信的独立进程。

VS代码调试体系结构

我们称此中介为调试适配器(或陆军部缩写),DA和VS代码之间使用的抽象协议是调试适配器协议(目的地交货简称)。由于调试适配器协议独立于VS代码,因此它有自己的网站在那里可以找到简介和概述,详细信息规范,以及一些带有的列表已知的实现和支持工具.DAP的历史和背后的动机解释如下博客帖子.

由于调试适配器独立于VS代码,因此可以在其他开发工具它们与VS Code基于扩展和贡献点的可扩展性体系结构不匹配。

因此,VS Code提供了一个贡献点,调试器,其中可以在特定调试类型下提供调试适配器(例如。节点用于Node.js调试器)。每当用户启动该类型的调试会话时,VS Code就会启动注册的DA。

因此,在其最简单的形式中,调试器扩展只是调试适配器实现的声明性贡献,并且该扩展基本上是调试适配器的打包容器,无需任何附加代码。

VS代码调试架构2

更实际的调试器扩展为VS代码提供了以下许多或所有声明性项:

  • 调试器支持的语言列表。VS代码使UI能够为这些语言设置断点。
  • 调试器引入的调试配置属性的JSON模式。VS Code使用此模式验证launch.json编辑器中的配置,并提供IntelliSense。请注意,JSON模式构造$参考定义不支持。
  • VS Code创建的初始launch.json的默认调试配置。
  • 调试用户可以添加到launch.json文件中的配置片段。
  • 可以在调试配置中使用的变量的声明。

您可以在中找到更多信息贡献.断点贡献者.调试器参考文献。

除了上述纯粹的声明性贡献外,调试扩展API还支持基于代码的功能:

  • 为VS Code创建的初始launch.json动态生成默认调试配置。
  • 确定要动态使用的调试适配器。
  • 在将调试配置传递给调试适配器之前,请验证或修改这些配置。
  • 与调试适配器通信。
  • 向调试控制台发送消息。

在本文的其余部分中,我们将介绍如何开发调试器扩展。

模拟调试扩展

由于从头开始创建调试适配器对于本教程来说有点繁重,所以我们将从一个简单的DA开始,它是我们创建的一个教育性的“调试适配器初学者工具包”。它被称为模拟调试因为它不与真正的调试器对话,而是模拟调试器。模拟调试模拟调试器并支持单步、继续、断点、异常和变量访问,但它没有连接到任何真正的调试器。

在深入研究mock-debug的开发设置之前,让我们首先安装一个预构建版本从VS Code Marketplace下载并使用它:

  • 切换到Extensions viewlet并键入“mock”以搜索mock Debug扩展,
  • “安装”和“重新加载”扩展。

要尝试模拟调试:

  • 创建新的空文件夹模拟测试并在VS Code中打开它。
  • 创建文件自述.md并输入几行任意文本。
  • 切换到“运行和调试”视图(⇧⌘D类(Windows、LinuxCtrl+Shift+D组合键))并选择创建launch.json文件链接。
  • VSCode将允许您选择“调试器”以创建默认启动配置。选择“模拟调试”。
  • 按下绿色按钮起点按钮,然后输入确认建议的文件自述.md.

调试会话启动后,您可以“单步执行”自述文件,设置并命中断点,并遇到异常(如果例外出现在一行中)。

模拟调试器正在运行

在使用模拟调试作为您自己开发的起点之前,我们建议先卸载预构建版本:

  • 切换到Extensions视图,单击Mock Debug扩展的gear图标。
  • 运行“卸载”操作,然后“重新加载”窗口。

模拟调试的开发设置

现在,让我们获取Mock Debug的源代码,并在VS Code中开始对其进行开发:

git克隆https://github.com/microsoft/vscode-mock-debug.git
光盘vscode模块
纱线

打开项目文件夹vscode模块在VS代码中。

包裹里有什么?

  • package.json包是模拟调试扩展的清单:
    • 它列出了mock-debug扩展的贡献。
    • 这个编译脚本用于将TypeScript源转换为外面的文件夹并监视后续的源代码修改。
    • 依赖关系vscode-debug协议,vscode去糖适配器、和vscode-debugadapter测试支持是NPM模块,可以简化基于节点的调试适配器的开发。
  • src/mockRuntime.ts是一个嘲弄使用简单的调试API运行时。
  • 代码适应调试适配器协议的运行时位于src/mockDebug.ts。这里是DAP各种请求的处理程序。
  • 由于调试器扩展的实现存在于调试适配器中,因此根本不需要扩展代码(即在扩展主机进程中运行的代码)。然而,模拟调试有一个小src/extension.ts(源代码/扩展名.ts)因为它说明了在调试器扩展的扩展代码中可以做什么。

现在,通过选择扩展发射配置和打击五楼.最初,这将把TypeScript源完全移植到外面的文件夹。完整构建后观察者任务开始显示您所做的任何更改。

在发布源代码之后,出现了一个新的VS Code窗口,标签为“[Extension Development Host]”,其中Mock Debug扩展现在以调试模式运行。从那扇窗户打开你的模拟测试项目自述.md文件,用“F5”启动调试会话,然后单步执行:

调试扩展和服务器

由于您在调试模式下运行扩展,现在可以在中设置并命中断点src/extension.ts(源代码/扩展名.ts)但正如我前面提到的,在扩展中没有多少有趣的代码执行。有趣的代码在调试适配器中运行,这是一个单独的进程。

为了调试调试适配器本身,我们必须在调试模式下运行它。这最容易通过在中运行调试适配器来实现服务器模式并配置VS Code以连接到它。在VS Code-mock-debug项目中,选择启动配置服务器从下拉菜单中选择,然后按下绿色启动按钮。

因为我们已经有了一个用于扩展的活动调试会话,所以VS代码调试器UI现在进入多会话通过查看两个调试会话的名称指示的模式扩展服务器显示在CALL STACK视图中:

调试扩展和服务器

现在我们可以同时调试扩展和DA。到达这里的更快方法是使用扩展+服务器启动配置,自动启动两个会话。

可以找到调试扩展和DA的另一种甚至更简单的方法在下面.

在方法的开头设置断点启动请求(…)在文件中src/mockDebug.ts最后一步,通过添加一个调试服务器端口的属性4711到模拟测试启动配置:

{
  “版本”:"0.2.0",
  “配置”: [
{
      “类型”:“模拟”,
      “请求”:“启动”,
      “名称”:“模拟测试”,
      “程序”:“${workspaceFolder}/readme.md”,
      “stopOnEntry”(停止进入):真的,
      “调试服务器”:4711
}
]
}

如果现在启动此调试配置,VS Code不会将模拟调试适配器作为单独的进程启动,而是直接连接到已经运行的服务器的本地端口4711,您应该在启动请求.

有了这个设置,您现在可以轻松地编辑、传输和调试Mock debug。

但现在真正的工作开始了:您必须替换中调试适配器的模拟实现src/mockDebug.tssrc/mockRuntime.ts通过一些与“真实”调试器或运行时对话的代码。这涉及到理解和实现调试适配器协议。更多详细信息关于这个可以找到在这里.

调试器扩展的package.json剖析

除了提供调试适配器的特定于调试器的实现之外,调试器扩展还需要package.json包这有助于实现各种与调试相关的贡献点。

让我们仔细看看package.json包模拟调试。

与每个VS Code扩展一样package.json包声明基本属性名称,出版商、和版本扩展名的。使用类别字段使扩展更容易在VS代码扩展市场中找到。

{
  “名称”:“模拟调试”,
  “显示名称”:“模拟调试”,
  “版本”:"0.24.0",
  “出版商”:"...",
  “描述”:“用于开发VS代码调试适配器的启动程序扩展。”,
  “作者”: {
    “名称”:"...",
    “电子邮件”:"..."
},
  “发动机”: {
    “vscode”:"^1.17.0",
    “节点”:"^7.9.0"
},
  “图标”:“images/mock-debug-icon.png”,
  “类别”: [“调试器”],

  “贡献”: {
    “断点”: [{“语言”:“降价”}],
    “调试器”: [
{
        “类型”:“模拟”,
        “标签”:“模拟调试”,

        “程序”:“./out/mockDebug.js”,
        “运行时”:“节点”,

        “配置属性”: {
          “启动”: {
            “必需”: [“程序”],
            “属性”: {
              “程序”: {
                “类型”:“字符串”,
                “描述”:“文本文件的绝对路径。”,
                “默认”:“${workspaceFolder}/${command:AskForProgramName}”
},
              “stopOnEntry”(停止进入): {
                “类型”:“布尔值”,
                “描述”:“启动后自动停止。”,
                “默认”:真的
}
}
}
},

        “initialConfigurations”(初始配置): [
{
            “类型”:“模拟”,
            “请求”:“启动”,
            “名称”:“询问文件名”,
            “程序”:“${workspaceFolder}/${command:AskForProgramName}”,
            “stopOnEntry”(停止进入):真的
}
],

        “配置代码段”: [
{
            “标签”:“模拟调试:启动”,
            “描述”:“用于启动模拟调试程序的新配置”,
            “主体”: {
              “类型”:“模拟”,
              “请求”:“启动”,
              “名称”:“${2:启动程序}”,
              “程序”:"^\"\\${workspaceFolder}/${1:程序}\""
}
}
],

        “变量”: {
          “请求程序名”:“扩展名.mock-debug.getProgramName”
}
}
]
},

  “激活事件”: [“onDebug”(正在调试),“onCommand:extension.mock-debug.getProgramName”]
}

现在来看一下贡献部分,其中包含特定于调试扩展的贡献。

首先,我们使用断点贡献点,列出将为其启用设置断点的语言。没有这一点,就不可能在降价文件中设置断点。

接下来是调试器第节。这里,在调试下引入了一个调试器类型 嘲弄。用户可以在启动配置中引用此类型。可选属性标签可以用于在UI中显示调试类型时为其提供一个好的名称。

由于调试扩展使用调试适配器,因此其代码的相对路径被指定为程序属性。为了使扩展独立,应用程序必须位于扩展文件夹中。按照惯例,我们将此应用程序保存在名为外面的箱子,但您可以自由使用其他名称。

由于VS代码运行在不同的平台上,我们必须确保DA程序也支持不同的平台。为此,我们有以下选项:

  1. 如果程序以独立于平台的方式实现,例如作为在所有支持的平台上可用的运行时上运行的程序,则可以通过运行时属性。截至目前,VS Code支持节点单声道运行时。上面的模拟调试适配器使用了这种方法。

  2. 如果DA实现需要不同平台上的不同可执行文件程序属性可以用于以下特定平台:

    “调试器”: [{
        “类型”:“gdb”,
        “窗口”: {
            “程序”:“./bin/gdbDebug.exe”,
    },
        “osx”: {
            “程序”:“./bin/gdbDebug.sh”,
    },
        “linux”: {
            “程序”:“./bin/gdbDebug.sh”,
    }
    }]
    
  3. 两种方法的组合也是可能的。以下示例来自Mono DA,它是作为一个需要在macOS和Linux上运行但不需要在Windows上运行的Mono应用程序实现的:

    “调试器”: [{
        “类型”:“单声道”,
        “程序”:“./bin/monoDebug.exe”,
        “osx”: {
            “运行时”:“单声道”
    },
        “linux”: {
            “运行时”:“单声道”
    }
    }]
    

配置属性声明的架构启动.json可用于此调试器的属性。此架构用于验证启动.json以及在编辑启动配置时支持IntelliSense和悬停帮助。

这个初始配置定义默认值的初始内容启动.json用于此调试器。当项目没有启动.json用户启动调试会话或选择创建launch.json文件“运行和调试”视图中的链接。在这种情况下,VS Code允许用户选择一个调试环境,然后创建相应的启动.json:

调试器快速选取

而不是定义启动.json静态地在package.json包,可以通过实现调试配置提供程序(有关详细信息,请参阅章节使用下面的DebugConfigurationProvider).

配置代码段定义在编辑时在IntelliSense中显示的启动配置片段启动.json。按照惯例,在标签属性,以便在显示在多个代码段建议列表中时可以清楚地识别代码段。

这个变量贡献将“变量”绑定到“命令”。这些变量可以在启动配置中使用${命令:xyz}启动调试会话时,语法和变量由绑定命令返回的值替换。

命令的实现存在于扩展中,可以是没有UI的简单表达式,也可以是基于扩展API中可用UI特性的复杂功能。模拟调试绑定变量请求程序名称到命令扩展名.mock-debug.getProgramName. The实施此命令的src/extension.ts(源代码/扩展名.ts)使用显示输入框要让用户输入程序名:

vscode码.命令.register命令('扩展名.mock-debug.getProgramName',配置 =>{
  返回 vscode码.窗口.显示输入框({
    占位符: '请输入工作区文件夹中降价文件的名称',
    值: '自述.md'
});
});

该变量现在可以在启动配置的任何字符串类型的值中用作${命令:AskForProgramName}.

使用DebugConfigurationProvider

如果package.json包不够,a调试配置提供程序可用于动态控制调试扩展的以下方面:

  • 新创建的launch.json的初始调试配置可以动态生成,例如基于工作区中可用的一些上下文信息。
  • 启动配置可以是断然的(或修改),然后使用它启动新的调试会话。这允许根据工作区中可用的信息填写默认值。两个决定方法存在:resolveDebugConfiguration解决债务配置在启动配置中替换变量之前调用,带替代变量的resolveDebugConfiguration替换所有变量后调用。如果验证逻辑在调试配置中插入额外的变量,则必须使用前者。如果验证逻辑需要访问所有调试配置属性的最终值,则必须使用后者。

这个模拟配置提供程序在里面src/extension.ts(源代码/扩展名.ts)实施resolveDebugConfiguration解决债务配置检测当不存在launch.json,但在活动编辑器中打开了Markdown文件时启动调试会话的情况。这是一个典型的场景,用户在编辑器中打开了一个文件,只想调试它而不创建launch.json。

通过为特定调试类型注册调试配置提供程序vscode.debug.register调试配置提供程序,通常在扩展的激活功能。确保调试配置提供程序如果注册得足够早,则必须在使用调试功能后立即激活扩展。通过为调试时中的事件package.json包:

“激活事件”: [
    “onDebug”(正在调试),
    // ...
],

这一套调试时使用任何调试功能后立即触发。只要扩展具有低廉的启动成本(即在其启动顺序中不花费太多时间),这就可以很好地工作。如果调试扩展的启动代价很高(例如因为启动语言服务器)调试时激活事件可能会对其他调试扩展产生负面影响,因为它会很早触发,并且不会考虑特定的调试类型。

对于昂贵的调试扩展,更好的方法是使用更细粒度的激活事件:

  • 调试初始配置就在提供调试配置方法调试配置提供程序被调用。
  • onDebugResolve:类型就在resolveDebugConfiguration解决债务配置带替代变量的resolveDebugConfiguration方法调试配置提供程序调用指定类型的。

经验法则:如果激活调试扩展很便宜,请使用调试时如果价格昂贵,请使用onDebugInitialConfigurations(调试初始配置)和/或onDebugResolve(调试解决)取决于调试配置提供程序实现相应的方法提供调试配置和/或resolveDebugConfiguration解决债务配置.

发布调试器扩展

创建调试器扩展后,可以将其发布到市场:

  • 更新中的属性package.json包以反映调试器扩展的命名和用途。
  • 按照中的说明上传到Marketplace发布扩展.

开发调试器扩展的替代方法

如我们所见,开发调试器扩展通常需要在两个并行会话中调试扩展和调试适配器。如上所述,VS代码很好地支持这一点,但如果扩展和调试适配器都是一个可以在一个调试会话中调试的程序,那么开发可能会更容易。

事实上,只要调试适配器是用TypeScript/JavaScript实现的,这种方法就很容易实现。基本思想是直接在扩展内运行调试适配器,并使VS代码连接到它,而不是在每个会话中启动新的外部调试适配器。

对于此VS代码,提供了扩展API来控制如何创建和运行调试适配器。A类调试适配器描述符工厂有一个方法创建DebugAdapterDescriptor当调试会话启动并且需要调试适配器时,由VS代码调用。此方法必须返回描述符对象(调试适配器描述符)它描述了调试适配器的运行方式。

如今,VS Code支持三种不同的方式来运行调试适配器,因此提供了三种不同类型的描述符:

  • 调试适配器可执行文件:此对象将调试适配器描述为具有路径、可选参数和运行时的外部可执行文件。可执行文件必须实现调试适配器协议并通过stdin/stdout进行通信。这是VSCode的默认操作模式,如果没有,VSCode会自动使用该描述符和package.json中的相应值调试适配器描述符工厂已显式注册。
  • 调试适配器服务器:此对象描述作为服务器运行的调试适配器,该服务器通过特定的本地或远程端口进行通信。基于vscode调试适配器npm模块自动支持这种服务器模式。
  • 调试适配器内联实现:此对象将调试适配器描述为实现vscode。调试适配器接口。基于版本1.38-pre.4或更高版本的调试适配器实现vscode去糖适配器npm模块自动实现接口。

模拟调试显示了三种类型的DebugAdapterDescriptorFactories以及他们是如何为“模拟”调试类型注册。可通过以下方式选择要使用的运行模式设置全局变量运行模式到一个可能的值外部的,服务器,或内联.

为了发展内联服务器模式特别有用,因为它们允许在单个进程中调试扩展和调试适配器。