c# - 检查 pdb 文件是否与源匹配

标签 c# pdb debug-symbols

我想写检查来证明给定程序集附近有有效的 pdb 文件,并且存储在 pdb 中的校验和与源校验和匹配。

该检查过去在我们的 CI 服务器上被称为单元测试,以防止在构建服务器配置损坏时发布二进制文件(例如,不同的 git 分支用于构建代码)。

如果可能,它应该接受MethodInfo 作为输入参数。我想仔细检查并验证方法的源行是否是估计的。

第一部分,阅读具体方法的源码非常简单。这是 a sample .

困难的部分是检查 PDB 中所有文档的校验和。标准的 System.Diagnostics.SymbolStore.SymDocument.GetCheckSum() 抛出 NotImplementedException 所以我肯定需要使用其他东西。

有什么建议吗?

UPD. 试图澄清:)

  1. 我有兴趣检查源文件的校验和是否与存储在 PDB 文件中的校验和匹配,第一部分(检查 pdb 是否与二进制匹配)已经完成。

  2. 我正在为此任务寻找一些 API,因为我不想解析控制台输出。此外,我不希望在我们的 CI 服务器上安装任何额外的 SDK,因为这需要额外的时间来证明没有任何问题。

最佳答案

如果您需要一种编程方式来做到这一点,您可以使用 Microsoft.DiaSymReaderPackage (版本 1.0.7.0),您还需要声明几个其他非托管接口(interface)。可以使用以下两种算法之一从文件字节轻松计算出校验和:SHA1 或 MD5。

这是一个示例控制台应用程序,它显示有关其自身来源的信息:

  class Program
  {
      static void Main(string[] args)
      {
          ValidateChecksums(Path.GetFullPath("ConsoleApplication1.exe"));
      }

      // this needs a reference to Microsoft.DiaSymReader
      // or redefine its interfaces manually from here https://github.com/dotnet/roslyn/tree/master/src/Dependencies/Microsoft.DiaSymReader
      public static void ValidateChecksums(string filePath)
      {
          if (filePath == null)
              throw new ArgumentNullException(nameof(filePath));

          var dispenser = (IMetaDataDispenser)new CorMetaDataDispenser();
          var import = dispenser.OpenScope(filePath, 0, typeof(IMetaDataImport).GUID);

          var binder = (ISymUnmanagedBinder)new CorSymBinder_SxS();
          ISymUnmanagedReader reader;
          binder.GetReaderForFile(import, filePath, null, out reader);

          int count;
          reader.GetDocuments(0, out count, null);
          if (count > 0)
          {
              var docs = new ISymUnmanagedDocument[count];
              reader.GetDocuments(count, out count, docs);
              foreach (var d in docs)
              {
                  var doc = new SymDocument(d);
                  Console.WriteLine(doc.Url);

                  if (doc.Checksum.SequenceEqual(doc.ComputeChecksum()))
                  {
                      Console.WriteLine(" checksum is valid.");
                  }
                  else
                  {
                      Console.WriteLine(" checksum is not valid.");
                  }
              }
          }
      }
  }

以及示例帮助程序类、 native 接口(interface)和组件类。

  public class SymDocument
  {
      // guids are from corsym.h
      public static readonly Guid CorSym_SourceHash_MD5 = new Guid(0x406ea660, 0x64cf, 0x4c82, 0xb6, 0xf0, 0x42, 0xd4, 0x81, 0x72, 0xa7, 0x99);
      public static readonly Guid CorSym_SourceHash_SHA1 = new Guid(0xff1816ec, 0xaa5e, 0x4d10, 0x87, 0xf7, 0x6f, 0x49, 0x63, 0x83, 0x34, 0x60);

      public SymDocument(ISymUnmanagedDocument doc)
      {
          if (doc == null)
              throw new ArgumentNullException(nameof(doc));

          int len;
          doc.GetUrl(0, out len, null);
          if (len > 0)
          {
              var urlChars = new char[len];
              doc.GetUrl(len, out len, urlChars);
              Url = new string(urlChars, 0, len - 1);
          }

          doc.GetChecksum(0, out len, null);
          if (len > 0)
          {
              Checksum = new byte[len];
              doc.GetChecksum(len, out len, Checksum);
          }

          Guid id = Guid.Empty;
          doc.GetChecksumAlgorithmId(ref id);
          ChecksumAlgorithmId = id;
      }

      public string Url { get; private set; }
      public byte[] Checksum { get; private set; }
      public Guid ChecksumAlgorithmId { get; private set; }

      public byte[] ComputeChecksum()
      {
          HashAlgorithm algo;
          if (ChecksumAlgorithmId == CorSym_SourceHash_MD5)
          {
              algo = MD5.Create();
          }
          else if (ChecksumAlgorithmId == CorSym_SourceHash_SHA1)
          {
              algo = SHA1.Create();
          }
          else
              throw new NotSupportedException();

          try
          {
              return algo.ComputeHash(File.ReadAllBytes(Url));
          }
          finally
          {
              algo.Dispose();
          }
      }
  }

  [ComImport, Guid("0A29FF9E-7F9C-4437-8B11-F424491E3931")]
  internal class CorSymBinder_SxS // from corsym.h
  {
  }

  [ComImport, Guid("E5CB7A31-7512-11d2-89CE-0080C792E5D8")]
  internal class CorMetaDataDispenser // from cor.h
  {
  }

  [Guid("7DAC8207-D3AE-4c75-9B67-92801A497D44"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  internal interface IMetaDataImport // from cor.h
  {
      // we don't need to use what's inside
  }

  [Guid("809c652e-7396-11d2-9771-00a0c9b4d50c"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  internal interface IMetaDataDispenser // from cor.h
  {
      void _VtblGap0_1(); // skip 1 method
      IMetaDataImport OpenScope([MarshalAs(UnmanagedType.LPWStr)] string szScope, int dwOpenFlags, [MarshalAs(UnmanagedType.LPStruct)] Guid riid);
  }

关于c# - 检查 pdb 文件是否与源匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36649271/

相关文章:

c# - DonorManagementTests 没有默认构造函数 (nunit/moq)

windows - 为什么我需要 ILK、PDB 和 EXP 文件?

C - 显示文件中 c 可执行文件的所有行以进行调试

visual-studio - Visual Studio : debug information in release build

c# - NHibernate - 在删除时将引用设置为空

c# - 如何删除文本文件中具有奇数索引行和下一个偶数字符串的重复字符串并避免出现偶数

ipython - python pdb : resume code execution after exception caught?

c++ - 在 Qt Creator 中访问内部项目库的调试符号?

c# - ShutDownListener 中的 TaskCanceledException

python - 如何跳过 Python 调试器 (pdb) 中的列表理解?