|
5 | 5 | using System.Collections.Generic;
|
6 | 6 | using System.IO;
|
7 | 7 | using System.Reflection;
|
| 8 | +using System.Reflection.Metadata; |
| 9 | +using System.Reflection.PortableExecutable; |
8 | 10 |
|
9 | 11 | using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
|
10 | 12 | using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers;
|
@@ -91,7 +93,10 @@ private void PopulateCacheForTypeAndMethodSymbols(string binaryPath)
|
91 | 93 | try
|
92 | 94 | {
|
93 | 95 | var pdbFilePath = Path.ChangeExtension(binaryPath, ".pdb");
|
94 |
| - using var pdbReader = new PortablePdbReader(new FileHelper().GetStream(pdbFilePath, FileMode.Open, FileAccess.Read)); |
| 96 | + using PortablePdbReader pdbReader = File.Exists(pdbFilePath) |
| 97 | + ? CreatePortablePdbReaderFromExistingPdbFile(pdbFilePath) |
| 98 | + : CreatePortablePdbReaderFromPEData(binaryPath); |
| 99 | + |
95 | 100 | // At this point, the assembly should be already loaded into the load context. We query for a reference to
|
96 | 101 | // find the types and cache the symbol information. Let the loader follow default lookup order instead of
|
97 | 102 | // forcing load from a specific path.
|
@@ -148,4 +153,36 @@ private void PopulateCacheForTypeAndMethodSymbols(string binaryPath)
|
148 | 153 | throw;
|
149 | 154 | }
|
150 | 155 | }
|
| 156 | + |
| 157 | + /// <summary> |
| 158 | + /// Reads the pdb data from the dlls itself, either by loading the referenced pdb file, or by reading |
| 159 | + /// embedded pdb from the dll itself. |
| 160 | + /// </summary> |
| 161 | + /// <param name="binaryPath"></param> |
| 162 | + /// <returns></returns> |
| 163 | + /// <exception cref="InvalidOperationException"></exception> |
| 164 | + private static PortablePdbReader CreatePortablePdbReaderFromPEData(string binaryPath) |
| 165 | + { |
| 166 | + using var dllStream = new FileStream(binaryPath, FileMode.Open, FileAccess.Read); |
| 167 | + using var peReader = new PEReader(dllStream); |
| 168 | + |
| 169 | + var hasPdb = peReader.TryOpenAssociatedPortablePdb(binaryPath, pdbPath => new FileStream(pdbPath, FileMode.Open, FileAccess.Read), out MetadataReaderProvider mp, pdbPath: out _); |
| 170 | + |
| 171 | + // The out parameters don't give additional info about the pdbFile in case it is not found. So we have few reasons to fail: |
| 172 | + if (!hasPdb) |
| 173 | + { |
| 174 | + throw new InvalidOperationException($"Cannot find portable .PDB file for {binaryPath}. This can have multiple reasons:" |
| 175 | + + "\n- The dll was built with <DebugType>portable</DebugType> and the pdb file is missing (it was deleted, or not moved together with the dll)." |
| 176 | + + "\n- The dll was built with <DebugType>embedded</DebugType> and there is some unknown error reading the metadata from the dll." |
| 177 | + + "\n- The sll was built with <DebugType>none</DebugType> and the pdb file was never even emitted during build." |
| 178 | + + "\n- Additionally if your dll is built with <DebugType>full</DebugType>, see FullPdbReader instead."); |
| 179 | + } |
| 180 | + |
| 181 | + return new PortablePdbReader(mp); |
| 182 | + } |
| 183 | + |
| 184 | + private static PortablePdbReader CreatePortablePdbReaderFromExistingPdbFile(string pdbFilePath) |
| 185 | + { |
| 186 | + return new PortablePdbReader(new FileHelper().GetStream(pdbFilePath, FileMode.Open, FileAccess.Read)); |
| 187 | + } |
151 | 188 | }
|
0 commit comments