SteveGilham / altcover

Cross-platform coverage gathering and processing tool set for dotnet/.Net Framework and Mono
MIT License
498 stars 18 forks source link

UWP program instrumentation phase fails #187

Closed robvdnieuw closed 4 months ago

robvdnieuw commented 1 year ago

We have a project that depends on a UWP winmd file.

C:\Program Files (x86)\Windows Kits\10\References\10.0.22000.0\Windows.Foundation.FoundationContract\4.0.0.0\Windows.Foundation.FoundationContract.winmd true
    <Reference Include="Windows.Foundation.UniversalApiContract">
        <HintPath>C:\Program Files (x86)\Windows Kits\10\References\10.0.22000.0\Windows.Foundation.UniversalApiContract\14.0.0.0\Windows.Foundation.UniversalApiContract.winmd</HintPath>
        <IsWinMDFile>true</IsWinMDFile>
    </Reference>

It builds ok.

During instrumentation we get the error following error.

Failed to resolve assembly: 'Windows.Foundation.UniversalApiContract, Version=14.0.0.0, Culture=neutral, PublicKeyToken=null'

resulting into ERROR *** Instrumentation phase failed

what can we do as a workaround?

SteveGilham commented 1 year ago

This will be happening while rewriting instrumented assemblies - Mono.Cecil insists on resolving all dependencies during that process, rather than taking the original assembly's references on trust. The issue seems to be that it isn't detecting the WindowsRT context, and so be expecting .winmd assemblies; but I'm not yet sure why.

The workround should be simply to add the .winmd file as an explicit dependency

--dependency=[full path to file]

or equivalents for dotnet test or scripting, and where the full path can include environment variables to expand on the fly.

SteveGilham commented 1 year ago

This is a speculative build that should resolve the issue without needing the workround above, if you'd like to give it a try:

altcover.8.6.64-pre-g6ef83946e7.nupkg.zip

robvdnieuw commented 1 year ago

Hi Steve,

with the dependencies and also the preview version we still have the same error.

Commandline: .nuget\packages\altcover\8.6.64-pre-g6ef83946e7\tools\net472\AltCover.exe" --inputDirectory="C:\agent01_work\2973\b\Release_x64\ctlrsvc_sh\UnitTests" --outputDirectory="C:\agent01_work\2973\s\ctlrsvc_sh.coes\AltCover-ctlrsvc_sh-Cos.UnitTests" --report="C:\agent01_work\2973\a\CodeCoverageResults\ctlrsvc_sh\NonNetCoreAssemblies-Cos.UnitTests-x64-raw.xml" --reportFormat=OpenCover -log:Info --dependency="C:\Program Files (x86)\Windows Kits\10\References\10.0.22000.0\Windows.Foundation.FoundationContract\4.0.0.0\Windows.Foundation.FoundationContract.winmd" --dependency="C:\Program Files (x86)\Windows Kits\10\References\10.0.22000.0\Windows.Foundation.UniversalApiContract\14.0.0.0\Windows.Foundation.UniversalApiContract.winmd" --dependency="C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Facade\Windows.WinMD" --save"

AltCover-2023-06-26--11-48-28.log

SteveGilham commented 1 year ago

One thing that stands out to me from that command line is that without any filters applied, coverage instrumentation will be applied to all the assemblies in the output folder, not just the ones you've written. As well as providing information of marginal use, that will also spread the amount of dependency resolution needed while rebuilding and relinking the system assemblies involved.

Could you try adding a suitable set of --assemblyFilter values to restrict coverage to your own code? If all your assemblies have a common name part (as in MyCompany.Application.exe and MyConpany.Support.dll) you can use the "exclude all but" trick and just write (in this case) --assemblyFilter=?MyCompany

Meanwhile, I shall continue trying to create a local repro case - having never before had cause to touch UWP, this is being a learning experience for me.

SteveGilham commented 1 year ago

Having tried several different combinations of UWP code from the samples repo, making sure that both contract assemblies are referenced by at least one of the locally built assemblies, I have been unable to repro this issue using the latest release build - the instrumentation process completes, and examining the output assemblies shows it has done the expected code weaving.

This is all using a simple path\to\AltCover.exe -i path\to\UWP\binaries command line, no filtering, no dependency declarations.

These all being self-contained examples, the one thing that is lacking is a complicated dependency stack possibly including third-party component libraries also binding to the UWP API contract assemblies - otherwise it all comes down to the mysterious "environmental differences" behind the "works on my machine" effect.

64J0 commented 1 year ago

I'm also facing this problem.

In my case, it's related to this tool trying to open the same file twice, apparently. Check the logs:

Coverage Report: /home/redacted/coverage.xml

      /home/redacted/tests/bin/Debug/net6.0/__Instrumented_Tests/Microsoft.IO.RecyclableMemoryStream.dll
                  <=  Microsoft.IO.RecyclableMemoryStream, Version=2.2.0.0, Culture=neutral, PublicKeyToken=null
      /home/redacted/tests/bin/Debug/net6.0/__Instrumented_Tests/FSharp.Core.dll
                  <=  FSharp.Core, Version=7.0.0.0, Culture=neutral, PublicKeyToken=null
      /home/redacted/tests/bin/Debug/net6.0/__Instrumented_Tests/AltCover.Monitor.dll
                  <=  AltCover.Monitor, Version=8.6.0.0, Culture=neutral, PublicKeyToken=null
      /home/redacted/tests/bin/Debug/net6.0/__Instrumented_Tests/Shared.dll
                  <=  Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
      /home/redacted/tests/bin/Debug/net6.0/__Instrumented_Tests/Serializer.dll
                  <=  Serializer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
      /home/redacted/tests/bin/Debug/net6.0/__Instrumented_Tests/Serializer.dll
                  <=  Serializer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

