问题标签 [clr-profiling-api]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
.net - 代码注入不适用于 .Net 2.x 中的 MSCorLib
在我们的 .Net 应用程序中,我们想要跟踪通过 .Net 代码打开的文件。任何打开的文件,执行调用总是通过一个内部实例方法 FileStrem.Init(...) ,它在 mscorlib 中可用。
为了将我们的监控代码注入到任何 .Net 方法中,我们使用 Microsoft 提供的 ICorProfiler API 在 C++ 中构建了一个框架。它所做的只是在模块加载完成后,从加载的模块中从目标方法读取现有的 IL,使用 IL 构建自定义代码并将其注入目标方法。
这个框架运行良好。我们能够将监控代码注入到许多方法中,包括 .Net 4.x 的 mscorlib 中的 FileStream.Init(...) 方法。但是相同的注入代码不会在 .Net 2.x 中被调用。
我们能够将我们的代码注入到任何 .Net DLL 中,除了用于 .Net 2.x 的 mscorlib。代码被正确注入,但没有被调用。注入代码后,我已经验证了 FileStream.Init(...) 的 IL;我可以看到我的代码存在。但是,不知道为什么它没有被调用。然而,相同的代码适用于 .Net 4.x 的 FileStream.Init(...) 文件。
这是通过 IL 注入的示例 C++ 代码 -
知道为什么它在 .Net 2.x 的 mscorlib 中不起作用吗?
c++ - 代码注入不能与 FILE* 一起用于日志记录
在我们的项目中,我们通过使用 .Net CorProfiler API 重写 IL 来在不同位置注入代码。一切正常。现在,出于记录目的,我们已经开始使用 FILE* 打开文件,如下所示 -
通过引入上面的代码,我没有得到任何错误,但同时代码注入在某些情况下也不起作用。但是,它继续适用于其他一些情况。通过删除上面的代码,一切都开始按预期工作。
知道 FILE 与 IL 重写之间有什么关系吗?我如何让他们两个一起工作?
更新:任何建议,我该如何调试?
.net - 将程序集从自身重新加载到共享应用程序域
我有一个奇怪的要求,因为我正在使用 C++ 代码对 .NET 代码进行检测。
在特定情况下,我需要将我的 C# 程序集(一个 dll)从该 dll 本身重新加载到共享的 appdomain 中。有可能吗?我怎样才能做到这一点?
我这样做的原因是(简而言之),我的探查器将代码注入 GAC 中存在的 .NET Framework 模块(在共享的 appdomain - EE 共享程序集存储库中)。执行期间注入的代码尝试加载 GAC 中不存在的我的 .NET dll(位于 Web 应用程序的 bin 中) - 它加载到 AppDomain“/LM/W3SVC/1519733499....”中。
我需要将我的 .NET dll 加载到共享应用程序域中,以便 GAC 模块在运行时加载我的 dll。但是我需要从那个dll本身(当它被它自己的AppDomain的模块加载时)这样做(如果可能的话)。
希望我的描述有助于理解我的问题,这与 .NET 检测和核心 CLR 更相关。
.net - 没有为泛型类调用 ICorProfilerCallback::ClassUnloadStarted,即使该类已卸载
我目前正在调试我公司的 CLR 分析器(通过 ASP.NET 4.7.3282.0、.NET 框架 4.7.2),并看到 CLR 卸载泛型类但未调用ClassUnloadStarted回调的场景。
简而言之,我们的分析器根据 ClassID 跟踪加载的类,遵循ClassLoadStarted、ClassLoadFinished和ClassUnloadStarted回调。在某些时候,类会被卸载(连同其相关模块),但不会为相关 ClassID 调用ClassUnloadStarted回调。因此,我们留下了一个停顿的 ClassID,认为该类仍在加载。稍后,当我们尝试查询该 ClassID 时,CLR 不出所料地崩溃了(因为它现在指向垃圾内存)。
我的问题,考虑到下面的详细情况:
- 为什么我的(通用)类没有调用 ClassUnloadStarted?
- 这是 CLR 的预期边缘情况行为,还是可能是 CLR/Profiling API 错误?
我找不到任何关于此行为的文档或推理,特别是ClassUnloadStarted没有被调用。我在 CoreCLR 代码中也找不到任何提示。提前感谢您的帮助!
详细场景:
这是有问题的课程(IComparable(T)
with T=ClassFromModuleFoo
):
在应用程序运行时,问题在某些模块被卸载后出现。
这是基于添加的调试打印的确切加载/卸载回调流程:
- mscorlib的类
System/IComparable'1(ClassFromModuleFoo)
已加载。 - 紧接着,
ClassFromModuleFoo
模块 Foo 的类被加载到程序集#1 中。 - 模块 Foo 完成加载到程序集 #1。
- 然后,模块 Foo 再次加载到不同的程序集#2 中。
IComparable
和再次加载,这次ClassFromModuleFoo
是在程序集 #2 中。现在每个类有两个实例:一个在 Foo 中加载到程序集 #1 中,另一个在 Foo 中加载在程序集 #2 中。- 模块 Foo 开始从程序集 #1 中卸载。
ClassUnloadStarted
ClassFromModuleFoo
在程序集 #1 中调用回调。- 模块 Foo 已完成从程序集 #1 中卸载。
ClassUnloadStarted
以后的任何时候都不会调用System/IComparable'1(ClassFromModuleFoo)
程序集#1(即使它的模块已卸载并且它的 ClassID 指向现在被颠簸的内存)。
一些附加信息:
- 最新的 .NET 框架版本 4.8 预览版也重现了该问题。
- 我通过添加
COR_PRF_DISABLE_ALL_NGEN_IMAGES
到探查器事件掩码来禁用本机图像,认为它可能会影响 ClassLoad* 回调,但它没有任何区别。我验证mscorlib.dll
了确实加载而不是其本机图像。
编辑:
感谢我非常聪明的同事,我能够通过一个小示例项目重现该问题,该项目通过加载和卸载 AppDomain 来模拟这种情况。这里是:
https ://github.com/shaharv/dotnet/tree/master/testers/module-load-unload
测试中的此类发生崩溃,该类已卸载,并且 CLR 未调用卸载回调:
这是相关代码,加载和卸载了几次:
在某些时候,探查器在尝试查询该类的 ClassID 时崩溃 - 认为它仍然有效,因为未调用卸载回调。
在旁注中,我尝试将此示例移植到 .NET Core 以进行进一步调查,但无法弄清楚如何,因为 .NET Core 不支持辅助 AppDomain(而且我不太确定它是否支持按需组装一般卸货)。
.net - clr profiler api: ICorProfilerCallback::ExceptionThrown GC 是否安全
我想知道在 .net 框架分析器中,ExceptionThrown ( https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/icorprofilercallback-exceptionthrown-method ) 是否保证不会与 GC 重叠?
查看文档,它看起来是这样 - “如果探查器在此处阻塞并尝试进行垃圾收集,则运行时将阻塞,直到此回调返回”
但是,我运行了附加到paint.net(https://www.getpaint.net/ - 版本4.1.6)的分析器,并看到了一个在ExceptionThrown期间我得到GC的特定情况。(但这是非常罕见的 - 仅在启动时发生,大约每 20 次运行中仅发生一次) - 这导致数据在我阅读时发生变化,因为 gc 移动了它。
至少在 .net 核心版本中 - https://github.com/dotnet/coreclr/blob/master/Documentation/botr/profiling.md它明确说明了哪些回调是 GC 调出安全的。ExceptionThrown 不是其中之一。例如,ExceptionUnwindFunctionEnter 是。但是,回到 .NET 框架 - https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/icorprofilercallback-exceptionunwindfunctionenter-method - .NET 框架文档中关于 GC 的注释与此相同异常抛出。
我知道 .NET 核心!= .NET 框架。但是,我觉得他们的分析器的代码非常相似并且具有相同的保证。我找不到关于 .NET 框架回调的 GC 安全性的类似资源。
所以在所有这些介绍之后,我的问题是:
.NET 框架 clr 探查器 ExceptionThrown 回调是否应该是 GC 安全的。如果是这样,我怎么会在 ExceptionThrown 期间看到 GC 调用开始和结束(可能是 clr 中的错误或预期的行为)?
如果没有错误,我是否可以至少 100% 依赖 .NET 框架 clr 探查器 UnwindExceptionFunctionEnter 回调是 GC 安全的,基于 core-clr 的类似文档?
谢谢
.net - ICorProfilerCallback2:无法区分已处理和未处理的异常与异常事件
我正在使用ICorProfilerCallback2接口来实现一个分析器来监视任何 .net 应用程序中发生的所有未处理/未捕获的异常。ExceptionThrown 事件帮助我找到程序中发生的所有异常(捕获/未捕获异常)。但我只需要捕获未处理的异常。
winapi - 使用 IMetaDataEmit::Save(/ToMemory/ToStream) 创建 PE 文件
我正在编写一个本地 CLR Profiler,它会进行一些繁重的 IL 重写。在开发新功能时,我们有时会遇到 CLR 验证错误。对于小型方法,比较前后的字节非常容易,查看各种元素(方法头、签名、局部变量、代码和异常表,主要是)并找到错误。有时,这可能是巨大的方法,这个过程可能需要一段时间。我正在尝试将当前模块转储到文件中,以便轻松运行 peverify.exe (和https://github.com/dotnet/corert/tree/master/src/ILVerify)。我发现了 IMetaDataEmit::Save,它在纸上看起来很完美(我们一直在使用 IMetaDataEmit 来执行 IL 重写)。我可以转储我的模块,在十六进制查看器中打开它并查看我所做的更改。但是,它只转储模块(PE 内的 .Net 目录)。如何从此模块创建完整的 PE (dll/exe),最好以编程方式?
.net - .NET 分析 API 初始化
我正在尝试学习如何使用分析 API 编写一个简单的 .net 分析器。
第一步,我只想能够加载分析器 dll 并从 ICorProfilerCallback::Initialize 写入日志文件。
在一个测试 .net 控制台应用程序中,我设置了以下环境变量:
COR_ENABLE_PROFILING="1"
COR_PROFILER="ProfilerTest"
--> 这就是我认为的问题所在。我不知道如何找到 GUID,而 ProfilerTest 是我的 dll 的名称。这里说从 .NET Framework 4 开始,不必注册分析器。这是否意味着不需要设置这个环境变量?
在微软的 CLRProfiler 源代码中,他们还设置了COR_PROFILER_PATH
.
这里要完整的是来自 dll 的初始化函数:
c# - 理解结构的“this”参数(特别是迭代器/异步)
我目前正在使用 Profiler API 检查 CLR 中的深层对象。我在分析迭代器/异步方法的“this”参数时遇到了一个特定问题(由编译器生成,格式为<name>d__123::MoveNext
)。
在研究这个时,我发现确实有一种特殊的行为。首先,C# 编译器将这些生成的方法编译为结构(仅在发布模式下)。ECMA-334(C# 语言规范,第 5 版:https ://www.ecma-international.org/publications/files/ECMA-ST/ECMA-334.pdf)状态(12.7.8 此访问):
“...如果方法或访问器是迭代器或异步函数,则 this 变量表示为其调用方法或访问器的结构的副本,...。”
这意味着与其他“this”参数不同,在这种情况下“this”是按值发送的,而不是通过引用发送的。我确实看到副本没有在外面修改。我试图了解结构实际上是如何发送的。
我冒昧地剥离了复杂的案例,并用一个小结构复制了它。看下面的代码:
NoInlining
只是为了我们可以正确检查 JITted 代码。我正在研究三个不同的东西:mainFoo 如何调用 foo/foo1、foo 是如何编译的以及 foo1 是如何编译的。以下是生成的 IL 代码(使用 ildasm):
生成的汇编代码(仅相关部分):
我们可以看到的第一件事是 foo 和 foo1 都生成相同的 IL 代码(以及相同的 JIT 汇编代码)。这是有道理的,因为最终我们只是使用第一个参数。我们看到的第二件事是 mainFoo 以不同的方式调用这两种方法(ldloc 与 ldloca)。由于 foo 和 foo1 都期望相同的输入,我希望 mainFoo 将发送相同的参数。这提出了3个问题
1)在堆栈上加载结构与在该堆栈上加载结构的地址到底是什么意思?我的意思是,大小大于 8 字节(64 位)的结构不能“坐在”堆栈上。
2) CLR 是否在用作“this”之前生成结构的副本(我们知道这是真的,根据 C# 规范)?这个副本存储在哪里?fooMain 程序集显示调用方法在其堆栈上生成副本。
3) 似乎按值和地址加载结构(ldarg/ldloc vs ldarga/ldloca)实际上加载了一个地址——对于第二组它只是在之前创建了一个副本。为什么?我在这里错过了什么吗?
4) 回到迭代器/异步 - foo/foo1 示例是否复制了迭代器和非迭代器结构的“this”参数之间的差异?为什么需要这种行为?创建副本似乎是浪费工作。动机是什么?
(此示例是使用 .Net framework 4.5 进行的,但使用 .Net framework 2 和 CoreCLR 也可以看到相同的行为)
.net-core - 是否有任何分析 api 可用于以 clr 版本托管的应用程序,因为 IIS 中没有托管代码
我正在使用 clr profiling api 来跟踪所有加载的程序集,以及在 IIS 中运行的应用程序中的类。当我的应用程序池的 .net clr 版本设置为 v2.0 或 v 4.0 时,它工作得非常好。
但是当 clr 版本设置为“无托管代码”时,clr 分析 api 不起作用。
我不确定我错过了什么。如果 clr 分析 api 不支持“无托管代码”应用程序,那么是否有任何替代方法可用于分析那些非托管代码应用程序。