今年 5 月份的时候研究分布式追踪的问题知道了的拦截方式比较零散, 刚好 8 月份的时候看到这篇文章, 这个文章总结的比较完整. 存档了很久, 趁今天有空翻译给大家. 原文地址, 校验: 张蘅水
.NET 是一个托管运行时 https://en.wikipedia.org/wiki/Managed_code , 这意味着它提供了 "管理" 您的程序的高级功能, 从简介到公共语言运行时(CLR)(2007 年编写):
运行时具有许多功能, 因此按如下方式对它们进行分类很有用:
基本功能 对其他功能设计有广泛影响的功能. 这些包括:
1. 垃圾收集
2. 记忆安全和类型安全
3. 对编程语言的高级支持.
辅助功能 - 许多有用的程序可能不需要基本特性所支持的功能:
1. 使用 AppDomains 进行程序隔离
2. 程序安全和沙盒
其他功能 - 所有运行时环境都需要但不利用 CLR 基本功能的功能. 相反, 它们是创建完整编程环境的结果. 其中包括:
1. 版本
2.Debugging/Profiling
3. 互操作
您可以看到,"Debugging/Profiling" 虽然不是基本或辅助功能, 但由于 "需要创建完整的编程环境" , 它仍然会进入列表.
这篇文章的其余部分将看什么 监测, 可观测性和内省功能核心 CLR 提供, 为什么他们是有用的, 如何提供他们.
为了便于浏览, 帖子分为 3 个主要部分(最后有一些 "额外阅读材料"):
诊断(Diagnostics)
Perf View(性能分析工具)
共同基础设施
未来的计划
剖析(Profiling)
ICorProfiler API
分析 v . 调试
调试(Debugging)
ICorDebug API
SOS 和 DAC
第三方调试器
记忆转储
诊断(Diagnostics)
首先, 我们将查看 CLR 提供的诊断信息, 传统上这些信息是通过 "Windows 事件跟踪"(ETW)提供的.
CLR 提供的各种事件涉及:
垃圾收集(GC)
即时 (JIT) 编译
模块和 AppDomains
线程和锁争用
以及更多
例如, 这是触发 AppDomain Load 事件的地方, 这是 Exception Thrown 事件, 这里是 GC Allocation Tick 事件.
Perf View
如果你想看到来自你的. NET 程序的 ETW 事件, 我建议使用优秀的 PerfView 工具 https://github.com/Microsoft/perfview , 从这些 PerfView 教程 https://channel9.msdn.com/Series/PerfView-Tutorial 开始, 或者这个优秀的演讲 PerfView: 终极. NET 性能工具. PerfView 被广泛认可, 因为它提供了宝贵的信息, 例如 Microsoft 工程师经常将其用于性能调查 https://github.com/dotnet/corefx/issues/28834 .
共同基础设施
但是, 如果从名称中不清楚, ETW 事件仅在 Windows 上可用, 这并不适合新的. NET"跨平台" 世界. 您可以在 Linux 上使用 PerfView 进行性能跟踪 (通过 LTTng https://lttng.org/ ), 但这只是 cmd-line 集合工具, 称为 "PerfCollect", 分析和丰富的 UI(包括 flamegraphs https://github.com/Microsoft/perfview/pull/502 ) 目前仅适用于 Windows.
但是如果你想分析. NET Performance Linux, 还有其他一些方法:
在 Linux 上使用. NET Core 获取 LTTng 事件的堆栈
Linux 性能问题 https://github.com/dotnet/coreclr/issues/18465
上面的第二个链接讨论了在. NET Core 中正在使用的新 "EventPipe" 基础架构(以及 EventSources 和 EventListeners, 你能发现一个主题!), 你可以看到它在跨平台性能监控设计中的目标. 在高层次上, 它将为 CLR 提供一个单独的位置来推动与诊断和性能相关的 "事件". 然后, 这些 "事件" 将被路由到一个或多个记录器, 例如, 可能包括 ETW,LTTng 和 BPF, 精确记录器由 CLR 运行的 OS / 平台确定..NET Cross-Plat 性能和事件设计中还有更多背景信息可以解释不同日志记录技术的优缺点.
"事件管道" 中正在进行的所有工作都在 "性能监控" 项目 https://github.com/dotnet/coreclr/projects/5 和相关的 "EventPipe" 问题中进行跟踪.
未来的计划
最后, 还有一个性能分析控制器的 (Performance Profiling Controller ) 未来计划, 其目标如下:
控制器负责以简单和跨平台的方式控制性能分析基础结构和. NET 性能诊断组件生成的性能数据.
我们的想法是通过从 "事件管道" 中提取所有相关数据, 通过 HTTP 服务器公开以下功能:
REST API
Pri 1: 简单分析: 为运行时间配置 X 个时间并返回跟踪.
Pri 1: 高级分析: 开始跟踪(以及配置)
Pri 1: 高级分析: 停止跟踪(对此调用的响应将是跟踪本身)
Pri 2: 获取与所有 EventCounters 或指定 EventCounter 相关的统计信息.
可浏览的 html 页面
Pri 1: 流程中所有托管代码堆栈的文本表示.
提供当前正在运行的用作简单诊断报告的快照概述.
Pri 2: 显示 EventCounters 的当前状态(可能具有历史记录).
* 提供现有计数器及其值的概述.
* 开放性问题: 我不相信存在必要的公共 API 来枚举 EventCounters.
我很高兴看到 "性能分析控制器 (Performance Profiling Controller)"(PPC?) 的位置, 我认为将这种内置到 CLR 中确实非常有价值, 这是其他运行时的内容 https://github.com/golang/go/wiki/Performance .
剖析(Profiling)
CLR 提供的另一个强大功能是 Profiling API, 它 (大部分) 被第三方工具用于在非常低级别挂钩到运行时. 您可以在此概述中找到有关 API 的更多信息, 但在较高级别, 它允许您连接在以下情况下触发的回调:
GC 相关事件发生
抛出异常
装配 / 卸载装配
更多, 更多
来自 BOTR 页面的图像分析 API - 概述
此外还有其他非常强大的功能. 首先, 您可以设置每次执行. NET 方法时调用的挂钩, 无论是在运行时还是用户代码中. 这些回调被称为 "进入 / 离开" 钩子, 并且有一个很好的示例显示如何使用它们, 但为了使它们工作, 您需要了解不同操作系统和 CPU 架构的 "调用约定" https://github.com/dotnet/coreclr/issues/19023 , 这并不总是容易的 https://github.com/dotnet/coreclr/issues/18977 . 另外, 作为警告, Profiling API 是一个只能通过 C / C ++ 代码访问的 COM 组件, 你不能在 C#/ F#/ VB.NET 中使用它!
其次, Profiler 能够通过 SetILFunctionBody()API 在 JIT 之前重写任何. NET 方法的 IL 代码. 这个 API 功能非常强大, 构成了许多. NET APM 工具的基础, 您可以在我之前的文章中了解更多关于如何使用它的方法. 如何模拟密封类和静态方法以及随附的代码.
ICorProfiler API
事实证明, 运行时必须执行各种疯狂的技巧才能使 Profiling API 正常工作, 只需查看进入此 PR 的内容允许重新连接 https://github.com/dotnet/coreclr/pull/19054 (有关'ReJIT'的详细信息, 请参阅 ReJIT:A How-To 指南).
所有 Profiling API 接口和回调的总体定义可在 \ vm\inc\corprof.idl 中找到 (请参阅接口说明语言). 但它分为 2 个逻辑部分, 一个是 Profiler ->'Execution Engine'(EE) 接口, 称为 ICorProfilerInfo:
- // Declaration of class that implements the ICorProfilerInfo* interfaces, which allow the
- // Profiler to communicate with the EE. This allows the Profiler DLL to get
- // access to private EE data structures and other things that should never be exported
- // outside of the EE.
这在以下文件中实现:
- \VM\proftoeeinterfaceimpl.h
- \VM\proftoeeinterfaceimpl.inl
- \VM\proftoeeinterfaceimpl.cpp
另一个主要部分是 EE -> Profiler 回调, 它们在 ICorProfilerCallback 界面下组合在一起:
- // This module implements wrappers around calling the profiler's
- // ICorProfilerCallaback* interfaces. When code in the EE needs to call the
- // profiler, it goes through EEToProfInterfaceImpl to do so.
这些回调在以下文件中实现:
- VM\eetoprofinterfaceimpl.h
- VM\eetoprofinterfaceimpl.inl
- VM\eetoprofinterfaceimpl.cpp
- VM\eetoprofinterfacewrapper.inl
最后, 值得指出的是, Profiler API 可能无法在. NET Core 运行的所有操作系统和 CPU-arch 上运行, 例如 Linux 上的 ELT 调用存根问题 https://github.com/dotnet/coreclr/issues/18977 , 有关详细信息, 请参阅 CoreCLR Profiler API 的状态.
分析和调试(Profiling v. Debugging)
除此之外,"分析" 和 "调试" 确实有一些重叠, 因此从 CLR 调试与 CLR 分析中了解. NET 运行时上下文中不同的 API 提供了什么是有帮助的.
调试(Debugging)
调试意味着不同的事情不同的人, 比如我问在 Twitter 上 "什么是你调试的. NET 程序的途径", 并得到了广泛的不同反应, 虽然反应两组含有一个很好的工具清单和技术, 所以他们值得一试, 谢谢 #Lazyweb!
但也许这句话最好总结一下 Debugging 究竟是什么
CLR 提供了与调试相关的非常广泛的功能, 但为什么需要提供这些服务, 优秀的帖子为什么托管调试与本机调试不同? 提供了 3 个理由:
可以在硬件级别抽象本机调试, 但需要在 IL 级别抽象管理调试
托管调试需要大量的信息, 直到运行时才可用
托管调试器需要与垃圾收集器 (GC) 协调
所以给一个体面的经验, CLR 具有提供更高级别的调试 API 称 ICorDebug, 这将在下面从 "常用的调试方案" 的图像中显示的 BOTR:
此外, 还有很好的描述了不同部分如何在管理断点如何工作中相互作用?, 虽然描述左和右是上图中的相反!
- Here's an overview of the pipeline of components:
- 1) End-user
- 2) Debugger (such as Visual Studio or MDbg).
- 3) CLR Debugging Services (which we call "The Right Side"). This is the implementation of ICorDebug (in mscordbi.dll).
- ---- process boundary between Debugger and Debuggee ----
- 4) CLR. This is mscorwks.dll. This contains the in-process portion of the debugging services (which we call "The Left Side") which communicates directly with the RS in stage #3.
- 5) Debuggee's code (such as end users C# program)
- ICorDebug API
但是如何实现所有这些以及从 CLR Debugging 简要介绍的不同组件是什么:
所有. Net 调试支持都在我们称之为 "The Dac" 的 dll 之上实现. 此文件 (通常命名 mscordacwks.dll) 是我们的公共调试 API(ICorDebug)以及两个私有调试 API 的构建块: SOS-Dac API 和 IXCLR.
在一个完美的世界中, 每个人都会使用 ICorDebug 我们的公共调试 API. 但是, 像您这样的工具开发人员所需的绝大多数功能都缺乏 ICorDebug. 这是我们正在修复的问题, 但这些改进将进入 CLR v.next, 而不是旧版本的 CLR. 实际上, ICorDebugAPI 仅在 CLR v4 中添加了对故障转储调试的支持. 任何调试 CLR v2 崩溃转储的人根本无法使用 ICorDebug!
(有关其他文章, 请参阅 SOS 和 ICorDebug)
该 ICorDebugAPI 实际上是分成多个接口, 也有在他们的 70! 我不会在这里列出所有内容, 但是我将展示它们所属的类别, 有关更多信息, 请参阅 ICorDebug 的分区, 其中包含此列表, 因为它更详细.
顶级(Debugging): ICorDebug + ICorDebug2 是顶级接口, 有效地充当 ICorDebugProcess 对象的集合.
回调(Callbacks): 通过调试器实现的回调对象上的方法调度托管调试事件
进程(Process): 这组接口表示正在运行的代码, 并包含与事件相关的 API.
代码 / 类型检查(Code / Type Inspection): 主要可以在静态 PE 映像上运行, 但实时数据有一些便捷方法.
执行控制 (Execution Control): 执行是 "检查" 线程执行的能力. 实际上, 这意味着放置断点(F9) 和踩踏 (F11 步入, F10 步进, S + F11 步出) 等. ICorDebug 的执行控制仅在托管代码中运行.
线程 + 调用堆栈(Threads + Callstacks): 调用堆栈是调试器检查功能的支柱. 以下接口与获取 callstack 有关. ICorDebug 仅公开调试托管代码, 因此堆栈跟踪仅受管理.
对象检查(Object Inspection): 对象检查是 API 的一部分, 它允许您在整个调试对象中查看变量的值. 对于每个接口, 我列出了 "MVP" 方法, 我认为必须简洁地传达该接口的用途.
另外需要注意的是, 与 Profiling APIs 一样, 调试 API 的支持级别因操作系统和 CPU 架构而异. 例如, 截至 2018 年 8 月,"没有针对 Linux ARM 进行托管调试和诊断的解决方案". 有关 "Linux" 支持的更多信息, 请参阅这篇很棒的文章, 在 Linux 上使用 LLDB 调试. NET Core, 并从 Microsoft 检出诊断存储库 https://github.com/dotnet/diagnostics , 其目标是更容易在 Linux 上调试. NET 程序.
最后, 如果你想看看 ICorDebugAPI 在 C#中的样子, 看一下 CLRMD 库中包含的包装器, 包括所有可用的回调(CLRMD 将在后面的文章中进行更深入的介绍).
SOS 和 DAC
"数据访问组件 (Data Access Component)"(DAC) 在 BOTR 页面中有详细讨论, 但实际上它提供了对 CLR 数据结构的 "进程外" 访问, 因此可以从另一个进程读取其内部详细信息. 这允许调试器 (via ICorDebug) 或'Son of Strike'(SOS)扩展进入 CLR 的运行实例或内存转储, 并找到如下内容:
所有正在运行的线程
托管堆上有哪些对象
有关方法的完整信息, 包括机器代码
当前的'堆栈跟踪'
除此之外, 如果您想要解释所有奇怪的名称和一点'.NET 历史课', 请参阅此 Stack Overflow 答案.
SOS 命令的完整列表非常令人印象深刻, 并且在 WinDBG 旁边使用它可以让您非常低级地了解程序和 CLR 中发生的情况. 要了解它是如何实现的, 让我们看一下这个! HeapStat 命令, 该命令可以为您提供. NET GC 正在使用的不同堆大小的摘要:
(来自 SOS 的图片: 即将发布的版本有一些新命令 - HeapStat)
这是代码流, 显示了 SOS 和 DAC 如何协同工作:
SOS 完整! HeapStat 命令(链接)
SOS!HeapStat 处理'Workstation GC' 的命令中的代码(链接)
- SOS
- GCHeapUsageStats(..)
功能, 重负荷(链接)
共享 DacpGcHeapDetails 包含指向 GC 堆中主数据的指针的数据结构, 例如段, 卡表和各代(链接)
GetGCHeapStaticData
填充 DacpGcHeapDetails 结构的 DAC 函数(链接)
共享
DacpHeapSegmentData
包含 GC 堆的单个 "段" 的详细信息的数据结构(链接)
GetHeapSegmentData(..)
填充
DacpHeapSegmentData
结构的 DAC(链接)
第三方'调试器'(3rd Party 'Debuggers')
由于 Microsoft 发布了调试 API, 它允许第三方使用 ICorDebug 接口, 这里列出了我遇到的一些内容:
调试器. NET Core 运行时 https://github.com/Samsung/netcoredbg 来自三星 https://github.com/Samsung
调试器提供 GDB / MI 或 VSCode 调试适配器接口, 并允许在. NET Core 运行时下调试. NET 应用程序.
可能是他们将. NET Core 移植到他们的 Tizen OS 的工作的一部分
https://github.com/0xd4d/dnSpy - ".NET 调试器和汇编编辑器"
一个非常令人印象深刻的工具, 它是一个'调试器','汇编编辑器','十六进制编辑器','反编译器'等等!
MDbg.exe(.NET Framework 命令行调试程序)
可以作为 NuGet 包和 GitHub 存储库使用, 也可以从 Microsoft 下载.
但是, 目前 MDBG 似乎不适用于. NET Core, 请参阅端口 MDBG 到 CoreCLR https://github.com/dotnet/coreclr/issues/1145 和 ETA 以将 mdbg 移植到 coreclr https://github.com/dotnet/coreclr/issues/8999 以获取更多信息.
JetBrains'Rider'允许在 Windows 上进行. NET Core 调试
虽然由于许可问题引起了一些争议
有关更多信息, 请参阅此 Hacker News 主题 https://news.ycombinator.com/item?id=17323911
记忆转储(Memory Dumps)
我们要看的最后一个区域是 "内存转储", 可以从实时系统中捕获并离线分析..NET 运行时一直很好地支持在 Windows 上创建 "内存转储", 现在. NET Core 是 "跨平台", 也可以在其他操作系统上使用相同的工具.
"内存转储" 的一个问题是, 获取 SOS 和 DAC 文件的正确匹配版本可能会非常棘手. 幸运的是, Microsoft 刚刚发布了以下 dotnet symbolCLI 工具:
可以下载任何给定核心转储, minidump 或任何支持平台的文件格式 (如 ELF,MachO,Windows DLL,PDB 和便携式 PDB) 的调试所需的所有文件(给出 coreclr 模块的符号, 模块, SOS 和 DAC).
最后, 如果你花费任何时间分析'内存转储', 你真的应该看看微软几年前发布的优秀的 CLR MD 库 https://github.com/Microsoft/clrmd . 我之前已经写过你可以用它做什么, 但简而言之, 它允许你通过一个直观的 C#API 与内存转储交互, 其中的类可以访问, GC Roots,CLR Threads,Stack Frames 和更多. 实际上, 除了实现工作所需的时间之外, CLR MD 还可以实现大多数(如果不是全部)SOS 命令 https://github.com/Microsoft/clrmd/issues/33 .
但是从宣布帖子来看它是如何运作的:
ClrMD 托管库是 CLR 仅内部调试 API 的包装器. 虽然这些仅内部 API 对于诊断非常有用, 但我们不支持它们作为公开的, 有文档的版本, 因为它们非常难以使用并且与 CLR 的其他实现细节紧密耦合. ClrMD 通过围绕这些低级调试 API 提供易于使用的托管包装来解决此问题.
通过在官方支持的库中提供这些 API,Microsoft 使开发人员能够在 CLRMD 之上构建各种工具, 这是一个很好的结果!
总而言之,.NET Runtime 提供了广泛的诊断, 调试和分析功能, 可以深入了解 CLR 内部的情况.
来源: https://www.cnblogs.com/WithLin/p/9798485.html