using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Threading; using Microsoft.Diagnostics.Runtime; using Microsoft.Diagnostics.Runtime.Interop; using WinSW.Native; using Xunit; namespace WinSW.Tests.Util { internal sealed class InterProcessCodeCoverageSession : IDebugEventCallbacks { private readonly Type trackerType; private readonly FieldInfo hitsField; private readonly IDebugControl control; private readonly DataTarget target; private readonly Thread thread; private List exceptions; private bool exited; internal InterProcessCodeCoverageSession(string serviceName) { var trackerType = this.trackerType = typeof(Program).Assembly.GetTypes().Single(type => type.Namespace == "Coverlet.Core.Instrumentation.Tracker"); var hitsField = this.hitsField = trackerType.GetField("HitsArray", BindingFlags.Public | BindingFlags.Static); Assert.NotNull(hitsField); using var scm = ServiceManager.Open(ServiceApis.ServiceManagerAccess.Connect); using var sc = scm.OpenService(serviceName, ServiceApis.ServiceAccess.QueryStatus); int processId = sc.ProcessId; Assert.True(processId >= 0); var guid = new Guid("27fe5639-8407-4f47-8364-ee118fb08ac8"); int hr = Native.DebugCreate(guid, out object unknown); AssertEx.Succeeded(hr); var client = (IDebugClient)unknown; this.control = (IDebugControl)unknown; hr = client.AttachProcess(0, (uint)processId, DEBUG_ATTACH.DEFAULT); AssertEx.Succeeded(hr); hr = client.SetEventCallbacks(this); AssertEx.Succeeded(hr); #pragma warning disable CA1416 // Validate platform compatibility IntPtr pointer = Marshal.GetIUnknownForObject(client); Assert.Equal(1, Marshal.Release(pointer)); #pragma warning restore CA1416 // Validate platform compatibility target = DataTarget.CreateFromDbgEng(pointer); var thread = this.thread = new Thread(() => { try { using (this.target) { do { int hr = this.control.WaitForEvent(DEBUG_WAIT.DEFAULT, 0xffffffff); AssertEx.Succeeded(hr); } while (!this.exited); } } catch (Exception e) { (this.exceptions ??= new List()).Add(e); } }); thread.Start(); } /// internal void Wait() { this.thread.Join(); if (this.exceptions != null) { throw new AggregateException(this.exceptions); } } int IDebugEventCallbacks.GetInterestMask(out DEBUG_EVENT Mask) { Mask = DEBUG_EVENT.EXIT_PROCESS; return 0; } int IDebugEventCallbacks.Breakpoint(IDebugBreakpoint Bp) { throw new NotImplementedException(); } int IDebugEventCallbacks.Exception(in EXCEPTION_RECORD64 Exception, uint FirstChance) { throw new NotImplementedException(); } int IDebugEventCallbacks.CreateThread(ulong Handle, ulong DataOffset, ulong StartOffset) { throw new NotImplementedException(); } int IDebugEventCallbacks.ExitThread(uint ExitCode) { throw new NotImplementedException(); } int IDebugEventCallbacks.CreateProcess(ulong ImageFileHandle, ulong Handle, ulong BaseOffset, uint ModuleSize, string ModuleName, string ImageName, uint CheckSum, uint TimeDateStamp, ulong InitialThreadHandle, ulong ThreadDataOffset, ulong StartOffset) { throw new NotImplementedException(); } int IDebugEventCallbacks.ExitProcess(uint ExitCode) { this.exited = true; try { using var runtime = this.target.ClrVersions.Single().CreateRuntime(); var module = runtime.EnumerateModules().First(module => module.Name == typeof(Program).Assembly.Location); var type = module.GetTypeByName(this.trackerType.FullName); var field = type.GetStaticFieldByName(this.hitsField.Name); var array = field.ReadObject(runtime.AppDomains.Single()).AsArray(); int[] hits = (int[])this.hitsField.GetValue(null); int[] values = array.ReadValues(0, hits.Length); for (int i = 0; i < hits.Length; i++) { hits[i] += values[i]; } } catch (Exception e) { (this.exceptions ??= new List()).Add(e); } return (int)DEBUG_STATUS.BREAK; } int IDebugEventCallbacks.LoadModule(ulong ImageFileHandle, ulong BaseOffset, uint ModuleSize, string ModuleName, string ImageName, uint CheckSum, uint TimeDateStamp) { throw new NotImplementedException(); } int IDebugEventCallbacks.UnloadModule(string ImageBaseName, ulong BaseOffset) { throw new NotImplementedException(); } int IDebugEventCallbacks.SystemError(uint Error, uint Level) { throw new NotImplementedException(); } int IDebugEventCallbacks.SessionStatus(DEBUG_SESSION Status) { throw new NotImplementedException(); } int IDebugEventCallbacks.ChangeDebuggeeState(DEBUG_CDS Flags, ulong Argument) { throw new NotImplementedException(); } int IDebugEventCallbacks.ChangeEngineState(DEBUG_CES Flags, ulong Argument) { throw new NotImplementedException(); } int IDebugEventCallbacks.ChangeSymbolState(DEBUG_CSS Flags, ulong Argument) { throw new NotImplementedException(); } private static class Native { [DllImport("dbgeng.dll")] internal static extern int DebugCreate(in Guid InterfaceId, [MarshalAs(UnmanagedType.IUnknown)] out object Interface); } } }