1

I am building a .Net Profiler for some custom requirement.

I want to hook at Enter and Leave for some specific methods. To achieve this, I have tried below two approaches.

  1. IL Rewrite - I am able to inject the custom code at both the places. It is successfully getting injected and calling the custom code. I am also able to get the input arguments, 'this' and return value. Injecting at the Enter is not much difficult. However, it's complex to inject at Leave as there could be return at multiple places in a method. I have to inject the code at every place where return statement is written.

    It's bit complex but somewhat doable. But, if there any exception, the execution is not reaching to return statement and hence my injected code is not getting invoked.

  2. Subscribe to Enter/Leave through SetEnterLeaveFunctionHooks2 in ICorProfilerInfo2 as per the sample code given here.

In both the cases, hook at the Leave is not getting invoked in case of exception in the method.

How to handle this? I want a return value in all the scenarios. In case of an exception, I should know there is an exception; I will consider as 'No return value'. Probably, I may need exception details as well.

Below is a sample method. I want to hook at Enter and Leave for GetString method. It has multiple returns. I am able to capture the return value in a normal scenario. But in case of exception, execution stops immediately and due to that the hook at return is not getting invoked.

    public int GetInt()
    {
        //int retVal = 10;
        int retVal = 1010;
        //throw new Exception("test");
        return retVal;
    }

    public string GetString()
    {
        var retunValue = "Return string ";

        if (GetInt() > 100)
        {
            retunValue += " inside IF > 100";
            return retunValue;
        }

        return retunValue + " at last return";
    }
Community
  • 1
  • 1
Hitesh
  • 1,067
  • 1
  • 10
  • 27
  • Hard to make sense of this. If you are going to use the profiler api to rewrite IL then you'd better do that *before* the code starts executing. In other words, you need the JITCompilationStarted callback. If you do it with SetEnterLeaveFunctionHooks2 then the die is cast, the IL already got jitted and changing it cannot have any affect. With the otherwise obvious advantage that when you do it with JITCompilationStarted then you don't have to worry about exceptions anymore. – Hans Passant Sep 23 '17 at 05:53
  • @HansPassant, for IL Rewrite, I do at ModuleLoadFinished callback, everything works fine, my code gets injected and getting invoked in the normal execution flow. But, it does not get invoked (due to obvious reasons) if I uncomment throw exception statement in GetValue method. GetValue is just sample code illustration purpose, in actual scenario exception get come anytime. – Hitesh Sep 24 '17 at 15:41

1 Answers1

2

To get the exception notification when using IL re-writing, you need to inject a try-finally or try-catch-throw. Since the ret instruction is not valid within a try block, you will need to replace them with a leave instruction that branches to an instruction after the inserted exception handler and return from there.

Another option is to include COR_PRF_MONITOR_EXCEPTIONS in your call to SetEventMask and listen on the ExceptionUnwindFunctionEnter and ExceptionUnwindFunctionLeave callbacks. These callbacks don't include the thrown exception however. You could track the exception from ExceptionThrown, but this may be misleading when an exception leaves a filter block as the runtime will treat it as returning false and continue with the previous exception, but IIRC, there is no callback to indicate when this happens.

Brian Reichle
  • 2,798
  • 2
  • 30
  • 34
  • Thanks Brian for the response. Can you please provide some more help on how to inject try/catch/finally block? – Hitesh Sep 24 '17 at 16:32
  • For another approach, I could get handle the ExceptionUnwindFunctionEnter/Leave method. However, as you mentioned, it does not provide exception details. Could you please provide more info how do I get the exception? – Hitesh Sep 24 '17 at 16:34
  • 1
    The exception handlers are specified in a data section of the function body passed to SetILFunctionBody. The details of the format can be found in [EMCA-335](https://www.ecma-international.org/publications/standards/Ecma-335.htm) Partition II Section 25.4 – Brian Reichle Sep 24 '17 at 21:56
  • 1
    As stated in the answer, you can get the exception from the ExceptionThrown callback, however, the ObjectID is a pointer to the object and so will change when the GC moves the object so you will need to either extract all the details you need during the ExceptionThrown callback or keep a reference to the ObjectID, request the GC callbacks and update your references when moved. In any case, you will need to track the exceptions per-thread and carefully consider what happens when an exception is thrown while handling another exception. – Brian Reichle Sep 24 '17 at 22:07
  • in 2nd approach, how to call .Net method in the same AppDomain in the instrumented executing process from C++ with input arguments along with 'this' object and return value? – Hitesh Sep 28 '17 at 16:41
  • You cant. No profiler callback is permitted to re-enter managed code under any circumstance, It's dangerous, and may result in deadlock. If you just want to access a field (or can get away with just accessing a field), then you use [GetClassLayout](https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/icorprofilerinfo2-getclasslayout-method) and related methods to find the value yourself. If you really do need to execute a method, then you will need to use IL injection instead. – Brian Reichle Sep 28 '17 at 21:45