未验证 提交 f6a92c04 编写于 作者: T Tomáš Matoušek 提交者: GitHub

Fix EnC for multi-targeted projects that emit embedded PDBs (#39178)

上级 9f0d3fcf
......@@ -785,6 +785,8 @@ void Emit()
}
else
{
// Report diagnosics even when the module is never going to be loaded (e.g. in multi-targeting scenario, where only one framework being debugged).
// This is consistent with reporting compilation errors - the IDE reports them for all TFMs regardless of what framework the app is running on.
diagnostics.Add((project.Id, createBaselineDiagnostics));
Telemetry.LogProjectAnalysisSummary(projectSummary, createBaselineDiagnostics);
isBlocked = true;
......
......@@ -20,24 +20,28 @@ public void OpenStream_Errors()
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void AssemblyAndPdb(bool exactPdbPath)
[InlineData(DebugInformationFormat.PortablePdb, true)]
[InlineData(DebugInformationFormat.PortablePdb, false)]
[InlineData(DebugInformationFormat.Embedded, false)]
public void AssemblyAndPdb(DebugInformationFormat pdbFormat, bool exactPdbPath)
{
var dir = Temp.CreateDirectory();
var dllFile = dir.CreateFile("lib.dll");
var pdbFile = dir.CreateFile("lib.pdb");
var pdbFile = (pdbFormat == DebugInformationFormat.Embedded) ? null : dir.CreateFile("lib.pdb");
var source = @"class C { public static void Main() { int x = 1; } }";
var compilation = CSharpTestBase.CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.DebugDll, assemblyName: "lib");
var pdbStream = new MemoryStream();
var pdbStream = (pdbFile != null) ? new MemoryStream() : null;
var debugDirPdbPath = exactPdbPath ? pdbFile.Path : "a/y/z/lib.pdb";
var peImage = compilation.EmitToArray(new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb, pdbFilePath: debugDirPdbPath), pdbStream: pdbStream);
pdbStream.Position = 0;
var peImage = compilation.EmitToArray(new EmitOptions(debugInformationFormat: pdbFormat, pdbFilePath: debugDirPdbPath), pdbStream: pdbStream);
dllFile.WriteAllBytes(peImage);
pdbFile.WriteAllBytes(pdbStream.ToArray());
if (pdbFile != null)
{
pdbStream.Position = 0;
pdbFile.WriteAllBytes(pdbStream.ToArray());
}
var outputs = new CompilationOutputFilesWithImplicitPdbPath(dllFile.Path);
......@@ -59,5 +63,43 @@ public void AssemblyAndPdb(bool exactPdbPath)
Directory.Delete(dir.Path, recursive: true);
}
[Fact]
public void AssemblyFileNotFound()
{
var dir = Temp.CreateDirectory();
var outputs = new CompilationOutputFilesWithImplicitPdbPath(Path.Combine(dir.Path, "nonexistent.dll"));
Assert.Null(outputs.OpenPdb());
Assert.Null(outputs.OpenAssemblyMetadata(prefetch: false));
}
[Fact]
public void PdbFileNotFound()
{
var dir = Temp.CreateDirectory();
var dllFile = dir.CreateFile("lib.dll");
var source = @"class C { public static void Main() { int x = 1; } }";
var compilation = CSharpTestBase.CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.DebugDll, assemblyName: "lib");
var pdbStream = new MemoryStream();
var debugDirPdbPath = Path.Combine(dir.Path, "nonexistent.pdb");
var peImage = compilation.EmitToArray(new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb, pdbFilePath: debugDirPdbPath), pdbStream: pdbStream);
pdbStream.Position = 0;
dllFile.WriteAllBytes(peImage);
var outputs = new CompilationOutputFilesWithImplicitPdbPath(dllFile.Path);
Assert.Null(outputs.OpenPdb());
using (var metadata = outputs.OpenAssemblyMetadata(prefetch: false))
{
var mdReader = metadata.GetMetadataReader();
Assert.Equal("lib", mdReader.GetString(mdReader.GetAssemblyDefinition().Name));
}
// make sure all files are closed and can be deleted
Directory.Delete(dir.Path, recursive: true);
}
}
}
......@@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using System.Reflection.PortableExecutable;
using Microsoft.CodeAnalysis.Debugging;
using Microsoft.CodeAnalysis.Emit;
using Roslyn.Utilities;
......@@ -27,12 +28,18 @@ public CompilationOutputFilesWithImplicitPdbPath(string assemblyFilePath = null)
}
public override string AssemblyDisplayPath => AssemblyFilePath;
// heuristic for error messages (determining the actual path requires opening the assembly):
public override string PdbDisplayPath => Path.GetFileNameWithoutExtension(AssemblyFilePath) + ".pdb";
protected override Stream OpenAssemblyStream()
=> AssemblyFilePath != null ? FileUtilities.OpenRead(AssemblyFilePath) : null;
=> TryOpenFileStream(AssemblyFilePath);
// Not gonna be called since we override OpenPdb.
protected override Stream OpenPdbStream()
=> throw ExceptionUtilities.Unreachable;
public override DebugInformationReaderProvider OpenPdb()
{
var assemblyStream = OpenAssemblyStream();
if (assemblyStream == null)
......@@ -44,30 +51,49 @@ protected override Stream OpenPdbStream()
string pdbPath;
using (var peReader = new PEReader(assemblyStream))
{
var pdbEntry = peReader.ReadDebugDirectory().FirstOrDefault(
e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb || e.Type == DebugDirectoryEntryType.CodeView);
var debugDirectory = peReader.ReadDebugDirectory();
var embeddedPdbEntry = debugDirectory.FirstOrDefault(e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb);
if (embeddedPdbEntry.DataSize != 0)
{
return DebugInformationReaderProvider.CreateFromMetadataReader(peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedPdbEntry));
}
if (pdbEntry.Type != DebugDirectoryEntryType.CodeView)
var codeViewEntry = debugDirectory.FirstOrDefault(e => e.Type == DebugDirectoryEntryType.CodeView);
if (codeViewEntry.DataSize == 0)
{
return null;
}
pdbPath = peReader.ReadCodeViewDebugDirectoryData(pdbEntry).Path;
pdbPath = peReader.ReadCodeViewDebugDirectoryData(codeViewEntry).Path;
}
// First try to use the full path as specified in the PDB, then look next to the assembly.
Stream result;
var pdbStream =
TryOpenFileStream(pdbPath) ??
TryOpenFileStream(Path.Combine(Path.GetDirectoryName(AssemblyFilePath), PathUtilities.GetFileName(pdbPath)));
return (pdbStream != null) ? DebugInformationReaderProvider.CreateFromStream(pdbStream) : null;
}
private static Stream TryOpenFileStream(string path)
{
if (path == null)
{
return null;
}
try
{
result = new FileStream(pdbPath, FileMode.Open, FileAccess.Read, FileShare.Read);
return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
}
catch (Exception e) when (e is FileNotFoundException || e is DirectoryNotFoundException)
{
pdbPath = Path.Combine(Path.GetDirectoryName(AssemblyFilePath), PathUtilities.GetFileName(pdbPath));
result = FileUtilities.OpenRead(pdbPath);
return null;
}
catch (Exception e) when (!(e is IOException))
{
throw new IOException(e.Message, e);
}
return result;
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册