防止JIT在方法上进行内联

37

我有一个比较独特的情况。我一直在开发一个用于发送电子邮件的开源库。在这个库中,我需要可靠地获取调用方法。我通过分析StackTrace中的StackFrame对象来实现了这一点。在关闭优化的调试模式项目中,这可以正常工作。

问题出现在我切换到开启优化的发布模式时。堆栈跟踪看起来像这样:

> FindActionName at offset 66 in file:line:column <filename unknown>:0:0
> Email at offset 296 in file:line:column <filename unknown>:0:0
> CallingEmailFromRealControllerShouldFindMailersActionName at offset 184
     in file:line:column <filename unknown>:0:0
> _InvokeMethodFast at offset 0 in file:line:column <filename unknown>:0:0
> InvokeMethodFast at offset 152 in file:line:column <filename unknown>:0:0
...

以下内容来自一个失败的单元测试。在这个跟踪记录的第3行,我应该看到一个叫做TestEmail的方法,它在其他地方有定义,但我相信JITter正在对其进行内联优化。我了解到可以通过将方法设置为虚方法来防止内联优化,但这种方法并不奏效。是否有人知道一种可靠的方法来防止方法内联优化,以便在堆栈跟踪中显示出您的方法?

2个回答

45

你可以使用MethodImplAttribute并指定MethodImplOptions.NoInlining

[MethodImpl(MethodImplOptions.NoInlining)]
void YourMethod()
{
    // do something
}
请注意,这仍然不能保证您可以获取源代码中实际调用的方法。您的方法不会被内联,但是您方法的调用者可能会被内联到其自己的调用者中等等。

非常好用!这个能放在一个类上,以防止该类中的任何方法被内联吗? - Scott Arrington
@Scott:据我所知,它仅适用于方法(和构造函数)。 - LukeH
这肯定有效,但我想我需要找到一种在类级别做类似事情的方法。我不想强迫我的消费者在每个EmailResult方法上都加上这个。如果一个消费者继承了你的基类,当他们调用自己实现中的方法时,是否有可能通知他们? - Scott Arrington
我在一个属性上有这个需求,我可以确认它也可以放在属性的getter或setter上,因为它们实际上被编译为方法。 - Kharlos Dominguez
它只适用于方法,因为只有方法可以内联。基于方法的通知更多是基于AOP或日志记录的解决方案。 - JerKimball

7

您可以使用带有System.Runtime.CompilerServices.CallerMemberNameAttribute及其姐妹属性CallerFilePathCallerLineNumber的附加参数。如果我理解正确,这应该能够获取正确的方法名称,无论什么被内联和什么没有被内联。但是,您只会得到方法名称,我没有看到获取类名/程序集等信息的任何内容。

不用说,但为了确保...这样的东西不应在日志记录/诊断之外使用。

正确的做法可能是:

我意识到这可能对Scott来说已经没有帮助了,但也许其他人可以从中受益。


注意:如果您可以依赖于.Net 4.5或更高版本,则这肯定是一个干净的解决方案。 - ebyrob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接