diff --git a/src/Microsoft.TestPlatform.ObjectModel/Navigation/DiaSession.cs b/src/Microsoft.TestPlatform.ObjectModel/Navigation/DiaSession.cs
index 249cf364c1..8ea8c96aa6 100644
--- a/src/Microsoft.TestPlatform.ObjectModel/Navigation/DiaSession.cs
+++ b/src/Microsoft.TestPlatform.ObjectModel/Navigation/DiaSession.cs
@@ -101,12 +101,21 @@ private static ISymbolReader GetSymbolReader(string binaryPath)
// For remote scenario, also look for pdb in current directory, (esp for UWP)
// The alternate search path should be an input from Adapters, but since it is not so currently adding a HACK
pdbFilePath = !File.Exists(pdbFilePath) ? Path.Combine(Directory.GetCurrentDirectory(), Path.GetFileName(pdbFilePath)) : pdbFilePath;
- using var stream = new FileHelper().GetStream(pdbFilePath, FileMode.Open, FileAccess.Read);
- if (PortablePdbReader.IsPortable(stream))
+
+ if (File.Exists(pdbFilePath))
{
+ using var stream = new FileHelper().GetStream(pdbFilePath, FileMode.Open, FileAccess.Read);
+ if (PortablePdbReader.IsPortable(stream))
+ {
+ return new PortableSymbolReader();
+ }
+
+ return new FullSymbolReader();
+ }
+ else
+ {
+ // If we cannot find the pdb file, it might be embedded in the dll.
return new PortableSymbolReader();
}
-
- return new FullSymbolReader();
}
}
diff --git a/src/Microsoft.TestPlatform.ObjectModel/Navigation/PortablePdbReader.cs b/src/Microsoft.TestPlatform.ObjectModel/Navigation/PortablePdbReader.cs
index e6cc268fb3..479a207de6 100644
--- a/src/Microsoft.TestPlatform.ObjectModel/Navigation/PortablePdbReader.cs
+++ b/src/Microsoft.TestPlatform.ObjectModel/Navigation/PortablePdbReader.cs
@@ -54,6 +54,17 @@ public PortablePdbReader(Stream stream)
_reader = _provider.GetMetadataReader();
}
+ ///
+ /// Reads the pdb using a provided metadata reader, when the pdb is embedded in the dll, or found by
+ /// path that is in the dll metadata.
+ ///
+ ///
+ public PortablePdbReader(MetadataReaderProvider metadataReaderProvider!!)
+ {
+ _provider = metadataReaderProvider;
+ _reader = _provider.GetMetadataReader();
+ }
+
///
/// Dispose Metadata reader
///
diff --git a/src/Microsoft.TestPlatform.ObjectModel/Navigation/PortableSymbolReader.cs b/src/Microsoft.TestPlatform.ObjectModel/Navigation/PortableSymbolReader.cs
index 4573197de0..2f7c437282 100644
--- a/src/Microsoft.TestPlatform.ObjectModel/Navigation/PortableSymbolReader.cs
+++ b/src/Microsoft.TestPlatform.ObjectModel/Navigation/PortableSymbolReader.cs
@@ -5,6 +5,8 @@
using System.Collections.Generic;
using System.IO;
using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers;
@@ -91,7 +93,10 @@ private void PopulateCacheForTypeAndMethodSymbols(string binaryPath)
try
{
var pdbFilePath = Path.ChangeExtension(binaryPath, ".pdb");
- using var pdbReader = new PortablePdbReader(new FileHelper().GetStream(pdbFilePath, FileMode.Open, FileAccess.Read));
+ using PortablePdbReader pdbReader = File.Exists(pdbFilePath)
+ ? CreatePortablePdbReaderFromExistingPdbFile(pdbFilePath)
+ : CreatePortablePdbReaderFromPEData(binaryPath);
+
// At this point, the assembly should be already loaded into the load context. We query for a reference to
// find the types and cache the symbol information. Let the loader follow default lookup order instead of
// forcing load from a specific path.
@@ -148,4 +153,36 @@ private void PopulateCacheForTypeAndMethodSymbols(string binaryPath)
throw;
}
}
+
+ ///
+ /// Reads the pdb data from the dlls itself, either by loading the referenced pdb file, or by reading
+ /// embedded pdb from the dll itself.
+ ///
+ ///
+ ///
+ ///
+ private static PortablePdbReader CreatePortablePdbReaderFromPEData(string binaryPath)
+ {
+ using var dllStream = new FileStream(binaryPath, FileMode.Open, FileAccess.Read);
+ using var peReader = new PEReader(dllStream);
+
+ var hasPdb = peReader.TryOpenAssociatedPortablePdb(binaryPath, pdbPath => new FileStream(pdbPath, FileMode.Open, FileAccess.Read), out MetadataReaderProvider mp, pdbPath: out _);
+
+ // The out parameters don't give additional info about the pdbFile in case it is not found. So we have few reasons to fail:
+ if (!hasPdb)
+ {
+ throw new InvalidOperationException($"Cannot find portable .PDB file for {binaryPath}. This can have multiple reasons:"
+ + "\n- The dll was built with portable and the pdb file is missing (it was deleted, or not moved together with the dll)."
+ + "\n- The dll was built with embedded and there is some unknown error reading the metadata from the dll."
+ + "\n- The sll was built with none and the pdb file was never even emitted during build."
+ + "\n- Additionally if your dll is built with full, see FullPdbReader instead.");
+ }
+
+ return new PortablePdbReader(mp);
+ }
+
+ private static PortablePdbReader CreatePortablePdbReaderFromExistingPdbFile(string pdbFilePath)
+ {
+ return new PortablePdbReader(new FileHelper().GetStream(pdbFilePath, FileMode.Open, FileAccess.Read));
+ }
}