Monday, July 28, 2008

Deploying PDBs to get the exact line number of an exception

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/deploying_pdbs_to_get_the_exact_line_number_of_an_exception.htm]

(Also check out Getting file and line numbers without deploying the PDB files).

 

Ideally an application in production will log all its unhandled exceptions. It would also be ideal for those error logs to have as much information as possible to help reproduce the bug. One piece of info that is very handy is knowing what line number of source code ultimately triggered the exception. For example, if you have a 50-line method that throws a null exception, you'll want to know what exact line was the culprit. If you just have the raw assemblies (only the DLL or EXE), that alone doesn't tell you the source-code line number, and for good reason - you lose all that line-number info when compiling your friendly source code (complete with comments and white space) into an assembly. That compiled assembly is essentially just a collection of bytes; it is not plain text or human-friendly.

 

That's why the PDB  ("program database") are so great. The PDB file maps the original source code to the compiled assembly. It is the PDB that helps you step through your actual code in the debugger. So, without your PDB file, the Exception's stack trace just shows method names. But with the PDB file, it also includes "debugging" information like file name and line number.

 

For example, say you have the following trivial program, whose sole point is to throw an exception.

using System;

namespace ExceptionDemo
{
  class Program
  {
    static void Main(string[] args)
    {
      try
      {
        Console.WriteLine("Started");
        DoStuff();
      }
      catch (Exception ex)
      {
        Console.WriteLine("Error: " + ex);
      }
      Console.WriteLine("Done");
    }

    public static void DoStuff()
    {
      throw new ApplicationException("test1");
    }
  }
}

If you compile this in release mode, it creates the exe and pdb. If you run this with the pdb existing, it will output:

Started

Error: System.ApplicationException: test1

    at ExceptionDemo.Program.DoStuff() in C:\Temp\Program.cs:line 26

    at ExceptionDemo.Program.Main(String[] args) in C:\Temp\Program.cs:line 15

Done

If you then delete the pdb, it outputs:

Started

Error: System.ApplicationException: test1

    at ExceptionDemo.Program.DoStuff()

    at ExceptionDemo.Program.Main(String[] args)

Done

Moral of the story - including your PDB files with the released code lets your exceptions automatically pick up the extra helpful info like line numbers and file names. Of course, there's always a catch - PDB files may expose your intellectual property, so you probably don't want to ship them. So, the ideal situation would be to keep your PDB files for the release builds, don't ship them, but have a way to look up the line and file info whenever the application logs an error. Can we do this? Yes, using several other techniques that we'll discuss tomorrow.

 

Note - including the PDB file is NOT the same as shipping the debug mode. Debug mode fundamentally can compile different code, like using the #IF DEBUG declaratives. Merely adding the PDB file does not suddenly turn the release build into the debug build.

 

No comments:

Post a Comment