How to access Attribute data from within a static method call (for tracing)

claus.rosendal's Avatar


18 Dec, 2017 04:02 PM


We have implemented our own trace logic, based on an attribute that inherits from OnMethodBoundaryAspect.
Each instance of the trace attribute object contains a privat "_state" member, that is serialized at compile time, and contains important information that is needed during trace output of each method call. I will call this the "trace state" below.

In "OnEntry", "OnExit" and "OnException" it is easy to access the trace state and thereby use it while outputting trace information on method entry, exit and exceptions.

However, we would also like to be able to do "inline tracing" using a static method call. Something like this:

void SomeMethod()
    <some code here>
    Trace.Info("Some relevant trace info")
    <some more code>

Now the issue is, that within the "Trace.Info" method we would like to have access to the trace state of the trace attribute assigned to "SomeMethod". But there doesn't seem to be an easy way to achieve this?
We have tried two approaches so far:

Approach 1:
1) Within "OnEntry" push the trace state to a ThreadLocal stack
2) Within "OnExit" pop the trace state from the stack
In "Trace.Info" peek the topmost state on the TreadLocal stack.

Approach 2:
1) Within RunTimeInitialize add the trace state to a global static dictionary, indexed by the full method signature.
2) Within "Trace.Info", use "(new StackFrame(1, false)).GetMethod().MemberSignature()" to get the member signature of the caller method, and look up the trace state in the dictionary.

Both approaches seem to work fine.... but only as long as async methods aren't used. The inline tracing within async methods does not work.
Approach 1 does not work, as SomeMethod could resume in a different thread after an await -- we have tried AsyncLocal instead of ThreadLocal, but this didn't seem to work as expected either.
Approach 2 does not work, as "SomeMethod" is not found as the caller on the stack, when it is async. Instead something like “<SomeMethod>d__1.MoveNext" will be found as the caller. We have tried to retrieve the right signature by extracting the method name between "<" and ">", but this did not seem reliable, and specifically does not work, when obfuscation with method renaming is applied.

So I am curious, whether there is some other, more simple approach, to get access to the trace state from within "Trace.Info" -- or you have some other suggestion for effective inline tracing?

Claus Rosendal

  1. Support Staff 1 Posted by PostSharp Techn... on 18 Dec, 2017 05:21 PM

    PostSharp Technologies's Avatar


    the is currently no built-in way to pass information from the aspect to the code in the transformed method, so the approach with context stacks is the most viable option.

    AsyncLocal<T> should be usable as a solution, just remember that the T is supposed to be immutable, otherwise you'll get into problems (the value is shared between multiple threads). AsyncLocal<ImmutableStack<T>> should work fine. Additionally you need to properly handle both async and non-async methods. You may need to combine thread static and AsyncLocal together for better performance.

    Please let us know if you have any further questions or issues.

    Best regards,

    Best regards,

  2. 2 Posted by claus.rosendal on 19 Dec, 2017 09:42 AM

    claus.rosendal's Avatar

    Thanks for the answer.

    I am a bit concerned about the performance overhead, if we need to push and pop from a stack on every method call. Especially if it is an ImmutableStack, which will only make performance worse. But I will try it out and evaluate the impact.

    In our case, more or less all methods will be decorated with the trace attribute, whereas it is more rare, that inline tracing occurs. Therefore something similar to "Approach 2" seems more favorable in our case, as it will have performance impact less often.

    I was wondering, whether something like this would be possible using PostSharp:
    1) At compile time, create a unique GUID as part of each trace state. This will be serialized into IL.
    2) Extend the Trace.Info method to have an additional "Guid id" parameter, with a default value of an empty guid.
    3) At compile time, for each method decorated with the trace attribute, analyze the body of such method and locate all calls to "Trace.Info" within the body. Modofy the IL of such calls, so that the "Trace.Info" call will be executed at runtime with the "Guid id" parameter set to the GUID generated for the related trace attribute.
    4) At runtime, within RunTimeInitialize insert each trace state into a dictionary, indexed by the GUID of the trace state.

    This would then make it easy within Trace.Info to locate the right trace state based on the GUID id transferred to it as a parameter.
    But it boils down to whether it is possible at compile time to inject the actual value of a parameter to a method call within the decorated method.
    Is this possible with PostSharp, or some other technique?

    Claus Rosendal

  3. Support Staff 3 Posted by PostSharp Techn... on 19 Dec, 2017 02:59 PM

    PostSharp Technologies's Avatar


    I would not be that concerned with performance overhead of ImmutableStack<T> itself as Push is a single allocation and Pop is trivial. Allocations are already made by each async call in form of Task object. I do not remember exactly, but I think that AsyncLocal would be the biggest problem and thus, using OnYield and OnResume advices and caching the value in ThreadLocal may improve performance.

    You would need to have some kind of generic IL rewriter in order to achieve this (e.g. Mono Cecil) or a Roslyn extension. PostSharp does not have public APIs that will help you achieve this outside of what Aspect Framework is capable of doing.

    I'm just thinking if it would not be better for you to use PostSharp Diagnostics possible with a custom backend? Or do you need to process the information in some advanced way?

    Best regards,

  4. claus.rosendal closed this discussion on 21 Dec, 2017 12:03 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts


? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac