Skip to content

Commit

Permalink
Enable embedding sources to Windows PDBs
Browse files Browse the repository at this point in the history
  • Loading branch information
tmat committed Aug 9, 2017
1 parent 7c59af2 commit b6756c3
Show file tree
Hide file tree
Showing 22 changed files with 445 additions and 246 deletions.
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/CSharpResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -4986,7 +4986,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<value>/sourcelink switch is only supported when emitting PDB.</value>
</data>
<data name="ERR_CannotEmbedWithoutPdb" xml:space="preserve">
<value>/embed switch is only supported when emitting Portable PDB (/debug:portable or /debug:embedded).</value>
<value>/embed switch is only supported when emitting a PDB.</value>
</data>
<data name="ERR_InvalidInstrumentationKind" xml:space="preserve">
<value>Invalid instrumentation kind: {0}</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1243,14 +1243,9 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable<string> ar
embeddedFiles.AddRange(sourceFiles);
}

if (embeddedFiles.Count > 0)
if (embeddedFiles.Count > 0 && !emitPdb)
{
// Restricted to portable PDBs for now, but the IsPortable condition should be removed
// and the error message adjusted accordingly when native PDB support is added.
if (!emitPdb || !debugInformationFormat.IsPortable())
{
AddDiagnostic(diagnostics, ErrorCode.ERR_CannotEmbedWithoutPdb);
}
AddDiagnostic(diagnostics, ErrorCode.ERR_CannotEmbedWithoutPdb);
}

var parsedFeatures = ParseFeatures(features);
Expand Down
71 changes: 57 additions & 14 deletions src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.DiaSymReader;
using Roslyn.Test.PdbUtilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
Expand Down Expand Up @@ -1898,18 +1900,14 @@ public void Embed()
parsedArgs = DefaultParse(new[] { "/embed:a.txt", "/debug-", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));

// These should fail when native PDB support is added.
parsedArgs = DefaultParse(new[] { "/embed", "/debug:full", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));

parsedArgs = DefaultParse(new[] { "/embed", "/debug:full", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));
parsedArgs.Errors.Verify();

parsedArgs = DefaultParse(new[] { "/embed", "/debug:pdbonly", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));
parsedArgs.Errors.Verify();

parsedArgs = DefaultParse(new[] { "/embed", "/debug+", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));
parsedArgs.Errors.Verify();
}

[Theory]
Expand All @@ -1921,7 +1919,7 @@ public void Embed()
[InlineData("/debug:embedded", "/embed:embed.cs", new[] { "embed.cs", "embed.xyz" })]
[InlineData("/debug:embedded", "/embed:embed2.cs", new[] { "embed2.cs" })]
[InlineData("/debug:embedded", "/embed:embed.xyz", new[] {"embed.xyz" })]
public void Embed_EndToEnd(string debugSwitch, string embedSwitch, string[] expectedEmbedded)
public void Embed_EndToEnd_Portable(string debugSwitch, string embedSwitch, string[] expectedEmbedded)
{
// embed.cs: large enough to compress, has #line directives
const string embed_cs =
Expand Down Expand Up @@ -1981,14 +1979,32 @@ static void Main() {
int exitCode = csc.Run(output);
Assert.Equal("", output.ToString().Trim());
Assert.Equal(0, exitCode);

bool embedded = debugSwitch == "/debug:embedded";

switch (debugSwitch)
{
case "/debug:embedded":
ValidateEmbeddedSources_Portable(expectedEmbeddedMap, dir, isEmbeddedPdb: true);
break;
case "/debug:portable":
ValidateEmbeddedSources_Portable(expectedEmbeddedMap, dir, isEmbeddedPdb: false);
break;
case "/debug:full":
ValidateEmbeddedSources_Windows(expectedEmbeddedMap, dir);
break;
}

Assert.Empty(expectedEmbeddedMap);
CleanupAllGeneratedFiles(src.Path);
}

private static void ValidateEmbeddedSources_Portable(Dictionary<string, string> expectedEmbeddedMap, TempDirectory dir, bool isEmbeddedPdb)
{
using (var peReader = new PEReader(File.OpenRead(Path.Combine(dir.Path, "embed.exe"))))
{
var entry = peReader.ReadDebugDirectory().SingleOrDefault(e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb);
Assert.Equal(embedded, entry.DataSize > 0);
Assert.Equal(isEmbeddedPdb, entry.DataSize > 0);

using (var mdProvider = embedded ?
using (var mdProvider = isEmbeddedPdb ?
peReader.ReadEmbeddedPortablePdbDebugDirectoryData(entry) :
MetadataReaderProvider.FromPortablePdbStream(File.OpenRead(Path.Combine(dir.Path, "embed.pdb"))))
{
Expand All @@ -2011,9 +2027,36 @@ static void Main() {
}
}
}
}

Assert.Empty(expectedEmbeddedMap);
CleanupAllGeneratedFiles(src.Path);
private static void ValidateEmbeddedSources_Windows(Dictionary<string, string> expectedEmbeddedMap, TempDirectory dir)
{
ISymUnmanagedReader5 symReader = null;

try
{
symReader = SymReaderFactory.CreateReader(File.OpenRead(Path.Combine(dir.Path, "embed.pdb")));

foreach (var doc in symReader.GetDocuments())
{
var docPath = doc.GetName();

var sourceBlob = doc.GetEmbeddedSource();
if (sourceBlob.Array == null)
{
continue;
}

var sourceStr = Encoding.UTF8.GetString(sourceBlob.Array, sourceBlob.Offset, sourceBlob.Count);

Assert.Equal(expectedEmbeddedMap[docPath], sourceStr);
Assert.True(expectedEmbeddedMap.Remove(docPath));
}
}
catch
{
symReader?.Dispose();
}
}

[Fact]
Expand Down
128 changes: 128 additions & 0 deletions src/Compilers/CSharp/Test/Emit/PDB/PDBEmbeddedSourceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.IO;
using System.Linq;
using System.Reflection.PortableExecutable;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.DiaSymReader;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.CSharp.UnitTests.PDB
{
public class PDBEmbeddedSourceTests : CSharpTestBase
{
[Fact]
public void StandalonePdb()
{
string source1 = @"
using System;
class C
{
public static void Main()
{
Console.WriteLine();
}
}
";
string source2 = @"
// no code
";

var tree1 = Parse(source1, "f:/build/goo.cs");
var tree2 = Parse(source2, "f:/build/nocode.cs");
var c = CreateStandardCompilation(new[] { tree1, tree2 }, options: TestOptions.DebugDll);
var embeddedTexts = new[]
{
EmbeddedText.FromSource(tree1.FilePath, tree1.GetText()),
EmbeddedText.FromSource(tree2.FilePath, tree2.GetText())
};

c.VerifyPdb(@"
<symbols>
<files>
<file id=""1"" name=""f:/build/goo.cs"" language=""3f5162f8-07c6-11d3-9053-00c04fa302a1"" languageVendor=""994b45c4-e6e9-11d2-903f-00c04fa302a1"" documentType=""5a869d0b-6611-11d3-bd2a-0000f80849bd"" checkSumAlgorithmId=""ff1816ec-aa5e-4d10-87f7-6f4963833460"" checkSum=""5D, 7D, CF, 1B, 79, 12, E, A, 80, 13, E0, 98, 7E, 5C, AA, 3B, 63, D8, 7E, 4F, "" embeddedSourceLength=""98""><![CDATA[
using System;
class C
{
public static void Main()
{
Console.WriteLine();
}
}
]]></file>
<file id=""2"" name=""f:/build/nocode.cs"" language=""3f5162f8-07c6-11d3-9053-00c04fa302a1"" languageVendor=""994b45c4-e6e9-11d2-903f-00c04fa302a1"" documentType=""5a869d0b-6611-11d3-bd2a-0000f80849bd"" checkSumAlgorithmId=""ff1816ec-aa5e-4d10-87f7-6f4963833460"" checkSum=""8B, 1D, 3F, 75, E0, A8, 8F, 90, B2, D3, 52, CF, 71, 9B, 17, 29, 3C, 70, 7A, 42, "" embeddedSourceLength=""21""><![CDATA[
// no code
]]></file>
</files>
<methods>
<method containingType=""C"" name=""Main"">
<customDebugInfo>
<using>
<namespace usingCount=""1"" />
</using>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""7"" startColumn=""5"" endLine=""7"" endColumn=""6"" document=""1"" />
<entry offset=""0x1"" startLine=""8"" startColumn=""9"" endLine=""8"" endColumn=""29"" document=""1"" />
<entry offset=""0x7"" startLine=""9"" startColumn=""5"" endLine=""9"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0x8"">
<namespace name=""System"" />
</scope>
</method>
</methods>
</symbols>
", embeddedTexts);
}

[Fact]
public void EmbeddedPdb()
{
string source = @"
using System;
class C
{
public static void Main()
{
Console.WriteLine();
}
}
";
var tree = Parse(source, "f:/build/goo.cs");
var c = CreateStandardCompilation(tree, options: TestOptions.DebugDll);

var pdbStream = new MemoryStream();
var peBlob = c.EmitToArray(
EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Embedded),
embeddedTexts: new[] { EmbeddedText.FromSource(tree.FilePath, tree.GetText()) });
pdbStream.Position = 0;

using (var peReader = new PEReader(peBlob))
{
var embeddedEntry = peReader.ReadDebugDirectory().Single(e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb);

using (var embeddedMetadataProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedEntry))
{
var pdbReader = embeddedMetadataProvider.GetMetadataReader();

var embeddedSource =
(from documentHandle in pdbReader.Documents
let document = pdbReader.GetDocument(documentHandle)
select new
{
FilePath = pdbReader.GetString(document.Name),
Text = pdbReader.GetEmbeddedSource(documentHandle)
}).Single();

Assert.Equal(embeddedSource.FilePath, "f:/build/goo.cs");
Assert.Equal(source, embeddedSource.Text.ToString());
}
}
}
}
}
88 changes: 0 additions & 88 deletions src/Compilers/CSharp/Test/Emit/PDB/PortablePdbTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -362,93 +362,5 @@ public static void Main()
// error CS0041: Unexpected error writing debug information -- 'Error!'
Diagnostic(ErrorCode.FTL_DebugEmitFailure).WithArguments("Error!").WithLocation(1, 1));
}

