.NET MonoVM 诊断追踪实战指南:基于 EventPipe、DiagnosticServer 与 dotnet-dsrouter 的 iOS/Android 性能剖析

发布时间:2026/9/18 22:52:03
.NET MonoVM 诊断追踪实战指南:基于 EventPipe、DiagnosticServer 与 dotnet-dsrouter 的 iOS/Android 性能剖析 .NET MonoVM 诊断追踪实战指南基于 EventPipe、DiagnosticServer 与 dotnet-dsrouter 的 iOS/Android 性能剖析【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtimeMonoVMMono 运行时在 .NET runtime 仓库中内置了完整的诊断追踪组件支持通过 EventPipe 与 DiagnosticServer 生成包含运行时事件与自定义EventSource事件的 nettrace 文件。本文以 docs/design/mono/diagnostics-tracing.md 为骨架结合仓库内src/mono、src/tasks的源码与示例工程系统讲解如何在 Android/iOS 上构建、配置并运行dotnet-trace、dotnet-counters、dotnet-gcdump等诊断工具涵盖动态/静态运行时组件、DOTNET_DiagnosticPorts环境变量、dotnet-dsrouter四种路由模式、启动追踪、方法级enter/leave剖析与 GC 堆转储等完整实战方案。读完本文你将能够独立为 MonoVM 移动端应用搭建从构建时组件开启到运行时数据采集再到nettrace 文件分析的全链路诊断工作流。MonoVM 诊断追踪组件概览MonoVM 对 EventPipe 与 DiagnosticServer 提供了一等支持EventPipe 负责在运行时内部收集事件DiagnosticServer 负责对外暴露诊断通道二者协同生成包含运行时事件以及自定义EventSource事件的 nettrace 文件。需要特别注意的是其构建与部署约束根据构建配置组件既可以动态加载Android也可以静态链接iOSEventPipe 主要用于开发/测试周期不应被部署或链接进提交给应用商店审核与发布的构建产物中。在仓库中Mono 侧 EventPipe/DiagnosticServer 的接入点位于 src/mono/mono/eventpipe/eventpipe.cmake当ENABLE_PERFTRACING开启时编译单元会包含 EventPipe 的运行时 shimep-rt-mono.c、ep-rt-mono-runtime-provider.c、ep-rt-mono-profiler-provider.c以及 DiagnosticServer 的 shimds-rt-mono.c并通过gen-eventing.cmake生成事件相关代码。支持的诊断场景与能力边界MonoVM 支持 .NET 诊断工具链主要来自 dotnet/diagnostics 仓库中的多种场景常见的是使用dotnet-counters与dotnet-trace收集并分析运行时性能数据。但并非所有 CoreCLR 场景都能在 MonoVM 上复用不支持通过 EventPipe 请求 core dump、附加 profiler 等能力在 MonoVM 上目前不可用NativeRuntimeEvents 子集由于运行时差异CoreCLR 的大量 NativeRuntimeEvents 并不适用于 MonoVM只有经过挑选的事件会被接入。当前 MonoVM 支持的 NativeRuntimeEvents 清单维护在 src/mono/mono/eventpipe/gen-eventing-event-inc.lst其中包含如EEStartupStart_V1、ExceptionThrown_V1、GCStart_V2、GCEnd_V1、MethodLoad_V1、MethodJittingStarted_V1、ModuleLoad_V2、TypeLoadStart/Stop、ThreadCreated/Terminated、线程池相关事件ThreadPoolWorkerThreadStart/Stop、ThreadPoolMinMaxThreads等以及Microsoft-DotNETRuntimeMonoProfiler:*ETW / LTTng 未启用由于重点聚焦 EventPipe 与移动平台iOS/AndroidNativeRuntimeEvents 目前没有集成/启用 ETW 与 LTTng provider。按平台区分的 DiagnosticServer 构建配置MonoVM 运行在多种平台上DiagnosticServer 的传输层随平台能力而不同平台传输方式桌面WindowsNamedPipes桌面Linux、macOSUnixDomainSockets移动端Android/iOS或远程沙箱环境TCP/IP支持 connect 与 listen 两种模式桌面端配置与 CoreCLR 的 DiagnosticServer 构建方式一致工作方式相同。移动端使用 TCP/IP 以更好处理远程目标它同时支持两类场景connect 场景运行时作为 TCP/IP 客户端回连到工具端listen 场景运行时作为 TCP/IP 监听者等待工具端连接例如启动追踪需要挂起运行时。根据平台允许的能力部分平台不允许监听 socket与追踪场景启动追踪需要挂起运行时可以组合使用这些模式。dotnet-dsrouter移动端诊断的桥接路由器现有诊断工具只支持NamedPipes/UnixDomainSockets为了在面向移动平台上的 MonoVM 时透明复用这些工具dotnet/diagnostics 仓库实现了一个新组件dotnet-dsrouter。它把运行在远程目标上的应用以本地进程的形式呈现给诊断工具工具连接本地 IPC 端点dotnet-dsrouter负责把 IPC 流量转发到远程目标上 MonoVM 所处理的 TCP/IP 端点。dotnet-dsrouter实现了 4 种模式根据配置均可与 DiagnosticServer 及诊断工具配合使用模式IPC 端TCP 端server-serverIPC serverTCP serverclient-serverIPC clientTCP serverserver-clientIPC serverTCP clientclient-clientIPC clientTCP client此外dotnet-dsrouter还改进/解决了两个问题支持在反向连接reversed connect场景下同时挂接多个诊断工具客户端把反向连接场景如启动追踪转换成普通的直接连接场景——使用 client-server 或 server-server 模式即可。构建包含诊断追踪支持的应用根据平台不同在构建应用时有不同的推荐方式引入诊断追踪支持。Android动态组件shared objectAndroid 使用动态组件支持组件以共享库shared object形式随包分发运行时会在与libmonosgen-2.0.so相同的目录尝试加载组件。加载失败则组件被禁用加载成功则启用。因此启用/禁用组件只需在 APK 中包含/排除对应的.so文件放在libmonosgen-2.0.so同目录同一份运行时构建可以支持任意组件组合。Android runtime pack 内置的运行时组件包括debugger、hot_reload、diagnostics_tracing、marshal-ilgen。默认场景下动态版本应与libmonosgen-2.0.so一起使用runtime pack 也提供了静态版本供以libmonosgen-2.0.a静态构建运行时的情况使用。静态链接时链接libmono-component-*-stub-static.a禁用组件链接libmono-component-*-static.a启用组件libmono-component-diagnostics_tracing.so libmono-component-diagnostics_tracing-static.a libmono-component-diagnostics_tracing-stub-static.aiOS静态组件static libraryiOS 使用静态组件支持组件以静态库形式存在必须与libmonosgen-2.0.a一起链接生成最终应用。静态组件有两种形态组件库与 stub桩库——链接组件库启用组件链接 stub 库禁用组件。通过选择链接哪种库可以构建出启用指定组件、禁用其他组件的应用。所有组件都必须被链接进来要么组件库、要么 stub 库否则libmonosgen-2.0.a中会出现未解析符号。iOS runtime pack 内置组件与 Android 一致debugger、hot_reload、diagnostics_tracing、marshal-ilgen。对应文件libmono-component-diagnostics_tracing-static.a libmono-component-diagnostics_tracing-stub-static.a注意iOS 模拟器具备额外能力iOS runtime pack 同时提供共享库与静态库构建类似上文 Android 场景。启用运行时组件RuntimeComponents 与 UseAllRuntimeComponents当使用AndroidAppBuilderTask构建 Android、或AppleAppBuilderTask构建 iOS 时可以通过 MSBuild 项RuntimeComponents把指定组件包含进生成的应用。默认情况下该列表为空即所有组件都被禁用。只启用单个组件例如diagnostics_tracing在项目文件中加入ItemGroup RuntimeComponents Includediagnostics_tracing / /ItemGroup如果需要包含全部组件有两种方式手动列出所有支持的组件ItemGroup RuntimeComponents Includedebugger / RuntimeComponents Includehot_reload / RuntimeComponents Includediagnostics_tracing / RuntimeComponents Includemarshal-ilgen / /ItemGroup自动包含使用 MSBuild 属性UseAllRuntimeComponents它会自动把全部受支持组件加入列表。前提是在项目文件中导入 src/mono/msbuild/android/build/AndroidBuild.targets然后通过命令行或项目文件开启该属性-p:UseAllRuntimeComponentstruePropertyGroup UseAllRuntimeComponentstrue/UseAllRuntimeComponents /PropertyGroup从源码看src/mono/msbuild/common/CommonMobileBuild.props 中UseAllRuntimeComponents默认值为false而 src/mono/msbuild/android/build/AndroidBuild.targets 中当UseAllRuntimeComponents true时会把(_MonoRuntimeAvailableComponents)全部加入RuntimeComponents否则至少确保marshal-ilgen被保留在组件列表里KeepDuplicatesfalse因为轻量级 marshal IL 生成是移动端默认需要的能力。此外构建任务对组件与诊断端口之间有强校验src/tasks/AppleAppBuilder/AppleAppBuilder.cs 中如果设置了DiagnosticPorts却没有在RuntimeComponents里包含diagnostics_tracing会直接抛出ArgumentException提示使用 DiagnosticPorts 需要 diagnostics_tracing 运行时组件。这解释了为什么所有诊断示例都必须先确保组件被启用。安装诊断客户端工具在开发机上安装三个命令行工具通过 dotnet 全局工具方式$ dotnet tool install -g dotnet-trace --add-sourcehttps://aka.ms/dotnet-tools/index.json $ dotnet tool install -g dotnet-counters --add-sourcehttps://aka.ms/dotnet-tools/index.json $ dotnet tool install -g dotnet-dsrouter --add-sourcehttps://aka.ms/dotnet-tools/index.json如果工具已安装把install关键字换成update即可全部更新到最新版本。注意所有工具的版本需要保持一致。运行带诊断追踪支持的应用默认情况下MonoVM 上的 EventPipe/DiagnosticServer 使用与 CoreCLR 相同的一组环境变量来控制其中最关键的是DOTNET_DiagnosticPorts——它决定运行时是回连还是接受诊断工具的请求。如果不定义它诊断服务器不会启动也就无法用诊断工具与运行时交互。根据平台、能力与场景不同DOTNET_DiagnosticPorts的内容会不一样。运行下述诊断场景前的前置条件构建/部署应用时确保diagnostics_tracing组件已启用只要用到任何EventSource相关内容构建应用时必须启用EventSourceSupport否则 ILLinker 可能裁掉所需的 EventSource 类。如果只是运行追踪、收集 SampleProfiler、DotNetRuntime 或 MonoProfiler provider 发出的原生 EventPipe 事件则EventSourceSupport可开可关在开发机上安装好所需诊断工具。默认 .NET 8 配置直接使用内置 profile从 .NET 8 开始dotnet-dsrouter增强了对其余场景的支持通过模拟器/模拟器环回接口loopback以及 usb 连接的物理设备可以使用更小的一组预设配置。dotnet-dsrouter会输出如何启动运行时、如何用诊断工具连接该实例的详细信息。以 iOS 模拟器为例使用ios-simprofile 启动$ dotnet-dsrouter ios-sim -i WARNING: dotnet-dsrouter is a development tool not intended for production environments. How to connect current dotnet-dsrouter pid26600 with iOS simulator and diagnostics tooling. Start an application on iOS simulator with ONE of the following environment variables set: [Default Tracing] DOTNET_DiagnosticPorts127.0.0.1:9000,nosuspend,listen [Startup Tracing] DOTNET_DiagnosticPorts127.0.0.1:9000,suspend,listen Run diagnostic tool connecting application on iOS simulator through dotnet-dsrouter pid26600: dotnet-trace collect -p 26600 See https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-dsrouter for additional details and examples. info: dotnet-dsrouter-26600[0] Starting dotnet-dsrouter using pid26600 info: dotnet-dsrouter-26600[0] Starting IPC server (dotnet-diagnostic-dsrouter-26600) -- TCP client (127.0.0.1:9000) router.-i参数会输出足够的信息如何按默认/启动追踪需要配置DOTNET_DiagnosticPorts以及如何针对特定dotnet-dsrouter实例运行诊断工具。例如配合上述实例做默认追踪为模拟器上启动的应用设置环境变量DOTNET_DiagnosticPorts127.0.0.1:9000,nosuspend,listen然后以dotnet-dsrouter的 pid 运行诊断工具$ dotnet-trace collect -p 26600把启动应用的环境变量改为DOTNET_DiagnosticPorts127.0.0.1:9000,suspend,listen即可通过同一dotnet-dsrouter实例做启动追踪。.NET 8 版本的dotnet-dsrouter支持以下 profile可分别用于对 iOS/Android 的模拟器/模拟器/真机运行诊断工具ios-simiosandroid-emuandroid使用-i运行这些 profile 会给出在应用与诊断工具两侧的详细启动说明。若默认 profile 不够用下文介绍所有底层配置细节与选项。自定义配置模拟器/模拟器上的手动路由回连connect场景在 iOS 上以DOTNET_DiagnosticPorts127.0.0.1:9000,nosuspend启动应用或在 Android 上以DOTNET_DiagnosticPorts10.0.2.2:9000,nosuspend启动10.0.2.2是 Android 模拟器访问宿主机环回接口的地址运行时就会连接本机监听在环回端口 9000可换成任意可用端口上的dotnet-dsrouter。运行时连接成功后dotnet-counters、dotnet-trace等工具就可以连向dotnet-dsrouter的本地 IPC 接口。若要把启动事件包含进 EventPipe 会话把nosuspend换成suspend运行时会在启动时挂起、等待诊断工具连接后再继续。listen 场景如果平台允许可以把 TCP/IP 监听器推到设备上本机只运行一个 TCP/IP 客户端去连运行时的监听器。使用DOTNET_DiagnosticPorts127.0.0.1:9000,nosuspend,listen会在模拟器/模拟器/设备上绑定环回接口并监听Android 上可配置adb端口转发iOS 模拟器则可让运行时直接绑定本机环回接口。此场景下dotnet-dsrouter配置为 TCP/IP 客户端。dotnet-dsrouter提供--forward-port参数简化跨模拟器/模拟器/usb 设备的端口转发Android--forward-port会自动适配配置无论模拟器还是 usb 连接的设备都会自动执行adb forward或adb reverse做端口转发/反向转发从而始终可以使用127.0.0.1这类环回接口。前提是dotnet-dsrouter能在PATH中找到adb或设置ANDROID_SDK_ROOT指向 Android SDK 安装目录iOS--forward-port适用于DiagnosticServer 在 usb 连接的设备上以 listen 模式运行并使用环回接口的场景。运行时配置环境变量DIAGNOSTIC_PORTS127.0.0.1:9000,suspend|nosuspend,listen使用suspend运行时在启动期间等待工具连接通常用于分析启动性能使用nosuspend运行时正常启动不等待任何诊断工具连接。针对 Android 模拟器或 usb 设备启动路由server-client本地 IPC server TCP client配合--forward-port Android$ dotnet-dsrouter server-client -ipcs ~/myport -tcpc 127.0.0.1:9000 --forward-port Android针对 iOS 模拟器$ dotnet-dsrouter server-client -ipcs ~/myport -tcpc 127.0.0.1:9000针对 usb 连接的 iOS 设备$ dotnet-dsrouter server-client -ipcs ~/myport -tcpc 127.0.0.1:9000 --forward-port iOS无论使用suspend|nosuspend、Android|iOS还是simulator|emulator|device场景诊断工具统一这样运行$ dotnet-trace collect --diagnostic-port ~/myport,connect或$ dotnet-counters monitor --diagnostic-port ~/myport,connect示例在 iOS 模拟器上用 dotnet-countersdotnet-dsrouter需要以兼容配置运行可以每次运行都启动一个新实例也可以让一个后台实例以相同配置跨多个会话长期运行。默认 .NET 8 配置确保 src/mono/sample/iOS/Makefile 中启用如下配置RUNTIME_COMPONENTSdiagnostics_tracing DIAGNOSTIC_PORTS127.0.0.1:9000,nosuspend,listen$ dotnet-dsrouter ios-sim $ dotnet-counters monitor -p dotnet-dsrouter pid自定义配置RUNTIME_COMPONENTSdiagnostics_tracing DIAGNOSTIC_PORTS127.0.0.1:9000,nosuspend$ dotnet-dsrouter server-server -ipcs ~/myport -tcps 127.0.0.1:9000 cd src/mono/sample/iOS/ $ make run-sim$ dotnet-counters monitor --diagnostic-port ~/myport,connect示例在 Android 模拟器上用 dotnet-counters默认 .NET 8 配置确保 src/mono/sample/Android/Makefile 中启用RUNTIME_COMPONENTSdiagnostics_tracing DIAGNOSTIC_PORTS10.0.2.2:9000,nosuspend,connect$ dotnet-dsrouter android-emu cd src/mono/sample/Android/ $ make run$ dotnet-counters monitor -p dotnet-dsrouter pid自定义配置RUNTIME_COMPONENTSdiagnostics_tracing DIAGNOSTIC_PORTS10.0.2.2:9000,nosuspend$ dotnet-dsrouter server-server -ipcs ~/myport -tcps 10.0.2.2:9000 cd src/mono/sample/Android/ $ make run$ dotnet-counters monitor --diagnostic-port ~/myport,connect若改用adb端口转发上例中可使用127.0.0.1:9000并在dotnet-dsrouter启动参数里加--forward-port Android它会自动执行所需的adb命令。注意dotnet-dsrouter需要能找到adb工具才能工作。示例在 iOS 模拟器上用 dotnet-trace 做启动追踪默认 .NET 8 配置src/mono/sample/iOS/MakefileRUNTIME_COMPONENTSdiagnostics_tracing DIAGNOSTIC_PORTS127.0.0.1:9000,suspend,listen$ dotnet-dsrouter ios-sim $ dotnet-counters monitor -p dotnet-dsrouter pid自定义配置client-server 模式RUNTIME_COMPONENTSdiagnostics_tracing DIAGNOSTIC_PORTS127.0.0.1:9000,suspend$ dotnet-dsrouter client-server -tcpc ~/myport -tcps 127.0.0.1:9000 $ dotnet-trace collect --diagnostic-port ~/myportcd src/mono/sample/iOS/ $ make run-sim由于dotnet-dsrouter支持多种模式启动追踪也可以使用server-server 模式RUNTIME_COMPONENTSdiagnostics_tracing DIAGNOSTIC_PORTS127.0.0.1:9000,suspend$ dotnet-dsrouter server-server -ipcs ~/myport -tcps 127.0.0.1:9000 cd src/mono/sample/iOS/ $ make run-sim$ dotnet-trace collect --diagnostic-port ~/myport,connect示例在 Android 模拟器上用 dotnet-trace 做启动追踪默认 .NET 8 配置src/mono/sample/Android/MakefileRUNTIME_COMPONENTSdiagnostics_tracing DIAGNOSTIC_PORTS10.0.2.2:9000,suspend,connect$ dotnet-dsrouter android-emu cd src/mono/sample/Android/ $ make run$ dotnet-counters monitor -p dotnet-dsrouter pid自定义配置client-server 模式RUNTIME_COMPONENTSdiagnostics_tracing DIAGNOSTIC_PORTS10.0.2.2:9000,suspend$ dotnet-dsrouter client-server -tcpc ~/myport -tcps 127.0.0.1:9000 $ dotnet-trace collect --diagnostic-port ~/myportcd src/mono/sample/Android/ $ make run同样Android 也可用server-server 模式RUNTIME_COMPONENTSdiagnostics_tracing DIAGNOSTIC_PORTS10.0.2.2:9000,suspend$ dotnet-dsrouter server-server -ipcs ~/myport -tcps 10.0.2.2:9000 cd src/mono/sample/Android/ $ make run$ dotnet-trace collect --diagnostic-port ~/myport,connect若改用adb端口转发可把上述场景中的地址换成127.0.0.1:9000并给dotnet-dsrouter加--forward-port Android。从示例可以看到src/mono/sample/iOS/Makefile 与 src/mono/sample/Android/Makefile 是仓库自带的参考实现它们把DIAGNOSTIC_PORTS通过/p:DiagnosticPorts$(DIAGNOSTIC_PORTS)传给dotnet publish最终由AndroidAppBuilderTask/AppleAppBuilderTask注入到应用的启动环境变量中Makefile 顶部注释也明确说明DIAGNOSTIC_PORTS与RuntimeComponentsdiagnostics_tracing存在相互约束关系——启用了DiagnosticPorts就必须包含diagnostics_tracing组件组件已包含但DIAGNOSTIC_PORTS未设置时则可用应用启动时的DOTNET_DiagnosticPorts环境变量来启用诊断。真机运行真机运行时使用同样的环境变量若设备通过 usb 连接开发机可按上文所述使用环回接口——但 Android 需要adb端口转发iOS 需要usbmux。如果环回接口不可用也可以在DOTNET_DiagnosticPorts中指定开发机与设备之间任意可达的接口地址但要记住该连接是未认证、未加密的。以下场景均使用环回接口配合 usb 连接的设备。Android默认 .NET 8 配置以环境变量DIAGNOSTIC_PORTS127.0.0.1:9000,suspend,connect启动应用$ dotnet-dsrouter android 在 usb 连接的设备上运行应用然后$ dotnet-trace collect -p dotnet-dsrouter pid自定义配置以DIAGNOSTIC_PORTS127.0.0.1:9000,suspend启动应用$ dotnet-dsrouter server-server -ipcs ~/myport -tcps 127.0.0.1:9000 --forward-port Android 在设备上运行应用然后$ dotnet-trace collect --diagnostic-port ~/myport,connect同一场景也可把 TCP/IP 监听器推到设备上只需做如下改动DIAGNOSTIC_PORTS127.0.0.1:9000,suspend,listen$ dotnet-dsrouter server-client -ipcs ~/myport -tcpc 127.0.0.1:9000 --forward-port Android iOS默认 .NET 8 配置以DIAGNOSTIC_PORTS127.0.0.1:9000,suspend,listen启动应用$ dotnet-dsrouter ios 在 usb 连接的设备上运行应用然后$ dotnet-trace collect -p dotnet-dsrouter pid自定义配置以DIAGNOSTIC_PORTS*:9000,suspend,listen启动应用*表示绑定所有可达接口$ dotnet-dsrouter server-client -ipcs ~/myport -tcpc 127.0.0.1:9000 --forward-port iOS 在设备上运行应用然后$ dotnet-trace collect --diagnostic-port ~/myport,connect注意iOS 只支持在设备上以 listen 模式运行 DiagnosticServer 并使用环回接口 dotnet-dsrouter以 server-clientIPC server、TCP/IP client 端口转发这一种组合。单文件 EventPipe 会话无需诊断服务器如果应用支持受控的运行时关闭——即在进程终止前会调用mono_jit_cleanup——就可以通过环境变量运行单文件file-basedEventPipe 会话配置方式与 .NET 文档中使用环境变量追踪一致。.NET 6 起新增变量DOTNET_EventPipeOutputStreaming可让数据周期性刷入输出文件。如果应用不支持受控关闭该模式将无法工作因为 rundown 事件只在关闭会话并刷新 memory manager 时发出——若应用在终止前没有调用mono_jit_cleanup生成的 nettrace 文件会缺少还原符号调用栈所需的 rundown 事件。单文件 EventPipe 会话会在工作目录产生一个文件应用终止后用平台相关工具取出即可。由于文件会话不经过诊断服务器因此无需设置DOTNET_DiagnosticPorts也无需运行dotnet-dsrouter。分析启动期的 JIT/Loader 事件把dotnet-trace中Microsoft-Windows-DotNETRuntimeprovider 的默认日志级别调高会在 nettrace 文件中包含更多 JIT 与 loader 活动细节例如所有已加载程序集、已加载类型、已加载/已 JIT 的方法以及时间与大小指标——这些都是分析启动性能、已加载/JIT 方法大小、JIT 耗时全体/子集/单方法的宝贵数据。只收集启动期间Microsoft-Windows-DotNETRuntime事件时使用上述任一启动追踪场景并给dotnet-trace加上--clreventlevel verbose --providers Microsoft-Windows-DotNETRuntimePerfView 内置了 JIT/Loader 统计的分析器可以把生成的 nettrace 文件加载进 PerfView 分析或用自定义TraceEvent解析器分析。追踪 MonoVM profiler 事件方法级剖析从 .NET 8 开始实验性的 profiler providerMicrosoft-DotNETRuntimeMonoProfiler默认被禁用。要启用它需要在 MonoVM 专属环境变量中加入MONO_DIAGNOSTICS--diagnostic-mono-profilerenableMonoVM 提供一个 EventPipe provider把大部分底层 Mono profiler 事件映射为原生 EventPipe 事件经由Microsoft-DotNETRuntimeMonoProfiler。该 provider 的主要目的是简化从旧的 MonoVM log profiler 迁移到 nettrace同时也带来了若干 MonoVM profiler 才有的特性。从源码看src/mono/mono/eventpipe/ep-rt-mono-profiler-provider.c 实现了该 provider 的运行时侧逻辑其中直接引用了mono/metadata/callspec.h的MonoCallSpec用于 callspec 过滤并通过开关变量控制 provider 是否启用。方法追踪enter/leave与关键字Mono profiler 支持方法追踪即方法进入/离开事件方法执行计时可用于 JIT/Interpreter 模式AOT 模式需要传给 MonoVM AOT 编译器。由于 enter/leave 会产生大量事件存在触发 buffer manager 容量上限、临时丢弃事件的风险可通过提高 buffer manager 内存上限或减少被插桩的方法数量来缓解。因为 enter/leave 依赖插桩必须在方法被 JIT 时就启用。方法追踪由 MonoProfiler provider 的关键字keyword控制0x20000000开始捕获 enter/leave 事件0x40000000000启用插桩覆盖 EventPipe 会话生命周期内所有被 JIT 的方法。若 EventPipe 会话未在方法被 JIT 时运行方法不会被插桩也就不会触发 enter/leave 事件。为确保所有需要的方法都被插桩应使用启动剖析见上文配置运行时并用以下 provider 配置运行dotnet-trace--providers Microsoft-DotNETRuntimeMonoProfiler:0x40020000000;4用 callspec 精确控制插桩要完全掌控插桩不依赖正在运行的 EventPipe 会话可设置 MonoVM 专属环境变量MONO_DIAGNOSTICS--diagnostic-mono-profiler-callspec它会为所有匹配 callspec 的 JIT 方法启用 enter/leave 剖析。callspec 使用 MonoVM callspec 格式关键字说明all所有程序集none无程序集program入口点程序集assembly指定程序集M:Type:Method指定方法N:Namespace指定命名空间T:Type指定类型EXPR包含表达式-EXPR排除表达式包含与排除表达式可以用,分隔组合使用。追踪所有方法、但排除System.Int32类型MONO_DIAGNOSTICS--diagnostic-mono-profiler-callspecall,-T:System.Int32追踪Program类型中的所有方法MONO_DIAGNOSTICS--diagnostic-mono-profiler-callspecT:Program注意事项如果没有正在运行的使用方法追踪关键字的 MonoProfiler provider 的 EventPipe 会话即使方法已通过MONO_DIAGNOSTICS插桩也不会发出任何事件用MONO_DIAGNOSTICS插桩方法后可以这样运行dotnet-trace--providers Microsoft-DotNETRuntimeMonoProfiler:0x20000000:4由于所有匹配 callspec 的方法都会被插桩可以在应用生命周期内的任意时刻按需捕获 enter/leave 事件。推荐工作流先用 SampleProfiler provider 定位值得深挖的热点路径再以匹配的 callspec 启用追踪用 MonoProfiler provider 的追踪关键字跟踪方法执行。当然也可以用 callspec 或 provider 驱动的插桩做启动期 enter/leave 剖析但要小心在未使用 callspec 时可能产生海量事件。注意EventPipe 技术本身有事件发射开销会影响 enter/leave 的测量精度。如果需要更精确的插桩建议直接实现一个处理 enter/leave 回调的 Mono profiler provider。收集 GC 转储.NET 8使用 dotnet-gcdump从 .NET 8 开始MonoVM 支持使用dotnet-gcdump之类的工具收集 GC dump无需使用Microsoft-DotNETRuntimeMonoProfiler或自定义工具来分析 GC dump。.NET 8 之前通过 MonoProfiler provider.NET 8 之前MonoVM 的 EventPipe providerMicrosoft-DotNETRuntimeMonoProfiler包含多个与 GC 相关的事件可用于跟踪分配allocations、根roots以及其他 GC 事件并可生成 GC 堆转储heap dump。按需请求堆转储用dotnet-trace按以下 provider 配置请求堆转储--providers Microsoft-DotNETRuntimeMonoProfiler:0x8900001:4这会触发一次堆转储包含对象引用、对象类型信息以及 GC 事件可用其检测转储开始/完成以决定何时结束 EventPipe 会话。用dotnet-trace时没有明确的转储完成指示可以观察流大小计数器当不再有新数据写入流时即可关闭会话。对同一应用实例在不同时间点取多个转储可以用自定义工具对比转储跟踪 GC 内存的增加/减少并定位是哪些对象类型导致了变化。该 provider 还可以提供更多细节根注册/反注册、handle 创建/删除、GC 分配含调用栈需要启动参数启用、对象终结等可结合堆转储获得 GC 堆活动的完整轨迹把单个分配追溯到其来源调用栈。跟踪带调用栈的 GC 分配需要在应用启动时用环境变量启用MONO_DIAGNOSTICS--diagnostic-mono-profileralloc注意这会改变底层分配器影响运行时性能但在创建了启用分配跟踪关键字的 EventPipe 会话之前不会发出任何事件。随后用以下 provider 配置开启分配跟踪会话--providers Microsoft-DotNETRuntimeMonoProfiler:0x200000:4多会话组合策略可以搭建一个长时间运行的 EventPipe 会话收集所有需要的 GC 事件包括多次堆转储再用另一个 EventPipe 会话按需触发堆转储该会话不会收到额外事件只负责触发转储--providers Microsoft-DotNETRuntimeMonoProfiler:0x800000:4组合不同含 GC 信息的会话可以在特定时间段内跟踪所有 GC 分配减少捕获数据量同时把堆转储放入独立会话从而对单个对象实例做GC 内存增减 ↔ 分配调用栈的完整分析。分析 nettrace 文件通过 EventPipe 会话收集的事件保存在 nettrace 文件中可用 PerfView、Speedscope、ChromiumChrome 性能分析器或 Visual Studio 等工具分析dotnet-trace也提供 convert 能力把采集文件转换成其他格式。更灵活的方式是使用 dotnet/diagnostics 仓库的诊断客户端库Diagnostic Client Library分析 trace 文件——可以完全自由地提取 nettrace 中的任意信息还可以实现自定义工具直接连接运行中的应用、对事件流做实时分析。TraceEvent库可用于实现自定义 nettrace 解析器例如用于解析启动追踪、插桩方法执行、.NET 8 之前 MonoVM GC 堆转储分析的示例工具。在 MonoVM 上开发 EventPipe/DiagnosticServer关于在 MonoVM 上扩展诊断能力原文档明确了三个待补充的专题目前标记为 TODOEventPipe/DiagnosticServer 库设计如何添加新的 NativeRuntimeEvent如何添加新的组件 API。从现状看事件清单集中维护在 src/mono/mono/eventpipe/gen-eventing-event-inc.lstMono 侧 shim 与构建接线集中在 src/mono/mono/eventpipe 目录由eventpipe.cmake在ENABLE_PERFTRACING条件下组织编译新增事件或组件 API 大概率需要同时触及这些生成配置与 shim 文件开发者可据此定位扩展点。小结围绕 MonoVM 的诊断追踪核心要点可归纳为四条主线构建期通过RuntimeComponents或UseAllRuntimeComponents在 Android 动态组件 / iOS 静态组件构建中启用diagnostics_tracing并注意DiagnosticPorts与组件之间的强校验参见 src/tasks/AppleAppBuilder/AppleAppBuilder.cs运行期以DOTNET_DiagnosticPorts或构建注入的DIAGNOSTIC_PORTS控制运行时是回连connect还是监听listen、是否挂起suspend等待工具联通层dotnet-dsrouter的四种模式server-server、client-server、server-client、client-client配合--forward-port把移动端 TCP/IP 透明桥接到本地 IPC供dotnet-trace/dotnet-counters直接使用数据层利用Microsoft-DotNETRuntimeMonoProfiler的方法追踪关键字与 callspec、GC 转储关键字组合获取从启动 JIT/Loader 事件到 GC 分配调用栈的全维度数据最终统一在 nettrace 文件中分析。这套工作流特别适合移动端性能分析场景启动优化、热点定位、GC 内存诊断且仓库中的 src/mono/sample/iOS/Makefile 与 src/mono/sample/Android/Makefile 提供了可直接照搬的端到端示例。【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考