diff --git a/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs b/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs index 2bdc94d730..7ea7cbb0b7 100644 --- a/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs +++ b/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs @@ -124,7 +124,7 @@ internal static void SetEnvironmentVariables(this ProcessStartInfo start, Benchm // we have to set "COMPlus_GC*" environment variables as documented in // https://docs.microsoft.com/en-us/dotnet/core/run-time-config/garbage-collector if (benchmarkCase.Job.Infrastructure.Toolchain is CoreRunToolchain _) - start.SetCoreRunEnvironmentVariables(benchmarkCase); + start.SetCoreRunEnvironmentVariables(benchmarkCase, resolver); if (!benchmarkCase.Job.HasValue(EnvironmentMode.EnvironmentVariablesCharacteristic)) return; @@ -229,19 +229,21 @@ private static int RunProcessAndIgnoreOutput(string fileName, string arguments, } } - private static void SetCoreRunEnvironmentVariables(this ProcessStartInfo start, BenchmarkCase benchmarkCase) + private static void SetCoreRunEnvironmentVariables(this ProcessStartInfo start, BenchmarkCase benchmarkCase, IResolver resolver) { var gcMode = benchmarkCase.Job.Environment.Gc; - if (!gcMode.HasChanges) - return; // do nothing for the default settings - - start.EnvironmentVariables["COMPlus_gcServer"] = gcMode.Server ? "1" : "0"; - start.EnvironmentVariables["COMPlus_gcConcurrent"] = gcMode.Concurrent ? "1" : "0"; - start.EnvironmentVariables["COMPlus_GCCpuGroup"] = gcMode.CpuGroups ? "1" : "0"; - start.EnvironmentVariables["COMPlus_gcAllowVeryLargeObjects"] = gcMode.AllowVeryLargeObjects ? "1" : "0"; - start.EnvironmentVariables["COMPlus_GCRetainVM"] = gcMode.RetainVm ? "1" : "0"; - start.EnvironmentVariables["COMPlus_GCNoAffinitize"] = gcMode.NoAffinitize ? "1" : "0"; + start.EnvironmentVariables["COMPlus_gcServer"] = gcMode.ResolveValue(GcMode.ServerCharacteristic, resolver) ? "1" : "0"; + start.EnvironmentVariables["COMPlus_gcConcurrent"] = gcMode.ResolveValue(GcMode.ConcurrentCharacteristic, resolver) ? "1" : "0"; + + if (gcMode.HasValue(GcMode.CpuGroupsCharacteristic)) + start.EnvironmentVariables["COMPlus_GCCpuGroup"] = gcMode.ResolveValue(GcMode.CpuGroupsCharacteristic, resolver) ? "1" : "0"; + if (gcMode.HasValue(GcMode.AllowVeryLargeObjectsCharacteristic)) + start.EnvironmentVariables["COMPlus_gcAllowVeryLargeObjects"] = gcMode.ResolveValue(GcMode.AllowVeryLargeObjectsCharacteristic, resolver) ? "1" : "0"; + if (gcMode.HasValue(GcMode.RetainVmCharacteristic)) + start.EnvironmentVariables["COMPlus_GCRetainVM"] = gcMode.ResolveValue(GcMode.RetainVmCharacteristic, resolver) ? "1" : "0"; + if (gcMode.HasValue(GcMode.NoAffinitizeCharacteristic)) + start.EnvironmentVariables["COMPlus_GCNoAffinitize"] = gcMode.ResolveValue(GcMode.NoAffinitizeCharacteristic, resolver) ? "1" : "0"; if (gcMode.HasValue(GcMode.HeapAffinitizeMaskCharacteristic)) start.EnvironmentVariables["COMPlus_GCHeapAffinitizeMask"] = gcMode.HeapAffinitizeMask.ToString("X"); if (gcMode.HasValue(GcMode.HeapCountCharacteristic)) diff --git a/src/BenchmarkDotNet/Toolchains/AppConfigGenerator.cs b/src/BenchmarkDotNet/Toolchains/AppConfigGenerator.cs index aa35f039f6..c1db452d2b 100644 --- a/src/BenchmarkDotNet/Toolchains/AppConfigGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/AppConfigGenerator.cs @@ -100,15 +100,15 @@ private static void GenerateJitSettings(XmlDocument xmlDocument, XmlNode runtime private static void GenerateGCSettings(XmlDocument xmlDocument, XmlNode runtimeElement, GcMode gcMode, IResolver resolver) { - if (!gcMode.HasChanges) - return; - CreateNodeWithAttribute(xmlDocument, runtimeElement, "gcConcurrent", "enabled", gcMode.ResolveValue(GcMode.ConcurrentCharacteristic, resolver).ToLowerCase()); CreateNodeWithAttribute(xmlDocument, runtimeElement, "gcServer", "enabled", gcMode.ResolveValue(GcMode.ServerCharacteristic, resolver).ToLowerCase()); - CreateNodeWithAttribute(xmlDocument, runtimeElement, "GCCpuGroup", "enabled", gcMode.ResolveValue(GcMode.CpuGroupsCharacteristic, resolver).ToLowerCase()); - CreateNodeWithAttribute(xmlDocument, runtimeElement, "gcAllowVeryLargeObjects", "enabled", gcMode.ResolveValue(GcMode.AllowVeryLargeObjectsCharacteristic, resolver).ToLowerCase()); - CreateNodeWithAttribute(xmlDocument, runtimeElement, "GCNoAffinitize", "enabled", gcMode.ResolveValue(GcMode.NoAffinitizeCharacteristic, resolver).ToLowerCase()); + if (gcMode.HasValue(GcMode.CpuGroupsCharacteristic)) + CreateNodeWithAttribute(xmlDocument, runtimeElement, "GCCpuGroup", "enabled", gcMode.ResolveValue(GcMode.CpuGroupsCharacteristic, resolver).ToLowerCase()); + if (gcMode.HasValue(GcMode.AllowVeryLargeObjectsCharacteristic)) + CreateNodeWithAttribute(xmlDocument, runtimeElement, "gcAllowVeryLargeObjects", "enabled", gcMode.ResolveValue(GcMode.AllowVeryLargeObjectsCharacteristic, resolver).ToLowerCase()); + if (gcMode.HasValue(GcMode.NoAffinitizeCharacteristic)) + CreateNodeWithAttribute(xmlDocument, runtimeElement, "GCNoAffinitize", "enabled", gcMode.ResolveValue(GcMode.NoAffinitizeCharacteristic, resolver).ToLowerCase()); if (gcMode.HasValue(GcMode.HeapAffinitizeMaskCharacteristic)) CreateNodeWithAttribute(xmlDocument, runtimeElement, "GCHeapAffinitizeMask", "enabled", gcMode.ResolveValue(GcMode.HeapAffinitizeMaskCharacteristic, resolver).ToString()); if (gcMode.HasValue(GcMode.HeapCountCharacteristic)) diff --git a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs index 1b490bd4df..821faf574f 100644 --- a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs @@ -83,16 +83,15 @@ protected override void GenerateProject(BuildPartition buildPartition, Artifacts [PublicAPI] protected virtual string GetRuntimeSettings(GcMode gcMode, IResolver resolver) { - if (!gcMode.HasChanges) - return string.Empty; - - return new StringBuilder(80) + var builder = new StringBuilder(80) .AppendLine("") - .AppendLine($"{gcMode.ResolveValue(GcMode.ServerCharacteristic, resolver).ToLowerCase()}") - .AppendLine($"{gcMode.ResolveValue(GcMode.ConcurrentCharacteristic, resolver).ToLowerCase()}") - .AppendLine($"{gcMode.ResolveValue(GcMode.RetainVmCharacteristic, resolver).ToLowerCase()}") - .AppendLine("") - .ToString(); + .AppendLine($"{gcMode.ResolveValue(GcMode.ServerCharacteristic, resolver).ToLowerCase()}") + .AppendLine($"{gcMode.ResolveValue(GcMode.ConcurrentCharacteristic, resolver).ToLowerCase()}"); + + if (gcMode.HasValue(GcMode.RetainVmCharacteristic)) + builder.AppendLine($"{gcMode.ResolveValue(GcMode.RetainVmCharacteristic, resolver).ToLowerCase()}"); + + return builder.AppendLine("").ToString(); } // the host project or one of the .props file that it imports might contain some custom settings that needs to be copied, sth like diff --git a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj index 5afd242d86..e213a47f05 100755 --- a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj +++ b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj @@ -12,6 +12,7 @@ pdbonly true $(NoWarn);CA2007 + true diff --git a/tests/BenchmarkDotNet.IntegrationTests/GcModeTests.cs b/tests/BenchmarkDotNet.IntegrationTests/GcModeTests.cs index 75769e8b77..123218d7ff 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/GcModeTests.cs +++ b/tests/BenchmarkDotNet.IntegrationTests/GcModeTests.cs @@ -18,10 +18,14 @@ public class GcModeTests : BenchmarkTestExecutor private IConfig CreateConfig(GcMode gc) => ManualConfig.CreateEmpty().AddJob(new Job(Job.Dry, gc)); [Fact] - public void CanHostGcMode() + public void HostProcessSettingsAreCopiedByDefault() { var config = CreateConfig(GcMode.Default); - CanExecute(config); + + if (GCSettings.IsServerGC) + CanExecute(config); + else + CanExecute(config); } [Fact] diff --git a/tests/BenchmarkDotNet.Tests/AppConfigGeneratorTests.cs b/tests/BenchmarkDotNet.Tests/AppConfigGeneratorTests.cs index baa1b2ab43..227fd85c9b 100644 --- a/tests/BenchmarkDotNet.Tests/AppConfigGeneratorTests.cs +++ b/tests/BenchmarkDotNet.Tests/AppConfigGeneratorTests.cs @@ -5,9 +5,11 @@ using System.Text; using BenchmarkDotNet.Characteristics; using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Running; using Xunit; using BenchmarkDotNet.Tests.XUnit; +using System.Runtime; namespace BenchmarkDotNet.Tests { @@ -20,10 +22,10 @@ public void GeneratesMinimalRequiredAppConfigForEmptySource() { using (var destination = new Utf8StringWriter()) { - const string expectedMinimal = + string expectedMinimal = "" + "" + - "" + + $"{GcSettings}" + ""; AppConfigGenerator.Generate(Job.Default, TextReader.Null, destination, Resolver); @@ -38,10 +40,10 @@ public void GeneratesMinimalRequiredAppConfigForAlmostEmptySource() using (var source = new StringReader("")) using (var destination = new Utf8StringWriter()) { - const string expectedMinimal = + string expectedMinimal = "" + "" + - "" + + $"{GcSettings}" + ""; AppConfigGenerator.Generate(Job.Default, source, destination, Resolver); @@ -53,50 +55,69 @@ public void GeneratesMinimalRequiredAppConfigForAlmostEmptySource() [Fact] public void RewritesCustomSettings() { - const string customSettings = + string customSettingsWithoutRuntimeNode = "" + "" + "" + "withItsValue" + - "" + ""; - using (var source = new StringReader(customSettings)) + string customSettingsWithRuntimeNode = + "" + + "" + + "" + + "withItsValue" + + $"{GcSettings}" + + ""; + + using (var source = new StringReader(customSettingsWithoutRuntimeNode)) using (var destination = new Utf8StringWriter()) { AppConfigGenerator.Generate(Job.Default, source, destination, Resolver); - AssertAreEqualIgnoringWhitespacesAndCase(customSettings, destination.ToString()); + AssertAreEqualIgnoringWhitespacesAndCase(customSettingsWithRuntimeNode, destination.ToString()); } } [Fact] public void RewritesCustomRuntimeSettings() { - const string customSettings = + string customSettingsBefore = "" + "" + "" + "withItsValue" + - "" + + $"" + ""; - using (var source = new StringReader(customSettings)) + string customSettingsAfter = + "" + + "" + + "" + + "withItsValue" + + $"{GcSettings}" + + ""; + + using (var source = new StringReader(customSettingsBefore)) using (var destination = new Utf8StringWriter()) { AppConfigGenerator.Generate(Job.Default, source, destination, Resolver); - AssertAreEqualIgnoringWhitespacesAndCase(customSettings, destination.ToString()); + AssertAreEqualIgnoringWhitespacesAndCase(customSettingsAfter, destination.ToString()); } } [Theory] - [InlineData(Jit.LegacyJit, "")] - [InlineData(Jit.RyuJit, "")] + [InlineData(Jit.LegacyJit, "")] + [InlineData(Jit.RyuJit, "")] public void GeneratesRightJitSettings(Jit jit, string expectedRuntimeNode) { const string customSettings = @@ -109,7 +130,7 @@ public void GeneratesRightJitSettings(Jit jit, string expectedRuntimeNode) "" + "" + "withItsValue" + - expectedRuntimeNode + + $"{expectedRuntimeNode}{GcSettings}" + "" + Environment.NewLine; using (var source = new StringReader(customSettings)) @@ -133,7 +154,7 @@ public void RemovesStartupSettingsForPrivateBuildsOfClr() string withoutStartup = "" + "" + - "" + + $"{GcSettings}" + "" + Environment.NewLine; using (var source = new StringReader(input)) @@ -158,7 +179,7 @@ public void LeavsStartupSettingsIntactForNonPrivateBuildsOfClr() "" + "" + "" + - "" + + $"{GcSettings}" + "" + Environment.NewLine; using (var source = new StringReader(input)) @@ -186,7 +207,7 @@ public void RewritesCustomAssemblyBindingRedirects() "" + ""; - const string settingsWithBindingsAndJit = + string settingsWithBindingsAndJit = "" + "" + "" + @@ -197,6 +218,7 @@ public void RewritesCustomAssemblyBindingRedirects() "" + "" + "" + + GcSettings + "" + ""; @@ -234,6 +256,8 @@ private static string RemoveWhiteSpaces(string input) } return buffer.ToString(); } + + private static readonly string GcSettings = $""; } internal class Utf8StringWriter : StringWriter