Skip to content

Commit

Permalink
Support reading embedded source
Browse files Browse the repository at this point in the history
  • Loading branch information
nguerrera committed Jul 13, 2016
1 parent 519e24a commit 477dfe2
Show file tree
Hide file tree
Showing 14 changed files with 362 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/DeployCoreClrTestRuntime/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"System.Diagnostics.Tools": "4.0.1",
"System.Dynamic.Runtime": "4.0.11",
"System.Globalization": "4.0.11",
"System.IO.Compression": "4.1.0",
"System.IO.FileSystem": "4.0.1",
"System.IO.FileSystem.Primitives": "4.0.1",
"System.IO.FileSystem.Watcher": "4.0.0",
Expand Down
193 changes: 193 additions & 0 deletions src/Microsoft.DiaSymReader.PortablePdb.Tests/EmbeddedSourceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// 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 Roslyn.Test.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.IO.Compression;
using System.Reflection.Metadata;
using Xunit;

namespace Microsoft.DiaSymReader.PortablePdb.UnitTests
{
using static SymTestHelpers;

public class EmbeddedSourceTests
{
[Fact]
public void EmbeddedSource_Portable()
{
EmbeddedSource(TestResources.EmbeddedSource.PortableDllAndPdb);
}

[Fact]
public void EmbeddedSource_Native()
{
NativeNotYetImplemented();
//EmbeddedSource(TestResources.EmbeddedSource.DllAndPdb);
}

private void EmbeddedSource(KeyValuePair<byte[], byte[]> dllAndPdb)
{
ISymUnmanagedReader symReader = CreateSymReaderFromResource(dllAndPdb);

foreach (string file in new[] { @"C:\EmbeddedSource.cs", @"C:\EmbeddedSourceSmall.cs" })
{
bool hasEmbeddedSource;
int length, bytesRead;
ISymUnmanagedDocument doc;
Assert.Equal(HResult.S_OK, symReader.GetDocument(file, default(Guid), default(Guid), default(Guid), out doc));
Assert.Equal(HResult.S_OK, doc.HasEmbeddedSource(out hasEmbeddedSource));
Assert.Equal(HResult.S_OK, doc.GetSourceLength(out length));
Assert.True(hasEmbeddedSource);

bool isCompressed;
var content = new byte[length];
Assert.Equal(HResult.S_OK, doc.GetSourceRange(0, 0, int.MaxValue, int.MaxValue, length, out bytesRead, content));
Assert.Equal(length, bytesRead);

if (content[0] == 0x1F && content[1] == 0x8B) // gzip signature
{
isCompressed = true;
Assert.Equal(@"C:\EmbeddedSource.cs", file);
}
else
{
isCompressed = false;
Assert.Equal(@"C:\EmbeddedSourceSmall.cs", file);
}

if (isCompressed)
{
content = Decompress(content);
}

if (dllAndPdb.Equals(TestResources.EmbeddedSource.PortableDllAndPdb))
{
CheckPortableFormatHeader(dllAndPdb.Value, file, isCompressed);
}

byte[] expectedContent = isCompressed ? TestResources.EmbeddedSource.CS : TestResources.EmbeddedSource.CSSmall;
AssertEx.Equal(expectedContent, content);
}
}

[Fact]
public void NoEmbeddedSource_Portable()
{
NoEmbeddedSource(TestResources.Documents.PortableDllAndPdb);
}

[Fact]
public void NoEmbeddedSource_Native()
{
NativeNotYetImplemented();
//NoEmbeddedSource(TestResources.Documents.DllAndPdb);
}

private void NoEmbeddedSource(KeyValuePair<byte[], byte[]> dllAndPdb)
{
ISymUnmanagedReader symReader = CreateSymReaderFromResource(dllAndPdb);

ISymUnmanagedDocument doc;
Assert.Equal(HResult.S_OK, symReader.GetDocument(@"C:\Documents.cs", default(Guid), default(Guid), default(Guid), out doc));

bool hasEmbeddedSource;
Assert.Equal(HResult.S_OK, doc.HasEmbeddedSource(out hasEmbeddedSource));
Assert.False(hasEmbeddedSource);

int length;
Assert.Equal(HResult.S_FALSE, doc.GetSourceLength(out length));
Assert.Equal(HResult.S_FALSE, doc.GetSourceRange(0, 0, -1, -1, 0, out length, null));
Assert.Equal(HResult.S_FALSE, doc.GetSourceRange(0, 0, int.MaxValue, int.MaxValue, 0, out length, null));
}

[Fact]
public void BadArgs_Portable()
{
BadArgs(TestResources.EmbeddedSource.PortableDllAndPdb);
}

[Fact]
public void BadArgs_Native()
{
NativeNotYetImplemented();
//EmbeddedSource(TestResources.EmbeddedSource.DllAndPdb);
}

private void BadArgs(KeyValuePair<byte[], byte[]> dllAndPdb)
{
ISymUnmanagedReader symReader = CreateSymReaderFromResource(dllAndPdb);

ISymUnmanagedDocument doc;
Assert.Equal(HResult.S_OK, symReader.GetDocument(@"C:\EmbeddedSource.cs", default(Guid), default(Guid), default(Guid), out doc));

int count;
Assert.Equal(HResult.E_INVALIDARG, doc.GetSourceRange(-1, 0, int.MaxValue, int.MaxValue, 0, out count, null));
Assert.Equal(HResult.E_INVALIDARG, doc.GetSourceRange(0, -1, int.MaxValue, int.MaxValue, 0, out count, null));
Assert.Equal(HResult.E_INVALIDARG, doc.GetSourceRange(0, 0, 1, int.MaxValue, 0, out count, null));
Assert.Equal(HResult.E_INVALIDARG, doc.GetSourceRange(0, 0, int.MaxValue, 1, 0, out count, null));
Assert.Equal(HResult.E_INVALIDARG, doc.GetSourceRange(0, 0, int.MaxValue, int.MaxValue, 1, out count, null));

// negative bufferLength test does not apply to native as it uses uint arguments.
if (dllAndPdb.Equals(TestResources.EmbeddedSource.PortableDllAndPdb))
{
Assert.Equal(HResult.E_INVALIDARG, doc.GetSourceRange(0, 0, int.MaxValue, int.MaxValue, -1, out count, new byte[1]));
}
}

private byte[] Decompress(byte[] content)
{
using (var compressed = new MemoryStream(content))
using (var decompressor = new GZipStream(compressed, CompressionMode.Decompress))
using (var decompressed = new MemoryStream())
{
decompressor.CopyTo(decompressed);
return decompressed.ToArray();
}
}

// Portable PDB has a leading format indicator that is not exposed via the COM API. Check it for consistency.
private static void CheckPortableFormatHeader(byte[] portablePdb, string file, bool isCompressed)
{
using (var provider = MetadataReaderProvider.FromPortablePdbImage(ImmutableArray.Create(portablePdb)))
{
var portablePdbReader = provider.GetMetadataReader();
var document = GetDocumentHandle(portablePdbReader, file);
var customDebugInfo = portablePdbReader.GetCustomDebugInformation(document, MetadataUtilities.EmbeddedSourceId);
var blobReader = portablePdbReader.GetBlobReader(customDebugInfo);
Assert.Equal(isCompressed ? 1 : 0, blobReader.ReadUInt16());
}
}

private static DocumentHandle GetDocumentHandle(MetadataReader portablePdbReader, string file)
{
foreach (var handle in portablePdbReader.Documents)
{
var document = portablePdbReader.GetDocument(handle);
if (portablePdbReader.StringComparer.Equals(document.Name, file))
{
return handle;
}
}

Assert.False(true, "Document not found.");
throw null;
}

private void NativeNotYetImplemented()
{
// TODO: This checks the behavior of Microsoft.DiaSymReader.Native before the update that
// has native code support. When this starts to fail, remove this and uncomment the
// tests that pass native PDB to same worker routines as portable PDB.
ISymUnmanagedReader symReader = CreateSymReaderFromResource(TestResources.EmbeddedSource.DllAndPdb);

ISymUnmanagedDocument doc;
Assert.Equal(HResult.S_OK, symReader.GetDocument(@"C:\EmbeddedSource.cs", default(Guid), default(Guid), default(Guid), out doc));

bool hasEmbeddedSource;
Assert.Equal(HResult.E_NOTIMPL, doc.HasEmbeddedSource(out hasEmbeddedSource));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,32 @@
<EmbeddedResource Include="Resources\MethodBoundaries.pdbx">
<LogicalName>MethodBoundaries.pdbx</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\EmbeddedSource.cs">
<LogicalName>EmbeddedSource.cs</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\EmbeddedSourceSmall.cs">
<LogicalName>EmbeddedSourceSmall.cs</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\EmbeddedSource.dll">
<LogicalName>EmbeddedSource.dll</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\EmbeddedSource.pdb">
<LogicalName>EmbeddedSource.pdb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\EmbeddedSource.dllx">
<LogicalName>EmbeddedSource.dllx</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\EmbeddedSource.pdbx">
<LogicalName>EmbeddedSource.pdbx</LogicalName>
</EmbeddedResource>
<Content Include="Resources\Documents.cmd" />
<Content Include="Resources\Scopes.cmd" />
<Content Include="Resources\MethodBoundaries.cmd" />
<Content Include="Resources\Async.cmd" />
<Content Include="Resources\Scopes.cs" />
<Content Include="Resources\Async.cs" />
<Content Include="Resources\Documents.cs" />
<Compile Include="EmbeddedSourceTests.cs" />
<Compile Include="MethodMapTests.cs" />
<Compile Include="ResourceLoader.cs" />
<Content Include="Resources\MethodBoundaries.cs" />
Expand All @@ -89,10 +108,11 @@
<Compile Include="SymReaderTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="project.json" />
<None Include="project.json" />
<None Include="Microsoft.DiaSymReader.PortablePdb.UnitTests.xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Content Include="Resources\EmbeddedSource.cmd" />
</ItemGroup>
<ImportGroup>
<Import Project="..\..\build\Targets\Imports.targets" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@REM -- TODO: Compiled with WIP writer and preliminary command-line.
@setlocal
@set CSC=D:\src\roslyn\binaries\debug\csc.exe

%CSC% /target:library /debug:portable /optimize- /deterministic /pathmap:%~dp0=C:\ /embedsourceinpdb EmbeddedSource.cs EmbeddedSourceSmall.cs
copy /y EmbeddedSource.pdb EmbeddedSource.pdbx
copy /y EmbeddedSource.dll EmbeddedSource.dllx

%CSC% /target:library /debug+ /optimize- /deterministic /pathmap:%~dp0=C:\ /embedsourceinpdb EmbeddedSource.cs EmbeddedSourceSmall.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// should be higher than compression threshold (200 chars)

using System;

namespace Test
{
public static class SomeCode
{
public static int SomeMethod(int value)
{
if (value < 0)
{
throw new ArgumentOutOfRangeException(nameof(value));
}

return checked(value + 42);
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// should be less than compression threshold (200 chars)
public class Small
{
public Small() {}
}
26 changes: 24 additions & 2 deletions src/Microsoft.DiaSymReader.PortablePdb.Tests/TestResources.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.Generic;

namespace TestResources
Expand Down Expand Up @@ -70,4 +68,28 @@ public static class MethodBoundaries
public static KeyValuePair<byte[], byte[]> PortableDllAndPdb => new KeyValuePair<byte[], byte[]>(PortableDll, PortablePdb);
public static KeyValuePair<byte[], byte[]> DllAndPdb => new KeyValuePair<byte[], byte[]>(Dll, Pdb);
}

public static class EmbeddedSource
{
private static byte[] s_portableDll;
public static byte[] PortableDll => ResourceLoader.GetOrCreateResource(ref s_portableDll, nameof(EmbeddedSource) + ".dllx");

private static byte[] s_portablePdb;
public static byte[] PortablePdb => ResourceLoader.GetOrCreateResource(ref s_portablePdb, nameof(EmbeddedSource) + ".pdbx");

private static byte[] s_dll;
public static byte[] Dll => ResourceLoader.GetOrCreateResource(ref s_dll, nameof(EmbeddedSource) + ".dll");

private static byte[] s_pdb;
public static byte[] Pdb => ResourceLoader.GetOrCreateResource(ref s_pdb, nameof(EmbeddedSource) + ".pdb");

private static byte[] s_cs;
public static byte[] CS => ResourceLoader.GetOrCreateResource(ref s_cs, nameof(EmbeddedSource) + ".cs");

private static byte[] s_csSmall;
public static byte[] CSSmall => ResourceLoader.GetOrCreateResource(ref s_csSmall, nameof(EmbeddedSource) + "Small.cs");

public static KeyValuePair<byte[], byte[]> PortableDllAndPdb => new KeyValuePair<byte[], byte[]>(PortableDll, PortablePdb);
public static KeyValuePair<byte[], byte[]> DllAndPdb => new KeyValuePair<byte[], byte[]>(Dll, Pdb);
}
}
1 change: 1 addition & 0 deletions src/Microsoft.DiaSymReader.PortablePdb.Tests/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"System.Runtime": "4.1.0",
"System.Runtime.Extensions": "4.1.0",
"System.Runtime.InteropServices": "4.1.0",
"System.IO.Compression": "4.1.0",
"System.IO.FileSystem": "4.0.1",
"Microsoft.DiaSymReader.Native": "1.4.0",
"xunit": "2.1.0",
Expand Down
Loading

0 comments on commit 477dfe2

Please sign in to comment.