From 00bb520b52baa03ebd784542d5ebdb01efc82881 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 10 Aug 2023 22:17:02 -0400 Subject: [PATCH 1/2] Use out-of-process disassembler with in-process toolchains when possible. --- .../Disassemblers/DisassemblyDiagnoser.cs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs index 2c6eb62f79..32abff3a2f 100644 --- a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs +++ b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs @@ -14,6 +14,8 @@ using BenchmarkDotNet.Portability; using BenchmarkDotNet.Reports; using BenchmarkDotNet.Running; +using BenchmarkDotNet.Toolchains; +using BenchmarkDotNet.Toolchains.InProcess.Emit; using BenchmarkDotNet.Toolchains.InProcess.NoEmit; using BenchmarkDotNet.Validators; @@ -71,6 +73,11 @@ public void Handle(HostSignal signal, DiagnoserActionParameters parameters) switch (signal) { + case HostSignal.AfterAll when benchmark.GetToolchain().IsInProcess: + if (!IsOutOfProcessDisassemblerSupportedForCurrentPlatform()) + throw new PlatformNotSupportedException("The current platform does not support disassembling in-process."); + results.Add(benchmark, windowsDifferentArchitectureDisassembler.Disassemble(parameters)); + break; case HostSignal.AfterAll when ShouldUseSameArchitectureDisassembler(benchmark, parameters): results.Add(benchmark, sameArchitectureDisassembler.Disassemble(parameters)); break; @@ -100,9 +107,16 @@ public IEnumerable Validate(ValidationParameters validationPara foreach (var benchmark in validationParameters.Benchmarks) { - if (benchmark.Job.Infrastructure.TryGetToolchain(out var toolchain) && toolchain is InProcessNoEmitToolchain) + if (benchmark.Job.Infrastructure.TryGetToolchain(out var toolchain) && toolchain.IsInProcess) { - yield return new ValidationError(true, "InProcessToolchain has no DisassemblyDiagnoser support", benchmark); + if (toolchain is InProcessNoEmitToolchain) + { + yield return new ValidationError(true, "InProcessNoEmitToolchain has no DisassemblyDiagnoser support", benchmark); + } + if (toolchain is InProcessEmitToolchain && !IsOutOfProcessDisassemblerSupportedForCurrentPlatform()) + { + yield return new ValidationError(true, "InProcessEmitToolchain has no DisassemblyDiagnoser support on this platform", benchmark); + } } else if (benchmark.Job.IsNativeAOT()) { @@ -155,6 +169,11 @@ private static bool ShouldUseSameArchitectureDisassembler(BenchmarkCase benchmar return false; } + private static bool IsOutOfProcessDisassemblerSupportedForCurrentPlatform() + => RuntimeInformation.IsWindows() + && RuntimeInformation.GetCurrentPlatform() is Platform.X64 or Platform.X86 + && !RuntimeInformation.IsMono && !RuntimeInformation.IsWasm && !RuntimeInformation.IsAot; + private static IEnumerable GetExporters(Dictionary results, DisassemblyDiagnoserConfig config) { if (config.ExportGithubMarkdown) From 82ac1d60317e2750a38848d1c2ea5e946fa01fcb Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 10 Aug 2023 23:27:05 -0400 Subject: [PATCH 2/2] Add test. --- .../DisassemblyDiagnoserTests.cs | 29 +++++++++++++++++++ .../XUnit/EnvRequirement.cs | 3 +- .../XUnit/EnvRequirementChecker.cs | 2 ++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/BenchmarkDotNet.IntegrationTests/DisassemblyDiagnoserTests.cs b/tests/BenchmarkDotNet.IntegrationTests/DisassemblyDiagnoserTests.cs index 49bd585709..434c9c1f8d 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/DisassemblyDiagnoserTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/DisassemblyDiagnoserTests.cs @@ -13,6 +13,8 @@ using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Portability; using BenchmarkDotNet.Tests.Loggers; +using BenchmarkDotNet.Tests.XUnit; +using BenchmarkDotNet.Toolchains.InProcess.Emit; using Xunit; using Xunit.Abstractions; @@ -105,6 +107,33 @@ public void CanDisassembleAllMethodCalls(Jit jit, Platform platform, Runtime run AssertDisassembled(disassemblyDiagnoser, $"{nameof(WithCalls.Recursive)}()"); } + // For some reason the GitHub runner with Core cannot fully disassemble, so we skip it. + [FactEnvSpecific("InProcess disassembly only supported on Windows x86/64.", EnvRequirement.WindowsOnly, EnvRequirement.X86X64Only, EnvRequirement.FullFrameworkOnly)] + [Trait(Constants.Category, Constants.BackwardCompatibilityCategory)] + public void CanDisassembleAllMethodCallsInProcess() + { + var disassemblyDiagnoser = new DisassemblyDiagnoser( + new DisassemblyDiagnoserConfig(printSource: true, maxDepth: 3)); + + var config = ManualConfig.CreateEmpty() + .AddJob(Job.Dry + .WithStrategy(RunStrategy.ColdStart) + .WithToolchain(InProcessEmitToolchain.Instance) + ) + .AddLogger(DefaultConfig.Instance.GetLoggers().ToArray()) + .AddColumnProvider(DefaultColumnProviders.Instance) + .AddDiagnoser(disassemblyDiagnoser) + .AddLogger(new OutputLogger(Output)); + + CanExecute(config); + + AssertDisassembled(disassemblyDiagnoser, $"{nameof(WithCalls.Benchmark)}(Int32)"); + AssertDisassembled(disassemblyDiagnoser, $"{nameof(WithCalls.Benchmark)}(Boolean)"); + AssertDisassembled(disassemblyDiagnoser, $"{nameof(WithCalls.Static)}()"); + AssertDisassembled(disassemblyDiagnoser, $"{nameof(WithCalls.Instance)}()"); + AssertDisassembled(disassemblyDiagnoser, $"{nameof(WithCalls.Recursive)}()"); + } + [Theory] [MemberData(nameof(GetAllJits))] [Trait(Constants.Category, Constants.BackwardCompatibilityCategory)] diff --git a/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirement.cs b/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirement.cs index cb2b1a3878..918ff438e0 100644 --- a/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirement.cs +++ b/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirement.cs @@ -8,5 +8,6 @@ public enum EnvRequirement FullFrameworkOnly, NonFullFramework, DotNetCoreOnly, - DotNetCore30Only + DotNetCore30Only, + X86X64Only } \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirementChecker.cs b/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirementChecker.cs index cbe4ac930e..e5f7822556 100644 --- a/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirementChecker.cs +++ b/tests/BenchmarkDotNet.Tests/XUnit/EnvRequirementChecker.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Runtime.InteropServices; +using BenchmarkDotNet.Environments; using BenchmarkDotNet.Jobs; using JetBrains.Annotations; using BdnRuntimeInformation = BenchmarkDotNet.Portability.RuntimeInformation; @@ -22,6 +23,7 @@ public static class EnvRequirementChecker EnvRequirement.NonFullFramework => !BdnRuntimeInformation.IsFullFramework ? null : "Non-Full .NET Framework test", EnvRequirement.DotNetCoreOnly => BdnRuntimeInformation.IsNetCore ? null : ".NET/.NET Core-only test", EnvRequirement.DotNetCore30Only => IsRuntime(RuntimeMoniker.NetCoreApp30) ? null : ".NET Core 3.0-only test", + EnvRequirement.X86X64Only => BdnRuntimeInformation.GetCurrentPlatform() is Platform.X64 or Platform.X86 ? null : "x86- or x64-only test", _ => throw new ArgumentOutOfRangeException(nameof(requirement), requirement, "Unknown value") };