Search code examples
stack-overflowpostsharp

PostSharp stackoverflow on aspect


Aspect class looks like this:

  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.Threading.Tasks;
  using System.Diagnostics;
  using PostSharp.Aspects;

  namespace GlobalExceptionHandler
  {

[Serializable]
class MyDebugger : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        Console.WriteLine("METHOD ENTRY: " + args.Method.Name + "(" + args.Arguments.GetArgument(0) + ")");
    }
    public override void OnException(MethodExecutionArgs args)
    {
        Console.WriteLine("Exception at: " + args.Method.Name + "()");
        args.FlowBehavior = FlowBehavior.Continue;
    }
}

}

I am applying the aspect to mscorlib assembly to system namespace but excluding the console class which i thought was causing the stackoverflow on my aspect as it uses a Console.WriteLine to print the log.

[assembly: GlobalExceptionHandler.MyDebugger(AttributeTargetAssemblies = "mscorlib", AttributeTargetTypes = "System.Console", AttributeExclude = true, AttributePriority = 100000)]

[assembly: GlobalExceptionHandler.MyDebugger(AttributeTargetAssemblies = "mscorlib", AttributeTargetTypes = "System.*")]

And im still getting the stackoverflow exception


Solution

  • The expression in the aspect code where you add several strings using "+" is actually emitted as a call to String.Concat method by C# compiler. So you get this code in the OnEntry:

    Console.WriteLine(String.Concat("METHOD ENTRY: ", args.Method.Name, "(", args.Arguments.GetArgument(0), ")"));
    

    To avoid recursion you can exclude System.String class in the same way you did with System.Console. However, in general case it's better to add a thread-static flag to your aspect that will serve to stop recursive calls.

    [Serializable]
    class MyDebugger : OnMethodBoundaryAspect
    {
        [ThreadStatic]
        private static bool isLogging;
    
        public override void OnEntry( MethodExecutionArgs args )
        {
            if ( isLogging ) return;
    
            isLogging = true;
            Console.WriteLine( "METHOD ENTRY: " + args.Method.Name + "(" + args.Arguments.GetArgument( 0 ) + ")" );
            isLogging = false;
        }
    
        public override void OnException( MethodExecutionArgs args )
        {
            if ( isLogging ) return;
    
            isLogging = true;
            Console.WriteLine( "Exception at: " + args.Method.Name + "()" );
            args.FlowBehavior = FlowBehavior.Continue;
            isLogging = false;
        }
    }