诊断端口

本文适用于:✔️ .NET Core 3.1 及更高版本

.NET 运行时公开一个服务终结点,该终结点允许其他进程通过 IPC 通道发送诊断命令和接收响应。 此终结点称为诊断端口。 可以将命令发送到诊断端口:

  • 捕获内存转储。
  • 启动 EventPipe 跟踪。
  • 请求用于启动应用的命令行。

诊断端口支持不同的传输,具体取决于平台。 目前,CoreCLR 和 Mono 运行时实现在 Windows 上使用命名管道,而在 Linux 和 macOS 上使用 Unix 域套接字。 Android、iOS 和 tvOS 上的 Mono 运行时实现使用 TCP/IP。 通道使用 自定义二进制协议。 大多数开发人员永远不会直接与基础通道和协议交互,而是使用代表其通信的 GUI 或 CLI 工具。 例如, dotnet-dumpdotnet-trace 工具抽象发送协议命令以捕获转储和启动跟踪。 对于想要编写自定义工具的开发人员, Microsoft.Diagnostics.NETCore.Client NuGet 包 提供基础传输和协议的 .NET API 抽象。

安全注意事项

诊断端口公开有关正在运行的应用程序的敏感信息。 如果不受信任的用户获得对此通道的访问权限,他们可以观察详细的程序状态,包括内存中的任何机密,并任意修改程序的执行。 在 CoreCLR 运行时中,默认诊断端口配置为只能由启动应用的同一用户帐户或具有超级用户权限的帐户访问。 如果安全模型不信任具有相同用户帐户凭据的其他进程,可以通过设置环境变量 DOTNET_EnableDiagnostics=0来禁用所有诊断端口。 此设置将阻止你使用外部工具(如 .NET 调试或任何 dotnet-* 诊断工具)的功能。

默认诊断端口

在 Windows、Linux 和 macOS 上,运行时默认在已知终结点上打开一个诊断端口。 这是 dotnet-* 诊断工具在未显式配置为使用备用端口时自动连接的端口。 终结点为:

  • Windows - 命名管道 \\.\pipe\dotnet-diagnostic-{pid}
  • Linux 和 macOS - Unix 域套接字 {temp}/dotnet-diagnostic-{pid}-{disambiguation_key}-socket

{pid} 是以十进制形式写入的进程 ID, {temp}TMPDIR 环境变量, /tmp 如果 TMPDIR 未定义/为空,则 {disambiguation_key} 为进程开始时间(以十进制形式写入)。 在 macOS 和 NetBSD 上,进程开始时间是 UNIX 时代以来的秒数。 在所有其他平台上,自启动时间以来,它就会发生异常。

在启动时暂停运行时

默认情况下,运行时在启动时立即执行托管代码,而不考虑任何诊断工具是否已连接到诊断端口。 有时,为了观察并分析初始程序行为,可以让运行时先等待运行托管代码,直到诊断工具已连接。 设置环境变量 DOTNET_DefaultDiagnosticPortSuspend=1 会导致运行时等到工具连接到默认端口。 如果在几秒钟后未附加任何工具,运行时会向控制台输出警告消息,说明它仍在等待工具附加。

配置其他诊断端口

注释

这仅适用于运行 .NET 5 或更高版本的应用。

Mono 和 CoreCLR 运行时都可以在 connect 角色中使用自定义配置的诊断端口。 Mono 还支持在listen角色中使用自定义 TCP/IP 端口,当在 Android 或 iOS 上使用dotnet-dsrouter时。 这些自定义端口是除默认端口之外的额外端口,默认端口仍然可用。 自定义端口很有用的一些常见原因:

  • 在 Android、iOS 和 tvOS 上,没有默认端口,因此需要配置端口才能使用诊断工具。
  • 在容器或防火墙的环境中,你可能想要设置一个可预测的终结点地址,该地址不会因进程 ID 而有所不同,因为默认端口也是如此。 然后,可以将自定义端口显式添加到允许列表或跨某些安全边界进行代理。
  • 对于监视工具,让工具侦听终结点非常有用,运行时会主动尝试连接到终结点。 这样就避免了监视工具需要持续轮询来检测新应用的启动。 在无法访问默认诊断端口的环境中,还无需为每个受监视的应用配置自定义终结点的监视器。

在诊断工具和 .NET 运行时之间的每条通信信道中,一方必须充当侦听器,等待另一方进行连接。 可以将运行时配置为充当 connect 任何端口的角色。 (也可以将 Mono 运行时配置为在任一端口上执行 listen 角色)。端口也可以独立配置为在启动时挂起,等待诊断工具发出继续命令。 配置用于连接的端口在远程终结点未侦听或连接丢失时,会无期限地重复进行连接尝试。 但是,该应用程序不会在等待建立连接时自动挂起托管代码。 如果希望应用程序在启动时等待建立连接,请使用“启动时挂起”选项。

使用环境变量配置 DOTNET_DiagnosticPorts 自定义端口。 此变量应设置为以分号分隔的端口说明列表。 每个端口说明都包含一个终结点地址和可选的修饰符,用于控制运行时 connectlisten 角色,以及是否应在启动时暂停运行。 在 Windows 上,终结点地址是没有 \\.\pipe\ 前缀的命名管道的名称。 在 Linux 和 macOS 上,它是 Unix 域套接字的完整路径。 在 Android、iOS 和 tvOS 上,地址是 IP 和端口。 例如:

  1. DOTNET_DiagnosticPorts=my_diag_port1 - (Windows) 运行时连接到命名管道 \\.\pipe\my_diag_port1
  2. DOTNET_DiagnosticPorts=/foo/tool1.socket;foo/tool2.socket - (Linux 和 macOS) 运行时同时连接到 Unix 域套接字 /foo/tool1.socket/foo/tool2.socket
  3. DOTNET_DiagnosticPorts=127.0.0.1:9000 - (Android、iOS 和 tvOS)运行时连接到 IP 127.0.0.1 的端口 9000。
  4. DOTNET_DiagnosticPorts=/foo/tool1.socket,nosuspend - (Linux 和 macOS) 此示例具有 nosuspend 修饰符。 运行时尝试连接到外部工具创建的 Unix 域套接字 /foo/tool1.socket 。 附加诊断端口通常会导致运行时在启动时暂停,等待恢复命令,但 nosuspend 会导致运行时不等待。

端口的完整语法为 address[,(listen|connect)][,(suspend|nosuspend)]. connect 如果两者 connect 均未指定或 listen 未指定,则为默认值(且 listen 仅受 Android 或 iOS 上的 Mono 运行时支持)。 suspend 如果两者 suspend 均未指定或 nosuspend 未指定,则为默认值。

在 dotnet 诊断工具中的用途

dotnet-dumpdotnet-countersdotnet-trace 等工具都支持通过诊断端口与 .NET 应用通信的动词,如 collectmonitor

  • 当这些工具使用 --processId 参数时,该工具会自动计算默认诊断端口地址并连接到该地址。
  • 指定 --diagnostic-port 参数时,该工具将侦听给定地址,应使用 DOTNET_DiagnosticPorts 环境变量来配置应用以连接。 有关 dotnet-counters 的完整示例,请参阅 “使用诊断端口”。

使用 ds-router 代理诊断端口

所有 dotnet-* 诊断工具都希望连接到本地命名管道或 Unix 域套接字的诊断端口。 Mono 通常在独立硬件或需要通过 TCP 的代理才能访问的模拟器上运行。 dotnet-dsrouter 工具可以将本地命名管道或 Unix 域套接字代理到 TCP,以便可以在这些环境中使用这些工具。 有关详细信息,请参阅 dotnet-dsrouter