[Fact]
public void EmbeddedSource()
{
string source = @"
using System;
class C
{
public static void Main()
{
Console.WriteLine();
}
}
";
var tree = Parse(source, "f:/build/goo.cs");
var c = CreateStandardCompilation(tree, options: TestOptions.DebugDll);

var pdbStream = new MemoryStream();
c.EmitToArray(
EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.PortablePdb),
pdbStream: pdbStream,
embeddedTexts: new[] { EmbeddedText.FromSource(tree.FilePath, tree.GetText()) });
pdbStream.Position = 0;

using (var provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream))
{
var pdbReader = provider.GetMetadataReader();

var embeddedSource =
(from documentHandle in pdbReader.Documents
let document = pdbReader.GetDocument(documentHandle)
select new
{
FilePath = pdbReader.GetString(document.Name),
Text = pdbReader.GetEmbeddedSource(documentHandle)
}).Single();

Assert.Equal(embeddedSource.FilePath, "f:/build/goo.cs");
Assert.Equal(source, embeddedSource.Text.ToString());
}
}

[Fact]
public void EmbeddedSource_InEmbeddedPdb()
{
string source = @"
using System;
class C
{
public static void Main()
{
Console.WriteLine();
}
}
";
var tree = Parse(source, "f:/build/goo.cs");
var c = CreateStandardCompilation(tree, options: TestOptions.DebugDll);

var pdbStream = new MemoryStream();
var peBlob = c.EmitToArray(
EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Embedded),
embeddedTexts: new[] { EmbeddedText.FromSource(tree.FilePath, tree.GetText()) });
pdbStream.Position = 0;

using (var peReader = new PEReader(peBlob))
{
var embeddedEntry = peReader.ReadDebugDirectory().Single(e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb);

using (var embeddedMetadataProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedEntry))
{
var pdbReader = embeddedMetadataProvider.GetMetadataReader();

var embeddedSource =
(from documentHandle in pdbReader.Documents
let document = pdbReader.GetDocument(documentHandle)
select new
{
FilePath = pdbReader.GetString(document.Name),
Text = pdbReader.GetEmbeddedSource(documentHandle)
}).Single();

Assert.Equal(embeddedSource.FilePath, "f:/build/goo.cs");
Assert.Equal(source, embeddedSource.Text.ToString());
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ public void Emit_BadArgs()

Assert.Throws<ArgumentException>("embeddedTexts", () => comp.Emit(
peStream: new MemoryStream(),
pdbStream: new MemoryStream(),
options: EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.Pdb),
pdbStream: null,
options: null,
embeddedTexts: new[] { EmbeddedText.FromStream("_", new MemoryStream()) }));

Assert.Throws<ArgumentException>("embeddedTexts", () => comp.Emit(
Expand Down
6 changes: 3 additions & 3 deletions src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b6756c3

Please sign in to comment.