Exceptions

Introduction

This was originally intended to be a post on identifying and troubleshooting Exceptions using windbg. As I started writing I thought I should write a little something about exceptions in general, just to begin the post. That little something grew to be quite big and all of a sudden it was by all means worthy of a post of its own.

There are two scenarios that are exceptionally (no pun intended) common in my line of work:

  1. Clients are reporting 2nd chance exceptions displayed on screen with the classic "Server Error"- page.
  2. Performance is generally bad, and when we investigate it turns out that there are tons of exceptions being thrown every second.

What exactly is an exception?

When it comes to the .NET Framework an exception is a managed object that inherits from the Exception Class. It is usually created when something's gone wrong either in the application or the runtime. The exception contains detailed information about the error that created it such as a friendly error message, the stack trace leading to this exception being created, possible inner exceptions (exceptions that caused the exception) and more. In other words they contain a lot more information than a function simply returning a status code other than 0 if something went wrong. This makes it a lot easier for us to troubleshoot the application.

The downside is that this is resource-consuming. All the information available needs to be gathered and so throwing an exception is a lot more costly than returning an error code.

Exceptions must be handled

When an exception is thrown it must be handled one way or another or the application/web request will crash/fail. This is done by setting up a try/catch block.

   try {
     conn.Close();
   }
   catch(Exception ex) {
     // Something went wrong.
   }

If anything goes wrong within the try block, the catch block can now deal with it and act appropriately. It is still up to you to code a suitable reaction to the exception, but the interesting thing is that you need to deal with the problem. If the exception is unattended it will cause the application to crash (winforms) or return a "Server Error"-page (IIS).

This is really good, because this way you will know, one way or the other, that something has gone wrong. Even if you forget to add a try/catch block the exception will still be thrown. This will possibly cause the application to crash, but that's a much better option than continuing execution as if nothing happened.

Exceptions should be avoided

This is really important. To throw an exception is costly. Chris Brumme has written an excellent post on this subject and in it he writes about some of the things that the runtime does when throwing an exception. Such as:

  • Grab a stack trace by interpreting metadata emitted by the compiler to guide our stack unwind.
  • Run through a chain of handlers up the stack, calling each handler twice.
  • Compensate for mismatches between SEH, C++ and managed exceptions.
  • Allocate a managed Exception instance and run its constructor. Most likely, this involves looking up resources for the various error messages.
  • Probably take a trip through the OS kernel. Often take a hardware exception.
  • Notify any attached debuggers, profilers, vectored exception handlers and other interested parties.

Exceptions are there to tell you that something's gone wrong, but since they're quite costly to throw it would be much better if you anticipated them and tried to minimize them.

Why use the following code:

    void calculateAvg(Int32 X, Int32 Y)
    {
        try
        {
            Label1.Text = (X / Y).ToString();
        }
        catch (Exception e)
        {
            Label1.Text = "Division by Zero";
        }
    }

when you can just as well use this:

    void calculateAvg(Int32 X, Int32 Y)
    {
        if (Y != 0)
        {
            Label1.Text = (X / Y).ToString();
        }
        else
        {
            Label1.Text = "Division by Zero";
        }

    }

As you can see, the first piece of code will throw an exception if Y happens to be zero. And all to no use. Take a look again at all the things we go through when throwing an exception and consider if that is really necessary when all you needed to do was to verify that Y != 0.

How many exceptions is your application throwing per second?

In a perfect world you should be able to answer this without hesitation. Anyway, if you honestly have no idea then I suggest you start Performance Monitor, add a counter for .NET CLR Exceptions/# of Exceps Thrown and run a stress test.

I've come across applications that throw over 150.000 exceptions in less than an hour.

There may also be times when this is actually expected and acceptable. For example, the Response.Redirect command results in a System.Threading.ThreadAbortException. If your application relies heavily on Response.Redirect for some reason, then you needn't worry. If, however, you're not aware of what is causing these exceptions I'd urge you to look into it.

None of us would want to let our systems run with constant red or yellow warnings in the event logs. The least we'd do is check up on them and see what caused them.

Well, having written this little introduction I guess I should now get going on the "real" post. The one about how to find and troubleshoot those exceptions using Windbg...

Later! / Johan