Skip to content

Commit cb3ebbc

Browse files
authored
Support reading embedded pdbs (#3454)
1 parent def12b8 commit cb3ebbc

File tree

3 files changed

+62
-5
lines changed

3 files changed

+62
-5
lines changed

src/Microsoft.TestPlatform.ObjectModel/Navigation/DiaSession.cs

+13-4
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,21 @@ private static ISymbolReader GetSymbolReader(string binaryPath)
101101
// For remote scenario, also look for pdb in current directory, (esp for UWP)
102102
// The alternate search path should be an input from Adapters, but since it is not so currently adding a HACK
103103
pdbFilePath = !File.Exists(pdbFilePath) ? Path.Combine(Directory.GetCurrentDirectory(), Path.GetFileName(pdbFilePath)) : pdbFilePath;
104-
using var stream = new FileHelper().GetStream(pdbFilePath, FileMode.Open, FileAccess.Read);
105-
if (PortablePdbReader.IsPortable(stream))
104+
105+
if (File.Exists(pdbFilePath))
106106
{
107+
using var stream = new FileHelper().GetStream(pdbFilePath, FileMode.Open, FileAccess.Read);
108+
if (PortablePdbReader.IsPortable(stream))
109+
{
110+
return new PortableSymbolReader();
111+
}
112+
113+
return new FullSymbolReader();
114+
}
115+
else
116+
{
117+
// If we cannot find the pdb file, it might be embedded in the dll.
107118
return new PortableSymbolReader();
108119
}
109-
110-
return new FullSymbolReader();
111120
}
112121
}

src/Microsoft.TestPlatform.ObjectModel/Navigation/PortablePdbReader.cs

+11
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,17 @@ public PortablePdbReader(Stream stream)
5454
_reader = _provider.GetMetadataReader();
5555
}
5656

57+
/// <summary>
58+
/// Reads the pdb using a provided metadata reader, when the pdb is embedded in the dll, or found by
59+
/// path that is in the dll metadata.
60+
/// </summary>
61+
/// <param name="metadataReaderProvider"></param>
62+
public PortablePdbReader(MetadataReaderProvider metadataReaderProvider!!)
63+
{
64+
_provider = metadataReaderProvider;
65+
_reader = _provider.GetMetadataReader();
66+
}
67+
5768
/// <summary>
5869
/// Dispose Metadata reader
5970
/// </summary>

src/Microsoft.TestPlatform.ObjectModel/Navigation/PortableSymbolReader.cs

+38-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using System.Collections.Generic;
66
using System.IO;
77
using System.Reflection;
8+
using System.Reflection.Metadata;
9+
using System.Reflection.PortableExecutable;
810

911
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
1012
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers;
@@ -91,7 +93,10 @@ private void PopulateCacheForTypeAndMethodSymbols(string binaryPath)
9193
try
9294
{
9395
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+
95100
// At this point, the assembly should be already loaded into the load context. We query for a reference to
96101
// find the types and cache the symbol information. Let the loader follow default lookup order instead of
97102
// forcing load from a specific path.
@@ -148,4 +153,36 @@ private void PopulateCacheForTypeAndMethodSymbols(string binaryPath)
148153
throw;
149154
}
150155
}
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+
}
151188
}

0 commit comments

Comments
 (0)