/home/.nuget/packages/altcover/8.6.68/build/netstandard2.0/AltCover.targets(83,5): error :  [/home/redacted/tests/Tests.fsproj]
/home/.nuget/packages/altcover/8.6.68/build/netstandard2.0/AltCover.targets(83,5): error : ERROR *** Instrumentation phase failed [/home/redacted/tests/Tests.fsproj]
/home/.nuget/packages/altcover/8.6.68/build/netstandard2.0/AltCover.targets(83,5): error :  [/home/redacted/tests/Tests.fsproj]
/home/.nuget/packages/altcover/8.6.68/build/netstandard2.0/AltCover.targets(83,5): error :  [/home/redacted/tests/Tests.fsproj]
/home/.nuget/packages/altcover/8.6.68/build/netstandard2.0/AltCover.targets(83,5): error : The process cannot access the file '/home/redacted/tests/bin/Debug/net6.0/__Instrumented_Tests/Serializer.dll' because it is being used by another process. [/home/redacted/tests/Tests.fsproj]
/home/.nuget/packages/altcover/8.6.68/build/netstandard2.0/AltCover.targets(83,5): error :  [/home/redacted/tests/Tests.fsproj]
/home/.nuget/packages/altcover/8.6.68/build/netstandard2.0/AltCover.targets(83,5): error : Details written to /home/redacted/tests/bin/Debug/net6.0/__Instrumented_Tests/AltCover-2023-08-08--14-56-05.log [/home/redacted/tests/Tests.fsproj]

Notice that /home/redacted/tests/bin/Debug/net6.0/__Instrumented_Tests/Serializer.dll appears twice.

And the log information:

System.IO.IOException: The process cannot access the file '/home/redacted/tests/bin/Debug/net6.0/__Instrumented_Tests/Serializer.dll' because it is being used by another process.
   at Microsoft.Win32.SafeHandles.SafeFileHandle.Init(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize)
   at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize)
   at System.IO.FileSystem.CopyFile(String sourceFullPath, String destFullPath, Boolean overwrite)
   at System.IO.File.Copy(String sourceFileName, String destFileName, Boolean overwrite)
   at AltCover.Instrument.I.writeAssemblies@825.Invoke(String p) in /_//AltCover.Engine/Instrument.fs:line 836
   at Microsoft.FSharp.Collections.SeqModule.Iterate[T](FSharpFunc`2 action, IEnumerable`1 source) in D:\a\_work\1\s\src\FSharp.Core\seq.fs:line 632
   at AltCover.Instrument.I.visitAfterAssembly(InstrumentContext state, AssemblyEntry assembly) in /_//AltCover.Engine/Instrument.fs:line 1084
   at AltCover.Instrument.I.instrumentationVisitorWrapper(FSharpFunc`2 core, InstrumentContext state, Node node) in /_//AltCover.Engine/Instrument.fs:line 1423
   at AltCover.Visitor.stateful@1645-1.Invoke(T node) in /_//AltCover.Engine/Visitor.fs:line 1647
   at Microsoft.FSharp.Primitives.Basics.List.mapToFreshConsTail[a,b](FSharpList`1 cons, FSharpFunc`2 f, FSharpList`1 x) in D:\a\_work\1\s\src\FSharp.Core\local.fs:line 236
   at Microsoft.FSharp.Primitives.Basics.List.map[T,TResult](FSharpFunc`2 mapping, FSharpList`1 x) in D:\a\_work\1\s\src\FSharp.Core\local.fs:line 247
   at Microsoft.FSharp.Collections.SeqModule.Fold[T,TState](FSharpFunc`2 folder, TState state, IEnumerable`1 source) in D:\a\_work\1\s\src\FSharp.Core\seq.fs:line 912
   at AltCover.Visitor.visit(IEnumerable`1 visitors, IEnumerable`1 assemblies) in /_//AltCover.Engine/Visitor.fs:line 1633
   at AltCover.Main.I.result@734.Invoke(Unit unitVar0) in /_//AltCover.Engine/Main.fs:line 756
   at AltCover.PathOperation.DoPathOperation[TAny](FSharpFunc`2 operation, FSharpFunc`2 handle) in /_//AltCover.Engine/Output.fs:line 21
TargetSite = 
Void Init(System.String, System.IO.FileMode, System.IO.FileAccess, System.IO.FileShare, System.IO.FileOptions, Int64)
Data = 
seq []
InnerException = 
<null>
HelpLink = 
<null>
Source = 
"System.Private.CoreLib"
HResult = 
11
64J0 commented 1 year ago

In my case, it worked after removing the /home/redacted/tests/bin folder entirely.

SteveGilham commented 1 year ago

ERROR *** Instrumentation phase failed is a generic message; it's the exception information that conveys where the problem lies.

The original issue is one of resolving extended dependencies in UWP assemblies while writing the instrumented assemblies, one that despite having made various attempts to reproduce I have not so far managed to. I'll take this opportunity to was whether @robvdnieuw has a stand-alone minimal repro that could be shared, so the issue could be progressed.

This new report looks like a situation where one of the instrumented assemblies already existed from a previous test run, and whatever was using it has not yet fully released it. The scorched earth approach of deleting the whole output directory is one way of ensuring that there are no lingering attachments on a file.

SteveGilham commented 5 months ago

@robvdnieuw Any updates/any simple repro case available? Or shall I close this issue as stale?