36

I've got sort of a unique situation. I've been working on an open source library for sending email. In this library, I need a reliable way to get the calling method. I've done this with a StackTrace by analyzing the StackFrame objects inside it. This works without issue in a debug-mode project where optimizations are turned off.

The problem occurs when I switch to release mode where optimizations are turned on. The stack trace looks like this:

> 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
...

This is taken from a failing unit test. In line 3 of this trace, I should see a method called TestEmail which is defined elsewhere, but I believe the JITter is inlining it. I've read that you can prevent inlining by making a method virtual, but this doesn't work. Does anyone know of a reliable method for preventing method inlining so your method will show up in a stack trace?

Scott Arrington
  • 12,325
  • 3
  • 42
  • 54

2 Answers2

44

You could use MethodImplAttribute and specify MethodImplOptions.NoInlining.

[MethodImpl(MethodImplOptions.NoInlining)]
void YourMethod()
{
    // do something
}

Note that this still doesn't guarantee that you can get at the actual calling method as seen in the source code. Your method won't be inlined, but your method's caller could be inlined into its own caller, etc etc.

LukeH
  • 263,068
  • 57
  • 365
  • 409
  • That works like a charm! Can this be placed on a class to prevent any method in that class from being inlined? – Scott Arrington Mar 02 '11 at 15:01
  • @Scott: It's only applicable to methods (and constructors) as far as I'm aware. – LukeH Mar 02 '11 at 15:07
  • This definitely works, but I guess I need to find a way do something similar at the class level. I'd hate to force my consumer to put this on every EmailResult method. If a consumer inherits from your base class, is it possible to be notified when they call a method in their implementation? – Scott Arrington Mar 02 '11 at 15:15
  • I had a need for this on a property, and I can confirm it can also be put above getters or setters of properties, as they are, in fact, compiled as methods. – Kharlos Dominguez Feb 08 '12 at 13:18
  • It's only applicable on methods because only methods can be inlined. Method based notification is more of an AOP or logging based solution. – JerKimball Jan 11 '13 at 15:33
7

You could use additional parameters marked with System.Runtime.CompilerServices.CallerMemberNameAttribute and it's siblings CallerFilePath and CallerLineNumber. If I understand it correctly this should get you the correct method name, no matter what's inlined an what's not. You will only get the method name however, I don't see anything to get the class name/assembly etc.

It should go without saying, but just to be sure... something like this should not be used outside of logging/diagnostics.

Proper ways to do this would probably be:

  • Pass the required information as a parameter
  • Temporarily store the required information in Thread.ExecutionContext while calling the function

I realize that this probably won't be of any help to Scott after all this time, but maybe someone else can benefit from it.

user2864740
  • 60,010
  • 15
  • 145
  • 220
Paul Groke
  • 6,259
  • 2
  • 31
  • 32