From 5417eb461e62d4f2d8b1739785abf2af6da9bfea Mon Sep 17 00:00:00 2001 From: Maxime Poulain Date: Sat, 6 May 2023 03:41:51 +0200 Subject: [PATCH] Fix broken Benchmarks This commit fixes errors when running a benchmark - added PingHandler to handles Ping request - correct the DotTraceDiagnoser in order to use dotTrace executable --- test/MediatR.Benchmarks/Benchmarks.cs | 11 +- test/MediatR.Benchmarks/DotTraceDiagnoser.cs | 132 +++++++++++------- .../GenericPipelineBehavior.cs | 2 +- .../GenericRequestPostProcessor.cs | 2 +- .../GenericRequestPreProcessor.cs | 1 + .../MediatR.Benchmarks.csproj | 6 +- test/MediatR.Benchmarks/Ping.cs | 8 ++ test/MediatR.Benchmarks/Pinged.cs | 10 ++ 8 files changed, 106 insertions(+), 66 deletions(-) diff --git a/test/MediatR.Benchmarks/Benchmarks.cs b/test/MediatR.Benchmarks/Benchmarks.cs index ae82f2ad..849066a2 100644 --- a/test/MediatR.Benchmarks/Benchmarks.cs +++ b/test/MediatR.Benchmarks/Benchmarks.cs @@ -1,7 +1,6 @@ using System.IO; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; -using MediatR.Pipeline; using Microsoft.Extensions.DependencyInjection; namespace MediatR.Benchmarks @@ -20,11 +19,11 @@ public void GlobalSetup() services.AddSingleton(TextWriter.Null); - services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining(typeof(Ping))); - - services.AddScoped(typeof(IPipelineBehavior<,>), typeof(GenericPipelineBehavior<,>)); - services.AddScoped(typeof(IRequestPreProcessor<>), typeof(GenericRequestPreProcessor<>)); - services.AddScoped(typeof(IRequestPostProcessor<,>), typeof(GenericRequestPostProcessor<,>)); + services.AddMediatR(cfg => + { + cfg.RegisterServicesFromAssemblyContaining(typeof(Ping)); + cfg.AddOpenBehavior(typeof(GenericPipelineBehavior<,>)); + }); var provider = services.BuildServiceProvider(); diff --git a/test/MediatR.Benchmarks/DotTraceDiagnoser.cs b/test/MediatR.Benchmarks/DotTraceDiagnoser.cs index 38d66647..65999eeb 100644 --- a/test/MediatR.Benchmarks/DotTraceDiagnoser.cs +++ b/test/MediatR.Benchmarks/DotTraceDiagnoser.cs @@ -29,12 +29,14 @@ public DotTraceDiagnoserAttribute() internal sealed class DotTraceDiagnoser : IDiagnoser { - private Process _process; - private string _saveLocation; + private const string DotTraceExecutableNotFoundErrorMessage = "dotTrace executable was not found. " + + "Make sure it is part of the PATH or install JetBrains.dotTrace.GlobalTools"; + + private readonly string _saveLocation; public DotTraceDiagnoser() { - _saveLocation = $"C:\\temp\\MyProject\\{DateTimeOffset.Now.UtcDateTime:yyyyMMddTHHmmss}.bench.dtp"; + _saveLocation = $"C:\\temp\\MediatR\\{DateTimeOffset.Now.UtcDateTime:yyyy-MM-dd-HH_mm_ss}.bench.dtp"; } /// @@ -43,60 +45,58 @@ public DotTraceDiagnoser() /// public void Handle(HostSignal signal, DiagnoserActionParameters parameters) { - switch (signal) + if (signal != HostSignal.BeforeActualRun) + { + return; + } + + try + { + if (!CanRunDotTrace()) + { + Console.WriteLine(DotTraceExecutableNotFoundErrorMessage); + return; + } + + // The directory must exist or an error is thrown by dotTrace. + Directory.CreateDirectory(_saveLocation); + RunDotTrace(parameters); + } + catch (Exception e) { - case HostSignal.BeforeActualRun: - try - { - var dotTracePath = Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - @"JetBrains\Installations\dotTrace192\ConsoleProfiler.exe"); - var startInfo = new ProcessStartInfo( - dotTracePath, - $"attach {parameters.Process.Id} --save-to={_saveLocation} --profiling-type=Sampling") - { - RedirectStandardError - = true, - RedirectStandardOutput = true, - WindowStyle = ProcessWindowStyle.Normal, - UseShellExecute = false, - }; - Console.WriteLine(startInfo.FileName); - Console.WriteLine(startInfo.Arguments); - _process = new Process - { - StartInfo = startInfo - }; - _process.ErrorDataReceived += (sender, eventArgs) => Console.Error.WriteLine(eventArgs.Data); - _process.OutputDataReceived += (sender, eventArgs) => Console.WriteLine(eventArgs.Data); - _process.Start(); - _process.BeginErrorReadLine(); - _process.BeginOutputReadLine(); - _process.Exited += (sender, args) => { _process.Dispose(); }; - } - catch (Exception e) - { - Console.Error.WriteLine(e.StackTrace); - throw; - } - break; - case HostSignal.AfterActualRun: - break; - case HostSignal.BeforeAnythingElse: - break; - case HostSignal.AfterAll: - break; - case HostSignal.SeparateLogic: - break; - case HostSignal.BeforeProcessStart: - break; - case HostSignal.AfterProcessExit: - break; - default: - throw new ArgumentOutOfRangeException(nameof(signal), signal, null); + Console.Error.WriteLine(e.ToString()); + throw; } } + private void RunDotTrace(DiagnoserActionParameters parameters) + { + var dotTrace = new Process + { + StartInfo = PrepareProcessStartInfo(parameters) + }; + dotTrace.ErrorDataReceived += (sender, eventArgs) => Console.Error.WriteLine(eventArgs.Data); + dotTrace.OutputDataReceived += (sender, eventArgs) => Console.WriteLine(eventArgs.Data); + dotTrace.Start(); + dotTrace.BeginErrorReadLine(); + dotTrace.BeginOutputReadLine(); + dotTrace.Exited += (sender, args) => dotTrace.Dispose(); + } + + private ProcessStartInfo PrepareProcessStartInfo(DiagnoserActionParameters parameters) + { + return new ProcessStartInfo( + "dottrace", + $"attach {parameters.Process.Id} --save-to={_saveLocation}") + { + RedirectStandardError = true, + RedirectStandardOutput = true, + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = false, + CreateNoWindow = true, + }; + } + /// public IEnumerable ProcessResults(DiagnoserResults results) => Enumerable.Empty(); @@ -114,5 +114,29 @@ public IEnumerable Validate(ValidationParameters validationPara public IEnumerable Exporters => Enumerable.Empty(); public IEnumerable Analysers { get; } = Enumerable.Empty(); + + private static bool CanRunDotTrace() + { + try + { + var startInfo = new ProcessStartInfo("dottrace") + { + RedirectStandardError = true, + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + + using var process = new Process { StartInfo = startInfo }; + process.Start(); + process.WaitForExit(); + + return true; + } + catch (Exception) + { + return false; + } + } } -} \ No newline at end of file +} diff --git a/test/MediatR.Benchmarks/GenericPipelineBehavior.cs b/test/MediatR.Benchmarks/GenericPipelineBehavior.cs index 26ffd59e..52577095 100644 --- a/test/MediatR.Benchmarks/GenericPipelineBehavior.cs +++ b/test/MediatR.Benchmarks/GenericPipelineBehavior.cs @@ -5,7 +5,7 @@ namespace MediatR.Benchmarks { public class GenericPipelineBehavior : IPipelineBehavior - where TRequest : IRequest + where TRequest : notnull { private readonly TextWriter _writer; diff --git a/test/MediatR.Benchmarks/GenericRequestPostProcessor.cs b/test/MediatR.Benchmarks/GenericRequestPostProcessor.cs index f8ae0f53..c657a07a 100644 --- a/test/MediatR.Benchmarks/GenericRequestPostProcessor.cs +++ b/test/MediatR.Benchmarks/GenericRequestPostProcessor.cs @@ -6,7 +6,7 @@ namespace MediatR.Benchmarks { public class GenericRequestPostProcessor : IRequestPostProcessor - where TRequest : IRequest + where TRequest : notnull { private readonly TextWriter _writer; diff --git a/test/MediatR.Benchmarks/GenericRequestPreProcessor.cs b/test/MediatR.Benchmarks/GenericRequestPreProcessor.cs index a2079c4b..ee253100 100644 --- a/test/MediatR.Benchmarks/GenericRequestPreProcessor.cs +++ b/test/MediatR.Benchmarks/GenericRequestPreProcessor.cs @@ -6,6 +6,7 @@ namespace MediatR.Benchmarks { public class GenericRequestPreProcessor : IRequestPreProcessor + where TRequest : notnull { private readonly TextWriter _writer; diff --git a/test/MediatR.Benchmarks/MediatR.Benchmarks.csproj b/test/MediatR.Benchmarks/MediatR.Benchmarks.csproj index 0232c140..1946994d 100644 --- a/test/MediatR.Benchmarks/MediatR.Benchmarks.csproj +++ b/test/MediatR.Benchmarks/MediatR.Benchmarks.csproj @@ -2,8 +2,6 @@ net6.0 Exe - - 7.3 AnyCPU @@ -14,8 +12,8 @@ Release - - + + diff --git a/test/MediatR.Benchmarks/Ping.cs b/test/MediatR.Benchmarks/Ping.cs index c1f40b3f..59566af7 100644 --- a/test/MediatR.Benchmarks/Ping.cs +++ b/test/MediatR.Benchmarks/Ping.cs @@ -1,7 +1,15 @@ +using System.Threading; +using System.Threading.Tasks; + namespace MediatR.Benchmarks { public class Ping : IRequest { public string Message { get; set; } } + + public class PingHandler : IRequestHandler + { + public Task Handle(Ping request, CancellationToken cancellationToken) => Task.CompletedTask; + } } \ No newline at end of file diff --git a/test/MediatR.Benchmarks/Pinged.cs b/test/MediatR.Benchmarks/Pinged.cs index 09dada08..6ee38429 100644 --- a/test/MediatR.Benchmarks/Pinged.cs +++ b/test/MediatR.Benchmarks/Pinged.cs @@ -1,7 +1,17 @@ +using System.Threading; +using System.Threading.Tasks; + namespace MediatR.Benchmarks { public class Pinged : INotification { + } + public class PingedHandler : INotificationHandler + { + public Task Handle(Pinged notification, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } } } \ No newline at end of file