What's in a PDB file? Use the Debug Interface Access SDK

It’s easy to use C# code and MSDia140.dll from the Debug Interface Access SDK to examine what’s inside a PDB.

A PDB is Program Database which is generated when an executable such as an EXE or DLL is built. It includes a lot of information about the file that is very useful for a debugger. This include names and addresses of symbols.

Managed code PDB contents are somewhat different from native code: a lot of the managed code information can be obtained from other sources. For example, the Type of a symbol can be obtained from the Metadata of the binary.

Below is some sample code that uses the DIA SDK to read a PDB and display its contents.

See also

Write your own Linq query viewer

Use DataTemplates and WPF in code to create a general purpose LINQ Query results display

<code>

 using Dia2Lib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
// File->New->Project->C# Windows WPF Application.
// Replace MainWindow.Xaml.cs with this content
// add a reference to c:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Packages\Debugger\msdia140.dll
namespace WpfApplication1
{
  public partial class MainWindow : Window
  {
    class SymbolInfo
    {
      public int Level { get; set; } //recursion level
      public string SymbolName { get; set; }
      public uint LocationType { get; set; }
      public ulong Length { get; set; }
      public uint AddressOffset { get; set; }
      public uint RelativeAddress { get; set; }
      public string SourceFileName { get; set; }
      public uint SourceLineNo { get; set; }
      public SymTagEnum SymTag { get; set; }
      public string SymbolType { get; set; }
      public override string ToString()
      {
        return $"{SymbolName} {SourceFileName}({SourceLineNo})  {SymbolType}";
      }
    }
    public MainWindow()
    {
      InitializeComponent();
      this.Loaded += (ol, el) =>
        {
          try
          {
            this.WindowState = WindowState.Maximized;
            var pdbName = System.IO.Path.ChangeExtension(
                      Assembly.GetExecutingAssembly().Location, "pdb");
            this.Title = pdbName;
            var lstSymInfo = new List<SymbolInfo>();
            using (var diaUtil = new DiaUtil(pdbName))
            {
              Action<IDiaEnumSymbols, int> lamEnum = null; // recursive lambda
                    lamEnum = (enumSym, lvl) =>
                    {
                      if (enumSym != null)
                      {
                        foreach (IDiaSymbol sym in enumSym)
                        {
                          var symbolInfo = new SymbolInfo()
                          {
                            Level = lvl,
                            SymbolName = sym.name,
                            Length = sym.length,
                            LocationType = sym.locationType,
                            SymTag = (SymTagEnum)sym.symTag,
                            AddressOffset = sym.addressOffset,
                            RelativeAddress = sym.relativeVirtualAddress
                          };
                          var symType = sym.type;
                          if (symType != null)
                          {
                            var symtypename = symType.name;
                            symbolInfo.SymbolType = symtypename;
                          }
                          lstSymInfo.Add(symbolInfo);
                          if (sym.addressOffset > 0 && sym.addressSection > 0 && sym.length > 0)
                          {
                            try
                            {
                              IDiaEnumLineNumbers enumLineNums;
                              diaUtil._IDiaSession.findLinesByAddr(
                                        sym.addressSection,
                                        sym.addressOffset,
                                        (uint)sym.length,
                                        out enumLineNums
                                        );
                              if (enumLineNums != null)
                              {
                                foreach (IDiaLineNumber line in enumLineNums)
                                {
                                  var linenumber = line.lineNumber;
                                  symbolInfo.SourceFileName = line.sourceFile.fileName;
                                  symbolInfo.SourceLineNo = line.lineNumber;
                                  break;
                                }
                              }
                            }
                            catch (Exception)
                            {
                            }
                          }
                          switch (symbolInfo.SymTag)
                          {
                            case SymTagEnum.SymTagFunction:
                            case SymTagEnum.SymTagBlock:
                            case SymTagEnum.SymTagCompiland:
                              IDiaEnumSymbols enumChildren;
                              sym.findChildren(SymTagEnum.SymTagNull, name: null, compareFlags: 0, ppResult: out enumChildren);
                              lamEnum.Invoke(enumChildren, lvl + 1);
                              break;
                          }
                        }
                      }
                    };
                    /* query by table of symbols
                    IDiaEnumTables enumTables;
                    diaUtil._IDiaSession.getEnumTables(out enumTables);
                    foreach (IDiaTable tabl in enumTables)
                    {
                        var tblName = tabl.name;
                        if (tblName == "Symbols")
                        {
                            IDiaEnumSymbols enumSyms = tabl as IDiaEnumSymbols;
                            lamEnum.Invoke(enumSyms, 0);
                        }
                    }
                    /*/ // query by global scope
                    var globalScope = diaUtil._IDiaSession.globalScope;
              IDiaEnumSymbols enumSymGlobal;
              globalScope.findChildrenEx(SymTagEnum.SymTagNull, name: null, compareFlags: 0, ppResult: out enumSymGlobal);
              lamEnum.Invoke(enumSymGlobal, 0);
                    //*/
                  }
            var gridvw = new GridView();
            foreach (var mem in typeof(SymbolInfo).GetMembers().
                        Where(m => m.MemberType == MemberTypes.Property)
                  )
            {
              var gridCol = new GridViewColumn();
              gridvw.Columns.Add(gridCol);
              gridCol.Header = new GridViewColumnHeader()
              {
                Content = mem.Name
              };
              var template = new DataTemplate(typeof(SymbolInfo));
              var factTblk = new FrameworkElementFactory(typeof(TextBlock));
              factTblk.SetBinding(TextBlock.TextProperty, new Binding(mem.Name));
                    // for wide columns let's set the tooltip too
                    factTblk.SetBinding(TextBlock.ToolTipProperty, new Binding(mem.Name));
              factTblk.SetValue(TextBlock.MaxWidthProperty, 300.0);
              var factSP = new FrameworkElementFactory(typeof(StackPanel));
              factSP.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
              factSP.AppendChild(factTblk);
              template.VisualTree = factSP;
              gridCol.CellTemplate = template;
            }

            var lv = new ListView()
            {
              ItemsSource = lstSymInfo,
              View = gridvw
            };
            lv.DataContext = lstSymInfo;
            this.Content = lv;

          }
          catch (Exception ex)
          {
            this.Content = ex.ToString();
          }
        };
    }
  }
  public class DiaUtil : IDisposable
  {
    public IDiaDataSource _IDiaDataSource;
    public IDiaSession _IDiaSession;
    public DiaUtil(string pdbName)
    {
      _IDiaDataSource = new DiaSource();
      _IDiaDataSource.loadDataFromPdb(pdbName);
      _IDiaDataSource.openSession(out _IDiaSession);
    }

    public void Dispose()
    {
      Marshal.ReleaseComObject(_IDiaSession);
      Marshal.ReleaseComObject(_IDiaDataSource);
    }
  }
}

</code>

Comments

  • Anonymous
    November 14, 2016
    Hi Calvin Hsia,"add a reference to c:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Packages\Debugger\msdia140.dll" you mentioned above is something wrong on my machine. The msdia140.dll is built by vctoolsrel, and it can not be referenced directly through add reference dialogue on wpf appliction. Please show more detail, thanks!