Skip to content

Commit

Permalink
Skip instrumentation of module with embedded ppbd without local sourc…
Browse files Browse the repository at this point in the history
…es (#510)

* handle ppbd without local sources

* add test

* nit formatting

* check all docs existence

* update tests
  • Loading branch information
MarcoRossignoli authored and tonerdo committed Aug 14, 2019
1 parent 872e087 commit 0faaeea
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 3 deletions.
40 changes: 39 additions & 1 deletion src/coverlet.core/Helpers/InstrumentationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text.RegularExpressions;
using Coverlet.Core.Abstracts;
Expand Down Expand Up @@ -67,8 +68,9 @@ public static string[] GetCoverableModules(string module, string[] directories,
.ToArray();
}

public static bool HasPdb(string module)
public static bool HasPdb(string module, out bool embedded)
{
embedded = false;
using (var moduleStream = File.OpenRead(module))
using (var peReader = new PEReader(moduleStream))
{
Expand All @@ -80,6 +82,7 @@ public static bool HasPdb(string module)
if (codeViewData.Path == $"{Path.GetFileNameWithoutExtension(module)}.pdb")
{
// PDB is embedded
embedded = true;
return true;
}

Expand All @@ -91,6 +94,41 @@ public static bool HasPdb(string module)
}
}

public static bool EmbeddedPortablePdbHasLocalSource(string module)
{
using (FileStream moduleStream = File.OpenRead(module))
using (var peReader = new PEReader(moduleStream))
{
foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory())
{
if (entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb)
{
using (MetadataReaderProvider embeddedMetadataProvider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(entry))
{
MetadataReader metadataReader = embeddedMetadataProvider.GetMetadataReader();
foreach (DocumentHandle docHandle in metadataReader.Documents)
{
Document document = metadataReader.GetDocument(docHandle);
string docName = metadataReader.GetString(document.Name);

// We verify all docs and return false if not all are present in local
// We could have false negative if doc is not a source
// Btw check for all possible extension could be weak approach
if (!File.Exists(docName))
{
return false;
}
}
}
}
}
}

// If we don't have EmbeddedPortablePdb entry return true, for instance empty dll
// We should call this method only on embedded pdb module
return true;
}

public static void BackupOriginalModule(string module, string identifier)
{
var backupPath = GetBackupPath(module, identifier);
Expand Down
24 changes: 23 additions & 1 deletion src/coverlet.core/Instrumentation/Instrumenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,29 @@ public bool CanInstrument()
{
try
{
return InstrumentationHelper.HasPdb(_module);
if (InstrumentationHelper.HasPdb(_module, out bool embeddedPdb))
{
if (embeddedPdb)
{
if (InstrumentationHelper.EmbeddedPortablePdbHasLocalSource(_module))
{
return true;
}
else
{
_logger.LogWarning($"Unable to instrument module: {_module}, embedded pdb without local source files");
return false;
}
}
else
{
return true;
}
}
else
{
return false;
}
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public void TestGetDependenciesWithTestAssembly()
[Fact]
public void TestHasPdb()
{
Assert.True(InstrumentationHelper.HasPdb(typeof(InstrumentationHelperTests).Assembly.Location));
Assert.True(InstrumentationHelper.HasPdb(typeof(InstrumentationHelperTests).Assembly.Location, out bool embeddedPdb));
Assert.False(embeddedPdb);
}

[Fact]
Expand Down
22 changes: 22 additions & 0 deletions test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Reflection;

using Coverlet.Core.Helpers;
using Coverlet.Core.Logging;
using Coverlet.Core.Samples.Tests;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -231,5 +232,26 @@ public void TestInstrument_NetStandardAwareAssemblyResolver_FromFolder()
// We check if final netstandard.dll resolved is local folder one and not "official" netstandard.dll
Assert.Equal(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "netstandard.dll"), Path.GetFullPath(resolved.MainModule.FileName));
}

[Fact]
public void SkipEmbeddedPpdbWithoutLocalSource()
{
string xunitDll = Directory.GetFiles(Directory.GetCurrentDirectory(), "xunit.*.dll").First();
var loggerMock = new Mock<ILogger>();
Instrumenter instrumenter = new Instrumenter(xunitDll, "_xunit_instrumented", Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false, loggerMock.Object);
Assert.True(InstrumentationHelper.HasPdb(xunitDll, out bool embedded));
Assert.True(embedded);
Assert.False(instrumenter.CanInstrument());
loggerMock.Verify(l => l.LogWarning(It.IsAny<string>()));

// Default case
string coverletCoreDll = Directory.GetFiles(Directory.GetCurrentDirectory(), "coverlet.core.dll").First();
instrumenter = new Instrumenter(coverletCoreDll, "_coverlet_core_instrumented", Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>(), false, loggerMock.Object);
Assert.True(InstrumentationHelper.HasPdb(coverletCoreDll, out embedded));
Assert.False(embedded);
Assert.True(instrumenter.CanInstrument());
loggerMock.VerifyNoOtherCalls();
}

}
}

0 comments on commit 0faaeea

Please sign in to comment.