Image Image Image Image Image
Scroll to Top

To Top

AOP Aspect-oriented programming (AOP) is a pragmatic passion that I document here.

19

Jan
2011

inAOP

vonJohannes Hoppe

FindStackOverlowAttribute vs. StackOverlowException

On 19, Jan 2011 | inAOP | vonJohannes Hoppe

StackOverlowExceptions caused by recursive calls are evil bugs.

While developing on your local machine you have a high chance to jump right into the buggy line of code by attaching Visual Studio to the process. I’m talking about ASP.NET websites, so in my case it’s the w3wp.exe process.

But as soon as your code goes live your debugging possibilities are limited. Of course, you can do remote debugging with Msvsmon.exe, but you still need to know where to look at.* It’s a fact: Software-Bugs successfully hide themselves from developers. So as long as you hunt them, they won’t appear. Instead they like it to scare principals or end customers. For obvious reasons these peoples won’t provide you with a Strack Trace that could help you.

* And some StackOverlowExceptions refuse any friendly service, for example if your damn old IIS6 crashes, too.

Logging

The classic answer for that problem is logging. One common framework is NLog. The usage is straight forward:

Logger Logger = LogManager.GetLogger("FindStackOverlow");
Logger.Trace("Still alive!");

The corresponding NLog.config could look like this:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
 
  <targets>
    <target name="TraceFindStackOverlow" xsi:type="File" fileName="${basedir}/${logger}.log" layout="${date} | ${message} | ${stacktrace}" />
  </targets>
 
  <rules>
    <logger name="FindStackOverlow" minlevel="Trace" maxlevel="Trace" writeTo="TraceFindStackOverlow" />
  </rules>
</nlog>

You will get a nicely formatted logfile with the message „Still alive!“.

AOP

But if you want to trace the complete application, you are getting troubles. You would have to insert the “Logger.Trace” command in every method. This is not only impracticable; inserting the same line of code very often totally violates the DRY principle. (Don’t Repeat Yourself)

The answer to that problem is called aspect oriented programming (AOP). In short: aspects are code fragments that are automatically added to a method. On Sharpcrafters.com is a great introduction to the tracing topic.

The solution

Back to the uncatchable StackOverlowException: Using the AOP approach and NLog we now have a simple but very effective way to spot the recursive loop:

namespace Helper.Logging
{
    using System;
    using System.Diagnostics;
    using System.Reflection;
 
    using NLog;
 
    using PostSharp.Aspects;
 
    [Serializable]
    public class FindStackOverlowAttribute : OnMethodBoundaryAspect
    {
        private const int CriticalFrameCount = 100;
 
        private static readonly Logger Logger = LogManager.GetLogger("FindStackOverlowAttribute");
        private string instanceName;
 
        /// <summary>
        /// Method executed at build time. Initializes the aspect instance. After the execution
        /// of <see cref="CompileTimeInitialize"/>, the aspect is serialized as a managed
        /// resource inside the transformed assembly, and deserialized at runtime.
        /// </summary>
        public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
        {
            this.instanceName = method.DeclaringType.FullName + "." + method.Name;
        }
 
        /// <summary>
        /// Method invoked before the execution of the method to which the current aspect is applied.
        /// </summary>
        [DebuggerStepThrough]
        public override void OnEntry(MethodExecutionArgs args)
        {
            StackTrace st = new StackTrace();
            int frameCount = st.FrameCount;
 
            if (frameCount > CriticalFrameCount)
            {
                Logger.Trace(String.Format("{0}x\t- {1}", frameCount, this.instanceName));
            }
 
            base.OnEntry(args);
        }
    }
}

The Nlog.config file could look like this:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
 
  <targets>
    <target name="TraceFindStackOverlowAttribute" xsi:type="File" fileName="${basedir}/${logger}.log" layout="${date} | ${message} | ${stacktrace}" />
  </targets>
 
  <rules>
    <logger name="FindStackOverlowAttribute" minlevel="Trace" maxlevel="Trace" writeTo="TraceFindStackOverlowAttribute" />
  </rules>
 
</nlog>

Finally we need to apply this aspect to every method in our application as well as all referenced projects. For that you just have insert the following command in all AssemblyInfo.cs files:

[assembly: FindStackOverlow(
  AttributeTargetElements = MulticastTargets.Method)]

Works like a charm. You can upload everything to the webserver and watch the logfile over a longer time. If everything works like expected, the aspect should be removed. Tracing (especially writing to the disk) wastes a lot of performance!

Happy debugging! :-)



(For search engines: “An unhandled Microsoft .NET Framework exception occurred in w3wp.exe.”)


Tags | ,