From 05826c996e7ed0470b30425f442f4f99d4025877 Mon Sep 17 00:00:00 2001 From: Nick Guerrera Date: Wed, 13 Jul 2016 15:43:18 -0700 Subject: [PATCH] Support reading embedded source --- src/DeployCoreClrTestRuntime/project.json | 1 + .../EmbeddedSourceTests.cs | 193 ++++++++++++++++++ ....DiaSymReader.PortablePdb.UnitTests.csproj | 19 ++ .../Resources/EmbeddedSource.cmd | 9 + .../Resources/EmbeddedSource.cs | 19 ++ .../Resources/EmbeddedSource.dll | Bin 0 -> 3584 bytes .../Resources/EmbeddedSource.dllx | Bin 0 -> 3584 bytes .../Resources/EmbeddedSource.pdb | Bin 0 -> 11776 bytes .../Resources/EmbeddedSource.pdbx | Bin 0 -> 900 bytes .../Resources/EmbeddedSourceSmall.cs | 5 + .../TestResources.cs | 26 ++- .../project.json | 1 + .../SymDocument.cs | 95 ++++++++- .../Utilities/MetadataUtilities.cs | 1 + 14 files changed, 360 insertions(+), 9 deletions(-) create mode 100644 src/Microsoft.DiaSymReader.PortablePdb.Tests/EmbeddedSourceTests.cs create mode 100644 src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.cmd create mode 100644 src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.cs create mode 100644 src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.dll create mode 100644 src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.dllx create mode 100644 src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.pdb create mode 100644 src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.pdbx create mode 100644 src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSourceSmall.cs diff --git a/src/DeployCoreClrTestRuntime/project.json b/src/DeployCoreClrTestRuntime/project.json index 8beb1d911..97bfab5e9 100644 --- a/src/DeployCoreClrTestRuntime/project.json +++ b/src/DeployCoreClrTestRuntime/project.json @@ -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", diff --git a/src/Microsoft.DiaSymReader.PortablePdb.Tests/EmbeddedSourceTests.cs b/src/Microsoft.DiaSymReader.PortablePdb.Tests/EmbeddedSourceTests.cs new file mode 100644 index 000000000..e7b5db169 --- /dev/null +++ b/src/Microsoft.DiaSymReader.PortablePdb.Tests/EmbeddedSourceTests.cs @@ -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 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 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 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)); + } + } +} diff --git a/src/Microsoft.DiaSymReader.PortablePdb.Tests/Microsoft.DiaSymReader.PortablePdb.UnitTests.csproj b/src/Microsoft.DiaSymReader.PortablePdb.Tests/Microsoft.DiaSymReader.PortablePdb.UnitTests.csproj index 1f27db4ba..a91184786 100644 --- a/src/Microsoft.DiaSymReader.PortablePdb.Tests/Microsoft.DiaSymReader.PortablePdb.UnitTests.csproj +++ b/src/Microsoft.DiaSymReader.PortablePdb.Tests/Microsoft.DiaSymReader.PortablePdb.UnitTests.csproj @@ -77,10 +77,29 @@ MiscEmbedded.dll + + EmbeddedSource.cs + + + EmbeddedSourceSmall.cs + + + EmbeddedSource.dll + + + EmbeddedSource.pdb + + + EmbeddedSource.dllx + + + EmbeddedSource.pdbx + + diff --git a/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.cmd b/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.cmd new file mode 100644 index 000000000..c71271b9a --- /dev/null +++ b/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.cmd @@ -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 diff --git a/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.cs b/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.cs new file mode 100644 index 000000000..c9dfadb1f --- /dev/null +++ b/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.cs @@ -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); + } + } +} diff --git a/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.dll b/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.dll new file mode 100644 index 0000000000000000000000000000000000000000..f31e6b27ccd91d8f798fa14b682843bc0a8a3c73 GIT binary patch literal 3584 zcmeHJU1$_n6#izjiEEq(MF0H(=`LeO^#^$MMH#X~0Sc@Fr ztT;gsCT3ZgQ6n(jz$}j2rXN<7wR&~;melmbD6r4yK>zz6{MhogfG5lzBM)>@B7<;2 zHkcd*4q&aOGtIa~An|LMC#GDW1BG`*RsPy@%8H0kqTO*J3c%HLpRkK?6j(*(>jB{T zMd3dnOdz*JEl2*fCA^iW*@XNh37l&>&%69neAJ5KsKR9(TY-e=FEvb-;(`?^FQlTx zilZE2OEa8PC;-DPx;39+{sIzP2Aw!%GMo;;5a9#BQ<<%dup8J0^yM?yNE@`(i_E&) z+3v3Gs}PZ`$DLF zYzl2BksH)v(fEq_$}tZduyLtH?>?}nNZB>=C0QHx!ZK$8ij3X;8LSlM4`UGPnmNmL zWBR&$bV1KeUg*{UK29U;!e0EsqdTtgv-k;*po3~%c+@yYd;#aUuenpOd~5L`CHcMD{&uo z5oSlNkxSHq$Km{j#c7Rn-m~ zQAO#M5k6)VRk=~CIb~1nN_ZaSMxrnh`gPZH5;qJcmFLWADsHb&HiE?Um8fy4?0Rl8 z*Iu1*yapZkj_2Lw)v8it&&7%Iu`8-Id=(_)jb!}Tq!ZNC=xjyR#V4#v5=N<_Rdk(N z5XOmHiCd!7ztxOWMYf7&+=`0RWO%7q39{qmwH?aYw>-GD$SC60g zsB7$-Uw8a`IQ!gZ$eMo8$-lIZQtuE=C^B3qOD?nLR}hM|SiSs1xo&PZSIM!64D znRSOEr@lW77UQ3qj>0!%gL*6E7olEZI?|6wmpQa;hrYQ4ga;4za|3QyOzCQ5c8E5-FZN=TB-K zYa-Q|sFp3MNnDy#vAzy*Y5DcA>eVt%roBbN`9f$@sC%K+gPJQxM#8A*d1H9gvRfp1;>kbm?h2)xWF?)>(8H_2RmP>iX_3})-X-$;8OxmLi~-Va;b!LxK>q#`*(habiNa`7c!a# y<4Sl^%d>9hE4j=6RZ^o}tV>Ky$m@J_oYK$)JR)o-Yvr{TDo51Ys$9Bl<&Sqw| zQew0wXz*ej2D@WFsO!3 z&2*i5J&4V+GQ(!w^y+4Q(l%>BMOkat_HIjD7si0YMi-{n{CGYw;TE1W`;08mLyI)h zMVVl-<=BArT28g%7K6mEX`Y;Rp)Ta!5vcri`a~5IsYJh%BIJOtl6j&o(Q#l6l?y|_ zvrEQ*AecaA87xEnm1VjWt5Y%c%Mv)pbe?yuPxEmrj6#>gI<_1I!7q1AmFAokDnB4l zV#RSb@ud~^3FUy1w%qQ|F@GV6Ero8JH5pD9V2JSn=&95WM%WAN1O~DxY^D$T>PKqu zPP(^e*BXRW8&Nkwr&C*Ye&6>DRuMa59~v=|RVD1%VJmCx&FLnQ)@q;pZA)1T>8&X&;%5TPA47PHX z-w0z_rrzoVtQ!N8}K-=`q=@7QHYYRGPke5w*w-Lf-bGBP;-3$u2roHP4-+As~Yx))n-lAGDg|nWkq9hp}DprE(Wcj3WV@#(}I9Q^vs zTfgrcyZXz*6F+?S=~1Lj!^m`*FuE!9_or8-QW>$W%Y1RVG`(r~)_Z9fnM}q=Zt!L~ z4^yeZS3{>U5!9FBpPC7S*CT_tRkHJKn%MsFZ5ZPPo?X1bc58@D4(Kw{Pu()TXy@(4 zUvK{L^UZklvu_4XS1r&jkbk~phi)mFtA)y`sBm{92xHmA65pHBxaWpJ6r6~qc=lYa zs4-R{!4xWGOKK98iYn5_Au6pr9#*4L#+l@8k#If{*%awsYQI6Pkz=DlnD_m0$E&x_ zp;CI=Wpr^necYYmy$&B*9y7aR<(yw;u@IKQC2ZS@8DGecS6>iOzct#dEwes746@xJFN39&|ko4 znv5&vPOZ$kldt41|5r(k_OmV#k&>U!NX@^U$EgqLzgEC({hN46uIoek-&NpGg?Dw> literal 0 HcmV?d00001 diff --git a/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.pdb b/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.pdb new file mode 100644 index 0000000000000000000000000000000000000000..7a146c73b5ca36a921e43aa281713df73a7f7319 GIT binary patch literal 11776 zcmeHN3s96*6uygyu(-yeqGIS~qM*6EJVeG63KkTJ%vrT8hL+v^X~|`O+6BvuT*c8! z$I!7RofhjFlx(RxtQKODFnHfQl@W%#GbP3V~Q6f>oZeW21 zp8`GE0;$Z~lbhe;+a7)(L6wSmEJ8wo_j$pVW1dv-UtVx5uR z)aS6a%FG(3Zw%V7Co6aD>63@FRq0|n{GFvwYKsnQca34}N@&immZ-tLg3S?m%l_bV zY?aBD0c5`42xICuYNuSzs93yqTf*tYin(EpbIuN59x50&m~mB~Kig>RyR>!D&dQYu zlatpp*X0g*@Xd_+L8URVDa+4U_MdG!RX^RO&fPw|^2G7#CGYmO%$KX9xUuQ2`mv?M&?Zj1^Yw8Z1_=0URST7y32A^9sswCovs?R>!eO7HU zr@j6St1SvEI$|n)ZP?S+Wl7KV@ipo%XqxXo_TBiO|J?B9iZk0SGoCu}qQezBDYItS z@wW9@iTOJpY6x#RR5dvy&7O8?N=(R0eN431t6&J+Y;s0Xt@>JvM9n)4nwy8YnRBw&G`C$4J z)e8*1kFLy7=+fEvl2SLNig_0?DCXcvBreft)Y%+`Zcn^MU0QB+ z7Ta_-XCW0bmlQgk8ubE=T1Oyd3Cy^7-GT~@xN^mO{7%df+>8;eIlGkXQiCa9P5hyhLViEuWfP4fJ!*D|c-XxP?Uv$1 z7|M1hfbI2?|Ls!4U^f}4{V=C!2feL~cSHU^d`g8-FXjW-Pxj#?iUU;ilXc*R*(h*@ zGG6$U%6egY(B7;+8^8wAFUs!|%q0NGBoj_WLV=sI0Pb|6!weU7RC)jnz!;+N*?5WO zNEAF7DSG<=gFXPDqqA@$vYo9{k?WiVFt!I1j_jPcB6XKqAezMrYd_}epuh#5(orlv zAkUa$1kj=#8Bj*W#6=-cR>dUQguArS%4hj4fPGg-uy4h*CV6e>C|}`q4-4S^uX^h` zAJ)>E$5ls@UdU}~eWUo*x|$!)JVQLgBgL+{Ij$m}8C-=WoQD}aZkxfu71}ws)ydoD zij1OSy5BRjJNo0)FBG^=zW-{VH>tul9iK5u`mY1bJ@S=-oR7U|1|hbOBM7kt1o)pL zXL6o0J5R0pA6s2;4~cz1i2XYD+9pC7 zaE2cuB+fU;Jn?lf@O@Cu0qx5iQgS9y6&okKB}yzNTpO{JM3X5gH8V@({(F7l>^-UQ zk`C&-p8v?)nX>6FbF c`WN&@5(*m(q6*v@3&?%+&S+P(-f0W`1OF3AFaQ7m literal 0 HcmV?d00001 diff --git a/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.pdbx b/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSource.pdbx new file mode 100644 index 0000000000000000000000000000000000000000..42a54c2a1c6aed82672bbc42fb651a1cebf3a1ad GIT binary patch literal 900 zcmZ+gqKCC-b_i76zf zF6Z!5o%t9j%-}A{#J~xpfy#j{U|?VdVxTrKpB2gn$-yv?36ckKfnYg=0=g2U2ZllJ z1hJ)o%H)Bpb3pnskY;3903`1-GJ@FF_Q-wah%XEo-|rxCws! z$H3%lCFYu&l$w%~ni8B}T9lj`oST@FqnBLF9K*;W;$bjpr`M!_<)y|iSb6pCp0$sd zU!Tpups%k`oRMFelcJE6s*safT&z%%k(j5DoS$1z1mtDr=K%$Ptc-l15)C5*1BK*_ z#G+zNUao@Dq@2uTh2)$>peC?Yyj<11Tnaz{l>!TDXew0K@^aM*0R6?s!paWxyc{Sz znAn0Vi%U{-nHd{43Zl9j=wy}{#)ds2vbz@xx_@)3ZJN2o@TF00Hs|5U*9~8R;oi*w z4kZ?b)CmW(4jb^ay>Hyp+-GQOGUe_rZ%ONEPP@emWL3?&-zJ=VeeA_<|2!`5?efh} z?$2*o`c>-0F`Kdiw+(q4W;CqjOIS2tNNA_TkqKwq8fW?5ZrW%yanQSgxP+ z<#rOw+lgwq0{15DShw#rmrJl+|2c+@X`E?STnf76;*;8q3)%l4*89MFSNZl0)2F`{ z9`AXzJs~jqNmWK3hmG@Ux%>YXxEjUmaX7&J=F$XPHoFwNFYdBzHGfSkHm+Hq*?hF_ z-RdjfHlE$uv(H4R$ZwN#_cPhuzw^UBY>5nudmhp>NB+oswKpBW^r8gBj9i?I417!+ bz@*E}zz9lmrc4~%4AN|sz))qXWMBXQsUZ76 literal 0 HcmV?d00001 diff --git a/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSourceSmall.cs b/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSourceSmall.cs new file mode 100644 index 000000000..20d7cdd54 --- /dev/null +++ b/src/Microsoft.DiaSymReader.PortablePdb.Tests/Resources/EmbeddedSourceSmall.cs @@ -0,0 +1,5 @@ +// should be less than compression threshold (200 chars) +public class Small +{ + public Small() {} +} \ No newline at end of file diff --git a/src/Microsoft.DiaSymReader.PortablePdb.Tests/TestResources.cs b/src/Microsoft.DiaSymReader.PortablePdb.Tests/TestResources.cs index bff4cde91..32f0ffca5 100644 --- a/src/Microsoft.DiaSymReader.PortablePdb.Tests/TestResources.cs +++ b/src/Microsoft.DiaSymReader.PortablePdb.Tests/TestResources.cs @@ -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 @@ -76,4 +74,28 @@ public static class MiscEmbedded private static byte[] s_dll; public static byte[] Dll => ResourceLoader.GetOrCreateResource(ref s_dll, nameof(MiscEmbedded) + ".dll"); } + + 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 PortableDllAndPdb => new KeyValuePair(PortableDll, PortablePdb); + public static KeyValuePair DllAndPdb => new KeyValuePair(Dll, Pdb); + } } diff --git a/src/Microsoft.DiaSymReader.PortablePdb.Tests/project.json b/src/Microsoft.DiaSymReader.PortablePdb.Tests/project.json index d51476f75..6ac134e0a 100644 --- a/src/Microsoft.DiaSymReader.PortablePdb.Tests/project.json +++ b/src/Microsoft.DiaSymReader.PortablePdb.Tests/project.json @@ -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", diff --git a/src/Microsoft.DiaSymReader.PortablePdb/SymDocument.cs b/src/Microsoft.DiaSymReader.PortablePdb/SymDocument.cs index 3a3275627..a1e3bf623 100644 --- a/src/Microsoft.DiaSymReader.PortablePdb/SymDocument.cs +++ b/src/Microsoft.DiaSymReader.PortablePdb/SymDocument.cs @@ -122,9 +122,17 @@ public int GetLanguageVendor(ref Guid vendor) public int GetSourceLength(out int length) { - // SymReader doesn't support embedded source. length = 0; - return HResult.E_NOTIMPL; + + BlobReader reader; + int hr = GetEmbeddedSourceBlobReader(out reader); + if (hr != HResult.S_OK) + { + return hr; + } + + length = reader.RemainingBytes; + return HResult.S_OK; } public int GetSourceRange( @@ -136,9 +144,50 @@ public int GetSourceRange( out int count, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4), Out]byte[] source) { - // SymReader doesn't support embedded source. count = 0; - return HResult.E_NOTIMPL; + + // This function used to return E_NOTIMPL in all implementations. When filling in the + // implementation, it was deemed not very useful and impractical to actually decompress + // and decode text and interpret lines and columns here. A convention was adopted that + // (0, 0, >= int.MaxValue, >= int.MaxValue) is a maximal range indicating that all + // source bytes should be returned. Anything else is rejected as invalid. This matches + // the new native behavior. + if (startLine != 0 || + startColumn != 0 || + unchecked((uint)endLine) < int.MaxValue || + unchecked((uint)endColumn) < int.MaxValue) + { + return HResult.E_INVALIDARG; + } + + if (bufferLength < 0) + { + return HResult.E_INVALIDARG; + } + + if (source == null && bufferLength > 0) + { + return HResult.E_INVALIDARG; + } + + BlobReader reader; + int hr = GetEmbeddedSourceBlobReader(out reader); + if (hr != HResult.S_OK) + { + return hr; + } + + count = Math.Min(bufferLength, reader.RemainingBytes); + if (count > 0) + { + // There is not currently a mechanism for reading from BlobReader to existing byte[] + // https://github.com/dotnet/corefx/issues/8004 tracks adding that API to corefx and + // this should be updated to use that when it's fixed. In the meantime, we are forced + // to make an extra copy here. + Array.Copy(reader.ReadBytes(count), source, count); + } + + return HResult.S_OK; } public int GetUrl( @@ -152,9 +201,41 @@ public int GetUrl( public int HasEmbeddedSource(out bool value) { - // SymReader doesn't support embedded source. - value = false; - return HResult.E_NOTIMPL; + int hr; + int length; + hr = GetSourceLength(out length); + value = length > 0; + return hr == HResult.S_FALSE ? HResult.S_OK : hr; + } + + private int GetEmbeddedSourceBlobReader(out BlobReader reader) + { + BlobHandle blobHandle = SymReader.MetadataReader.GetCustomDebugInformation(Handle, MetadataUtilities.EmbeddedSourceId); + if (blobHandle.IsNil) + { + reader = default(BlobReader); + return HResult.S_FALSE; + } + + reader = SymReader.MetadataReader.GetBlobReader(blobHandle); + if (reader.RemainingBytes < sizeof(ushort)) + { + return HResult.E_UNEXPECTED; + } + + var format = reader.ReadUInt16(); + switch (format) + { + // Note that native PDB format does not have this leading format indicator + // and native API consumer is expected to sniff for GZIP signature. + case 0: // Uncompressed + case 1: // GZIP compressed + break; + default: + return HResult.E_UNEXPECTED; + } + + return HResult.S_OK; } } } \ No newline at end of file diff --git a/src/Microsoft.DiaSymReader.PortablePdb/Utilities/MetadataUtilities.cs b/src/Microsoft.DiaSymReader.PortablePdb/Utilities/MetadataUtilities.cs index 05d1014a5..ad0d89b8d 100644 --- a/src/Microsoft.DiaSymReader.PortablePdb/Utilities/MetadataUtilities.cs +++ b/src/Microsoft.DiaSymReader.PortablePdb/Utilities/MetadataUtilities.cs @@ -17,6 +17,7 @@ internal static class MetadataUtilities // Custom Attribute kinds: public static readonly Guid MethodSteppingInformationBlobId = new Guid("54FD2AC5-E925-401A-9C2A-F94F171072F8"); public static readonly Guid VbDefaultNamespaceId = new Guid("58b2eab6-209f-4e4e-a22c-b2d0f910c782"); + public static readonly Guid EmbeddedSourceId = new Guid("0E8A571B-6926-466E-B4AD-8AB04611F5FE"); internal static int GetTypeDefOrRefOrSpecCodedIndex(EntityHandle typeHandle) {