诊断端口

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

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

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

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

安全注意事项

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

注意

.NET 6 为用于配置 .NET 运行时行为的环境变量标准化前缀 DOTNET_ 而不是 COMPlus_。 但是,COMPlus_ 前缀仍将继续正常工作。 如果使用的是早期版本的 .NET 运行时,则环境变量仍应该使用 COMPlus_ 前缀。

默认诊断端口

在 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 角色的身份使用自定义的已配置诊断端口。 当在 Android 或 iOS 上与 dotnet-dsrouter 结合使用时,Mono 还支持 listen 角色中的自定义 TCP/IP 端口。 这些自定义端口是除了默认端口之外的其他可用端口。 自定义端口很有用的一些常见原因:

  • 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)运行时连接到端口 9000 上的 IP 127.0.0.1。
  4. DOTNET_DiagnosticPorts=/foo/tool1.socket,nosuspend -(Linux 和 macOS)本例使用 nosuspend 修饰符。 该运行时尝试连接到外部工具创建的 Unix 域套接字 /foo/tool1.socket。 其他诊断端口通常会导致运行时在启动时暂停,等待继续命令,但 nosuspend 会导致运行时不等待。

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

dotnet 诊断工具中的用法

dotnet-dumpdotnet-countersdotnet-trace 等工具都支持谓词 collectmonitor,这些谓词通过诊断端口与 .NET 应用通信。

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

使用 ds-router 代理诊断端口

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