diff --git a/NuGet.Config b/NuGet.Config
index 2b16f5257e..855812b5a5 100644
--- a/NuGet.Config
+++ b/NuGet.Config
@@ -12,5 +12,6 @@
+
diff --git a/README.md b/README.md
index 424439148a..1d183aeb6d 100644
--- a/README.md
+++ b/README.md
@@ -7,10 +7,10 @@
[](https://www.nuget.org/packages/BenchmarkDotNet/)
- [](https://www.myget.org/feed/Packages/benchmarkdotnet)
+ [](https://www.myget.org/feed/benchmarkdotnet/package/nuget/BenchmarkDotNet)
[](https://www.nuget.org/packages/BenchmarkDotNet/)
[](https://github.com/dotnet/BenchmarkDotNet/stargazers)
- 
+ [](https://github.com/dotnet/BenchmarkDotNet/blob/master/LICENSE.md)
[](https://twitter.com/BenchmarkDotNet)
@@ -30,7 +30,7 @@ It's no harder than writing unit tests!
Under the hood, it performs a lot of [magic](#automation) that guarantees [reliable and precise](#reliability) results thanks to the [perfolizer](https://github.com/AndreyAkinshin/perfolizer) statistical engine.
BenchmarkDotNet protects you from popular benchmarking mistakes and warns you if something is wrong with your benchmark design or obtained measurements.
The results are presented in a [user-friendly](#friendliness) form that highlights all the important facts about your experiment.
-BenchmarkDotNet is already adopted by [17400+ GitHub projects](https://github.com/dotnet/BenchmarkDotNet/network/dependents) including
+BenchmarkDotNet is already adopted by [18100+ GitHub projects](https://github.com/dotnet/BenchmarkDotNet/network/dependents) including
[.NET Runtime](https://github.com/dotnet/runtime),
[.NET Compiler](https://github.com/dotnet/roslyn),
[.NET Performance](https://github.com/dotnet/performance),
diff --git a/build/BenchmarkDotNet.Build/BenchmarkDotNet.Build.csproj b/build/BenchmarkDotNet.Build/BenchmarkDotNet.Build.csproj
index 3b5596f3b5..6b8ecc7ffc 100644
--- a/build/BenchmarkDotNet.Build/BenchmarkDotNet.Build.csproj
+++ b/build/BenchmarkDotNet.Build/BenchmarkDotNet.Build.csproj
@@ -9,7 +9,7 @@
-
+
\ No newline at end of file
diff --git a/build/BenchmarkDotNet.Build/Program.cs b/build/BenchmarkDotNet.Build/Program.cs
index ce291ecd0f..f099c38094 100644
--- a/build/BenchmarkDotNet.Build/Program.cs
+++ b/build/BenchmarkDotNet.Build/Program.cs
@@ -223,6 +223,9 @@ public class ReleaseTask : FrostingTask, IHelpProvider
new Example(Name)
.WithArgument(KnownOptions.Stable)
.WithArgument(KnownOptions.NextVersion, "0.1.1729")
+ .WithArgument(KnownOptions.Push),
+ new Example(Name)
+ .WithArgument(KnownOptions.Stable)
.WithArgument(KnownOptions.Push)
}
};
diff --git a/build/BenchmarkDotNet.Build/Runners/DocumentationRunner.cs b/build/BenchmarkDotNet.Build/Runners/DocumentationRunner.cs
index 6883abe79a..2d2128a86f 100644
--- a/build/BenchmarkDotNet.Build/Runners/DocumentationRunner.cs
+++ b/build/BenchmarkDotNet.Build/Runners/DocumentationRunner.cs
@@ -133,8 +133,8 @@ private void RunDocfx()
var currentDirectory = Directory.GetCurrentDirectory();
Directory.SetCurrentDirectory(docfxJsonFile.GetDirectory().FullPath);
- Microsoft.DocAsCode.Dotnet.DotnetApiCatalog.GenerateManagedReferenceYamlFiles(docfxJsonFile.FullPath).Wait();
- Microsoft.DocAsCode.Docset.Build(docfxJsonFile.FullPath).Wait();
+ Docfx.Dotnet.DotnetApiCatalog.GenerateManagedReferenceYamlFiles(docfxJsonFile.FullPath).Wait();
+ Docfx.Docset.Build(docfxJsonFile.FullPath).Wait();
Directory.SetCurrentDirectory(currentDirectory);
}
diff --git a/build/BenchmarkDotNet.Build/Runners/ReleaseRunner.cs b/build/BenchmarkDotNet.Build/Runners/ReleaseRunner.cs
index 0562639a9b..fb9aa895ec 100644
--- a/build/BenchmarkDotNet.Build/Runners/ReleaseRunner.cs
+++ b/build/BenchmarkDotNet.Build/Runners/ReleaseRunner.cs
@@ -1,7 +1,6 @@
using System;
using System.Linq;
using System.Text;
-using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BenchmarkDotNet.Build.Helpers;
using BenchmarkDotNet.Build.Meta;
@@ -34,9 +33,15 @@ public void Run()
else
EnvVar.NuGetToken.SetEmpty();
- var nextVersion = KnownOptions.NextVersion.AssertHasValue(context);
var currentVersion = context.VersionHistory.CurrentVersion;
var tag = "v" + currentVersion;
+ var nextVersion = KnownOptions.NextVersion.Resolve(context);
+ if (nextVersion == "")
+ {
+ var version = Version.Parse(currentVersion);
+ nextVersion = $"{version.Major}.{version.Minor}.{version.Build + 1}";
+ context.Information($"Evaluated NextVersion: {nextVersion}");
+ }
context.GitRunner.Tag(tag);
diff --git a/build/common.props b/build/common.props
index f84648040b..a9bd13f812 100644
--- a/build/common.props
+++ b/build/common.props
@@ -18,6 +18,7 @@
false
true
True
+ false
$(MSBuildThisFileDirectory)CodingStyle.ruleset
true
@@ -39,7 +40,7 @@
- 0.13.8
+ 0.13.10
diff --git a/build/sdk/global.json b/build/sdk/global.json
index 5e4624ef91..c1e852daeb 100644
--- a/build/sdk/global.json
+++ b/build/sdk/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "7.0.305",
+ "version": "7.0.401",
"rollForward": "disable"
}
}
diff --git a/build/versions.txt b/build/versions.txt
index 1f6634ffbc..e202cbc55a 100644
--- a/build/versions.txt
+++ b/build/versions.txt
@@ -52,4 +52,6 @@
0.13.5
0.13.6
0.13.7
-0.13.8
\ No newline at end of file
+0.13.8
+0.13.9
+0.13.10
\ No newline at end of file
diff --git a/docs/_changelog/footer/v0.13.10.md b/docs/_changelog/footer/v0.13.10.md
new file mode 100644
index 0000000000..68ef4fd0db
--- /dev/null
+++ b/docs/_changelog/footer/v0.13.10.md
@@ -0,0 +1,11 @@
+_Date: November 01, 2023_
+
+_Milestone: [v0.13.10](https://github.com/dotnet/BenchmarkDotNet/issues?q=milestone%3Av0.13.10)_
+([List of commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.9...v0.13.10))
+
+_NuGet Packages:_
+* https://www.nuget.org/packages/BenchmarkDotNet/0.13.10
+* https://www.nuget.org/packages/BenchmarkDotNet.Annotations/0.13.10
+* https://www.nuget.org/packages/BenchmarkDotNet.Diagnostics.dotTrace/0.13.10
+* https://www.nuget.org/packages/BenchmarkDotNet.Diagnostics.Windows/0.13.10
+* https://www.nuget.org/packages/BenchmarkDotNet.Templates/0.13.10
diff --git a/docs/_changelog/footer/v0.13.9.md b/docs/_changelog/footer/v0.13.9.md
new file mode 100644
index 0000000000..962bc252cc
--- /dev/null
+++ b/docs/_changelog/footer/v0.13.9.md
@@ -0,0 +1,11 @@
+_Date: October 05, 2023_
+
+_Milestone: [v0.13.9](https://github.com/dotnet/BenchmarkDotNet/issues?q=milestone%3Av0.13.9)_
+([List of commits](https://github.com/dotnet/BenchmarkDotNet/compare/v0.13.8...v0.13.9))
+
+_NuGet Packages:_
+* https://www.nuget.org/packages/BenchmarkDotNet/0.13.9
+* https://www.nuget.org/packages/BenchmarkDotNet.Annotations/0.13.9
+* https://www.nuget.org/packages/BenchmarkDotNet.Diagnostics.dotTrace/0.13.9
+* https://www.nuget.org/packages/BenchmarkDotNet.Diagnostics.Windows/0.13.9
+* https://www.nuget.org/packages/BenchmarkDotNet.Templates/0.13.9
diff --git a/docs/_changelog/header/v0.13.10.md b/docs/_changelog/header/v0.13.10.md
new file mode 100644
index 0000000000..9f55725d19
--- /dev/null
+++ b/docs/_changelog/header/v0.13.10.md
@@ -0,0 +1,3 @@
+## Highlights
+
+Initial support of .NET 9 and minor bug fixes.
\ No newline at end of file
diff --git a/docs/_changelog/header/v0.13.9.md b/docs/_changelog/header/v0.13.9.md
new file mode 100644
index 0000000000..07e4813c9f
--- /dev/null
+++ b/docs/_changelog/header/v0.13.9.md
@@ -0,0 +1,3 @@
+## Highlights
+
+This release contains bug fixes.
\ No newline at end of file
diff --git a/samples/BenchmarkDotNet.Samples/IntroExceptionDiagnoser.cs b/samples/BenchmarkDotNet.Samples/IntroExceptionDiagnoser.cs
index a43844e592..7e0f847c95 100644
--- a/samples/BenchmarkDotNet.Samples/IntroExceptionDiagnoser.cs
+++ b/samples/BenchmarkDotNet.Samples/IntroExceptionDiagnoser.cs
@@ -16,7 +16,10 @@ public void ThrowExceptionRandomly()
throw new Exception();
}
}
- catch { }
+ catch
+ {
+ // ignored
+ }
}
}
}
diff --git a/samples/BenchmarkDotNet.Samples/IntroOrderManual.cs b/samples/BenchmarkDotNet.Samples/IntroOrderManual.cs
index 625e78a816..a624edd205 100644
--- a/samples/BenchmarkDotNet.Samples/IntroOrderManual.cs
+++ b/samples/BenchmarkDotNet.Samples/IntroOrderManual.cs
@@ -22,7 +22,7 @@ private class Config : ManualConfig
private class FastestToSlowestOrderer : IOrderer
{
public IEnumerable GetExecutionOrder(ImmutableArray benchmarksCase,
- IEnumerable order = null) =>
+ IEnumerable? order = null) =>
from benchmark in benchmarksCase
orderby benchmark.Parameters["X"] descending,
benchmark.Descriptor.WorkloadMethodDisplayInfo
@@ -39,7 +39,7 @@ public string GetLogicalGroupKey(ImmutableArray allBenchmarksCase
benchmarkCase.Job.DisplayInfo + "_" + benchmarkCase.Parameters.DisplayInfo;
public IEnumerable> GetLogicalGroupOrder(IEnumerable> logicalGroups,
- IEnumerable order = null) =>
+ IEnumerable? order = null) =>
logicalGroups.OrderBy(it => it.Key);
public bool SeparateLogicalGroups => true;
diff --git a/src/BenchmarkDotNet.Annotations/Attributes/TargetedAttribute.cs b/src/BenchmarkDotNet.Annotations/Attributes/TargetedAttribute.cs
index f026675bd7..efeb7ee0c6 100644
--- a/src/BenchmarkDotNet.Annotations/Attributes/TargetedAttribute.cs
+++ b/src/BenchmarkDotNet.Annotations/Attributes/TargetedAttribute.cs
@@ -20,6 +20,6 @@ public string Target
set => Targets = string.IsNullOrEmpty(value) ? new string[0] : value.Split(','); // , is for backward compat
}
- public bool Match(MethodInfo method) => Targets == null || Targets.Length == 0 || Targets.Contains(method.Name);
+ public bool Match(MethodInfo method) => Targets.Length == 0 || Targets.Contains(method.Name);
}
}
diff --git a/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs b/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs
index ca9b5a0772..4fc2db7388 100644
--- a/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs
+++ b/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs
@@ -105,6 +105,11 @@ public enum RuntimeMoniker
///
Net80,
+ ///
+ /// .NET 9.0
+ ///
+ Net90,
+
///
/// NativeAOT compiled as net6.0
///
@@ -120,6 +125,11 @@ public enum RuntimeMoniker
///
NativeAot80,
+ ///
+ /// NativeAOT compiled as net9.0
+ ///
+ NativeAot90,
+
///
/// WebAssembly with default .Net version
///
@@ -145,6 +155,11 @@ public enum RuntimeMoniker
///
WasmNet80,
+ ///
+ /// WebAssembly with net9.0
+ ///
+ WasmNet90,
+
///
/// Mono with the Ahead of Time LLVM Compiler backend
///
@@ -165,6 +180,11 @@ public enum RuntimeMoniker
///
MonoAOTLLVMNet80,
+ ///
+ /// Mono with the Ahead of Time LLVM Compiler backend and net9.0
+ ///
+ MonoAOTLLVMNet90,
+
///
/// .NET 6 using MonoVM (not CLR which is the default)
///
@@ -179,5 +199,10 @@ public enum RuntimeMoniker
/// .NET 8 using MonoVM (not CLR which is the default)
///
Mono80,
+
+ ///
+ /// .NET 9 using MonoVM (not CLR which is the default)
+ ///
+ Mono90,
}
}
diff --git a/src/BenchmarkDotNet.Diagnostics.Windows/Configs/InliningDiagnoserAttribute.cs b/src/BenchmarkDotNet.Diagnostics.Windows/Configs/InliningDiagnoserAttribute.cs
index 0a3f4e0dc5..c9878ed6fc 100644
--- a/src/BenchmarkDotNet.Diagnostics.Windows/Configs/InliningDiagnoserAttribute.cs
+++ b/src/BenchmarkDotNet.Diagnostics.Windows/Configs/InliningDiagnoserAttribute.cs
@@ -13,7 +13,7 @@ public InliningDiagnoserAttribute(bool logFailuresOnly = true, bool filterByName
Config = ManualConfig.CreateEmpty().AddDiagnoser(new InliningDiagnoser(logFailuresOnly, filterByNamespace));
}
- public InliningDiagnoserAttribute(bool logFailuresOnly = true, string[] allowedNamespaces = null)
+ public InliningDiagnoserAttribute(bool logFailuresOnly = true, string[]? allowedNamespaces = null)
{
Config = ManualConfig.CreateEmpty().AddDiagnoser(new InliningDiagnoser(logFailuresOnly, allowedNamespaces));
}
diff --git a/src/BenchmarkDotNet.Diagnostics.Windows/EtwDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.Windows/EtwDiagnoser.cs
index c52006d7c6..80423d72cf 100644
--- a/src/BenchmarkDotNet.Diagnostics.Windows/EtwDiagnoser.cs
+++ b/src/BenchmarkDotNet.Diagnostics.Windows/EtwDiagnoser.cs
@@ -97,7 +97,7 @@ private void Clear()
private void OnProcessExit(object sender, EventArgs e) => Session?.Dispose();
- private static string GetSessionName(string prefix, BenchmarkCase benchmarkCase, ParameterInstances parameters = null)
+ private static string GetSessionName(string prefix, BenchmarkCase benchmarkCase, ParameterInstances? parameters = null)
{
if (parameters != null && parameters.Items.Count > 0)
return $"{prefix}-{benchmarkCase.FolderInfo}-{parameters.FolderInfo}";
diff --git a/src/BenchmarkDotNet.Diagnostics.Windows/EtwProfilerConfig.cs b/src/BenchmarkDotNet.Diagnostics.Windows/EtwProfilerConfig.cs
index 967dcc2d03..7846edd1dd 100644
--- a/src/BenchmarkDotNet.Diagnostics.Windows/EtwProfilerConfig.cs
+++ b/src/BenchmarkDotNet.Diagnostics.Windows/EtwProfilerConfig.cs
@@ -41,8 +41,8 @@ public EtwProfilerConfig(
float cpuSampleIntervalInMilliseconds = 1.0f,
KernelTraceEventParser.Keywords kernelKeywords = KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Profile,
KernelTraceEventParser.Keywords kernelStackKeywords = KernelTraceEventParser.Keywords.Profile,
- IReadOnlyDictionary> intervalSelectors = null,
- IReadOnlyCollection<(Guid providerGuid, TraceEventLevel providerLevel, ulong keywords, TraceEventProviderOptions options)> providers = null,
+ IReadOnlyDictionary>? intervalSelectors = null,
+ IReadOnlyCollection<(Guid providerGuid, TraceEventLevel providerLevel, ulong keywords, TraceEventProviderOptions options)>? providers = null,
bool createHeapSession = false)
{
CreateHeapSession = createHeapSession;
diff --git a/src/BenchmarkDotNet.Diagnostics.Windows/InliningDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.Windows/InliningDiagnoser.cs
index 4c09957eef..a21dd1dc81 100644
--- a/src/BenchmarkDotNet.Diagnostics.Windows/InliningDiagnoser.cs
+++ b/src/BenchmarkDotNet.Diagnostics.Windows/InliningDiagnoser.cs
@@ -13,7 +13,7 @@ public class InliningDiagnoser : JitDiagnoser
diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs
index a53c305938..fe9e77005a 100644
--- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs
+++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs
@@ -19,10 +19,10 @@ namespace BenchmarkDotNet.Diagnostics.dotTrace
{
public class DotTraceDiagnoser : IProfiler
{
- private readonly Uri nugetUrl;
- private readonly string toolsDownloadFolder;
+ private readonly Uri? nugetUrl;
+ private readonly string? toolsDownloadFolder;
- public DotTraceDiagnoser(Uri nugetUrl = null, string toolsDownloadFolder = null)
+ public DotTraceDiagnoser(Uri? nugetUrl = null, string? toolsDownloadFolder = null)
{
this.nugetUrl = nugetUrl;
this.toolsDownloadFolder = toolsDownloadFolder;
@@ -97,24 +97,29 @@ internal static bool IsSupported(RuntimeMoniker runtimeMoniker)
case RuntimeMoniker.Net60:
case RuntimeMoniker.Net70:
case RuntimeMoniker.Net80:
+ case RuntimeMoniker.Net90:
return true;
case RuntimeMoniker.NotRecognized:
case RuntimeMoniker.Mono:
case RuntimeMoniker.NativeAot60:
case RuntimeMoniker.NativeAot70:
case RuntimeMoniker.NativeAot80:
+ case RuntimeMoniker.NativeAot90:
case RuntimeMoniker.Wasm:
case RuntimeMoniker.WasmNet50:
case RuntimeMoniker.WasmNet60:
case RuntimeMoniker.WasmNet70:
case RuntimeMoniker.WasmNet80:
+ case RuntimeMoniker.WasmNet90:
case RuntimeMoniker.MonoAOTLLVM:
case RuntimeMoniker.MonoAOTLLVMNet60:
case RuntimeMoniker.MonoAOTLLVMNet70:
case RuntimeMoniker.MonoAOTLLVMNet80:
+ case RuntimeMoniker.MonoAOTLLVMNet90:
case RuntimeMoniker.Mono60:
case RuntimeMoniker.Mono70:
case RuntimeMoniker.Mono80:
+ case RuntimeMoniker.Mono90:
#pragma warning disable CS0618 // Type or member is obsolete
case RuntimeMoniker.NetCoreApp50:
#pragma warning restore CS0618 // Type or member is obsolete
diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoserAttribute.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoserAttribute.cs
index de803e6443..19e88a6de1 100644
--- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoserAttribute.cs
+++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoserAttribute.cs
@@ -13,7 +13,7 @@ public DotTraceDiagnoserAttribute()
Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotTraceDiagnoser());
}
- public DotTraceDiagnoserAttribute(Uri nugetUrl = null, string toolsDownloadFolder = null)
+ public DotTraceDiagnoserAttribute(Uri? nugetUrl = null, string? toolsDownloadFolder = null)
{
Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotTraceDiagnoser(nugetUrl, toolsDownloadFolder));
}
diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceToolBase.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceToolBase.cs
index c41ffc53e5..f2f07625fa 100644
--- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceToolBase.cs
+++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceToolBase.cs
@@ -11,11 +11,11 @@ namespace BenchmarkDotNet.Diagnostics.dotTrace
internal abstract class DotTraceToolBase
{
private readonly ILogger logger;
- private readonly Uri nugetUrl;
+ private readonly Uri? nugetUrl;
private readonly NuGetApi nugetApi;
- private readonly string downloadTo;
+ private readonly string? downloadTo;
- protected DotTraceToolBase(ILogger logger, Uri nugetUrl = null, NuGetApi nugetApi = NuGetApi.V3, string downloadTo = null)
+ protected DotTraceToolBase(ILogger logger, Uri? nugetUrl = null, NuGetApi nugetApi = NuGetApi.V3, string? downloadTo = null)
{
this.logger = logger;
this.nugetUrl = nugetUrl;
@@ -48,9 +48,9 @@ public void Init(DiagnoserActionParameters parameters)
public string Start(DiagnoserActionParameters parameters)
{
string snapshotFile = ArtifactFileNameHelper.GetFilePath(parameters, "snapshots", DateTime.Now, "dtp", ".0000".Length);
- string snapshotDirectory = Path.GetDirectoryName(snapshotFile);
+ string? snapshotDirectory = Path.GetDirectoryName(snapshotFile);
logger.WriteLineInfo($"Target snapshot file: {snapshotFile}");
- if (!Directory.Exists(snapshotDirectory))
+ if (!Directory.Exists(snapshotDirectory) && snapshotDirectory != null)
{
try
{
@@ -126,7 +126,7 @@ protected string GetRunnerPath()
if (consoleRunnerPackageField == null)
throw new InvalidOperationException("Field 'ConsoleRunnerPackage' not found.");
- object consoleRunnerPackage = consoleRunnerPackageField.GetValue(null);
+ object? consoleRunnerPackage = consoleRunnerPackageField.GetValue(null);
if (consoleRunnerPackage == null)
throw new InvalidOperationException("Unable to get value of 'ConsoleRunnerPackage'.");
@@ -135,7 +135,7 @@ protected string GetRunnerPath()
if (getRunnerPathMethod == null)
throw new InvalidOperationException("Method 'GetRunnerPath' not found.");
- string runnerPath = getRunnerPathMethod.Invoke(consoleRunnerPackage, null) as string;
+ string? runnerPath = getRunnerPathMethod.Invoke(consoleRunnerPackage, null) as string;
if (runnerPath == null)
throw new InvalidOperationException("Unable to invoke 'GetRunnerPath'.");
diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs
index 9c5161b309..dfc9903b82 100644
--- a/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs
+++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/ExternalDotTraceTool.cs
@@ -12,7 +12,7 @@ internal class ExternalDotTraceTool : DotTraceToolBase
{
private static readonly TimeSpan AttachTimeout = TimeSpan.FromMinutes(5);
- public ExternalDotTraceTool(ILogger logger, Uri nugetUrl = null, NuGetApi nugetApi = NuGetApi.V3, string downloadTo = null) :
+ public ExternalDotTraceTool(ILogger logger, Uri? nugetUrl = null, NuGetApi nugetApi = NuGetApi.V3, string? downloadTo = null) :
base(logger, nugetUrl, nugetApi, downloadTo) { }
protected override bool AttachOnly => true;
@@ -44,7 +44,7 @@ protected override void Attach(DiagnoserActionParameters parameters, string snap
{
process.OutputDataReceived += (_, args) =>
{
- string content = args.Data;
+ string? content = args.Data;
if (content != null)
{
logger.WriteLineInfo("[dotTrace] " + content);
@@ -54,7 +54,7 @@ protected override void Attach(DiagnoserActionParameters parameters, string snap
};
process.ErrorDataReceived += (_, args) =>
{
- string content = args.Data;
+ string? content = args.Data;
if (content != null)
logger.WriteLineError("[dotTrace] " + args.Data);
};
diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/InProcessDotTraceTool.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/InProcessDotTraceTool.cs
index a124e3e495..a02c9c1995 100644
--- a/src/BenchmarkDotNet.Diagnostics.dotTrace/InProcessDotTraceTool.cs
+++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/InProcessDotTraceTool.cs
@@ -7,7 +7,7 @@ namespace BenchmarkDotNet.Diagnostics.dotTrace
{
internal class InProcessDotTraceTool : DotTraceToolBase
{
- public InProcessDotTraceTool(ILogger logger, Uri nugetUrl = null, NuGetApi nugetApi = NuGetApi.V3, string downloadTo = null) :
+ public InProcessDotTraceTool(ILogger logger, Uri? nugetUrl = null, NuGetApi nugetApi = NuGetApi.V3, string? downloadTo = null) :
base(logger, nugetUrl, nugetApi, downloadTo) { }
protected override bool AttachOnly => false;
diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/Progress.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/Progress.cs
index 1d8249f31e..c353939f1f 100644
--- a/src/BenchmarkDotNet.Diagnostics.dotTrace/Progress.cs
+++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/Progress.cs
@@ -18,7 +18,7 @@ public Progress(ILogger logger, string title)
}
private int lastProgress;
- private Stopwatch stopwatch;
+ private Stopwatch? stopwatch;
public void Report(double value)
{
diff --git a/src/BenchmarkDotNet.Disassembler.x64/SourceCodeProvider.cs b/src/BenchmarkDotNet.Disassembler.x64/SourceCodeProvider.cs
index 7cdb00a6d9..c9ac897570 100644
--- a/src/BenchmarkDotNet.Disassembler.x64/SourceCodeProvider.cs
+++ b/src/BenchmarkDotNet.Disassembler.x64/SourceCodeProvider.cs
@@ -134,7 +134,7 @@ private static SourceLocation FindNearestLine(PdbFunction function, int ilOffset
return null;
int distance = int.MaxValue;
- SourceLocation nearest = null;
+ SourceLocation? nearest = null;
foreach (PdbSequencePointCollection sequenceCollection in function.SequencePoints)
{
@@ -183,7 +183,7 @@ private static PdbReader GetReaderForMethod(ClrMethod method)
ClrModule module = method?.Type?.Module;
PdbInfo info = module?.Pdb;
- PdbReader reader = null;
+ PdbReader? reader = null;
if (info != null)
{
if (!s_pdbReaders.TryGetValue(info, out reader))
diff --git a/src/BenchmarkDotNet/Analysers/OutliersAnalyser.cs b/src/BenchmarkDotNet/Analysers/OutliersAnalyser.cs
index 6da2088e86..c5c54f8c91 100644
--- a/src/BenchmarkDotNet/Analysers/OutliersAnalyser.cs
+++ b/src/BenchmarkDotNet/Analysers/OutliersAnalyser.cs
@@ -22,25 +22,11 @@ protected override IEnumerable AnalyseReport(BenchmarkReport report,
var workloadActual = report.AllMeasurements.Where(m => m.Is(IterationMode.Workload, IterationStage.Actual)).ToArray();
if (workloadActual.IsEmpty())
yield break;
- var result = report.AllMeasurements.Where(m => m.Is(IterationMode.Workload, IterationStage.Result)).ToArray();
var outlierMode = report.BenchmarkCase.Job.ResolveValue(AccuracyMode.OutlierModeCharacteristic, EngineResolver.Instance); // TODO: improve
var statistics = workloadActual.GetStatistics();
var allOutliers = statistics.AllOutliers;
var actualOutliers = statistics.GetActualOutliers(outlierMode);
- if (result.Length + actualOutliers.Length != workloadActual.Length)
- {
- // This should never happen
- yield return CreateHint(
- "Something went wrong with outliers: " +
- $"Size(WorkloadActual) = {workloadActual.Length}, " +
- $"Size(WorkloadActual/Outliers) = {actualOutliers.Length}, " +
- $"Size(Result) = {result.Length}), " +
- $"OutlierMode = {outlierMode}",
- report);
- yield break;
- }
-
var cultureInfo = summary.GetCultureInfo();
if (allOutliers.Any())
yield return CreateHint(GetMessage(actualOutliers, allOutliers, statistics.LowerOutliers, statistics.UpperOutliers, cultureInfo), report);
diff --git a/src/BenchmarkDotNet/Characteristics/Characteristic.cs b/src/BenchmarkDotNet/Characteristics/Characteristic.cs
index 6ca88f5f75..d6e5815b4e 100644
--- a/src/BenchmarkDotNet/Characteristics/Characteristic.cs
+++ b/src/BenchmarkDotNet/Characteristics/Characteristic.cs
@@ -86,7 +86,7 @@ protected Characteristic(
private object FallbackValue { get; }
- public object this[CharacteristicObject obj]
+ public object? this[CharacteristicObject obj]
{
get { return obj.GetValue(this); }
set { obj.SetValue(this, value); }
diff --git a/src/BenchmarkDotNet/Code/EnumParam.cs b/src/BenchmarkDotNet/Code/EnumParam.cs
index 9d0ac58950..5c0ecf6afd 100644
--- a/src/BenchmarkDotNet/Code/EnumParam.cs
+++ b/src/BenchmarkDotNet/Code/EnumParam.cs
@@ -24,7 +24,7 @@ private EnumParam(object value, Type type)
public string ToSourceCode() =>
$"({type.GetCorrectCSharpTypeName()})({ToInvariantCultureString()})";
- internal static IParam FromObject(object value, Type type = null)
+ internal static IParam FromObject(object value, Type? type = null)
{
type = type ?? value.GetType();
if (!type.IsEnum)
diff --git a/src/BenchmarkDotNet/Columns/StatisticColumn.cs b/src/BenchmarkDotNet/Columns/StatisticColumn.cs
index 31e72f2446..f0b9655acc 100644
--- a/src/BenchmarkDotNet/Columns/StatisticColumn.cs
+++ b/src/BenchmarkDotNet/Columns/StatisticColumn.cs
@@ -107,7 +107,7 @@ private enum Priority
private readonly IStatisticColumn parentColumn;
private StatisticColumn(string columnName, string legend, Func calc, Priority priority, UnitType type = UnitType.Time,
- IStatisticColumn parentColumn = null)
+ IStatisticColumn? parentColumn = null)
{
this.calc = calc;
this.priority = priority;
diff --git a/src/BenchmarkDotNet/Configs/ConfigExtensions.cs b/src/BenchmarkDotNet/Configs/ConfigExtensions.cs
index fa0920c7f9..5177227c2a 100644
--- a/src/BenchmarkDotNet/Configs/ConfigExtensions.cs
+++ b/src/BenchmarkDotNet/Configs/ConfigExtensions.cs
@@ -7,6 +7,7 @@
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Diagnosers;
+using BenchmarkDotNet.EventProcessors;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Filters;
using BenchmarkDotNet.Jobs;
@@ -110,6 +111,7 @@ public static class ConfigExtensions
[Obsolete("This method will soon be removed, please start using .AddLogicalGroupRules() instead.")]
[EditorBrowsable(EditorBrowsableState.Never)] public static IConfig With(this IConfig config, params BenchmarkLogicalGroupRule[] rules) => config.AddLogicalGroupRules(rules);
[PublicAPI] public static ManualConfig AddLogicalGroupRules(this IConfig config, params BenchmarkLogicalGroupRule[] rules) => config.With(c => c.AddLogicalGroupRules(rules));
+ [PublicAPI] public static ManualConfig AddEventProcessor(this IConfig config, params EventProcessor[] eventProcessors) => config.With(c => c.AddEventProcessor(eventProcessors));
[PublicAPI] public static ManualConfig HideColumns(this IConfig config, params string[] columnNames) => config.With(c => c.HideColumns(columnNames));
[PublicAPI] public static ManualConfig HideColumns(this IConfig config, params IColumn[] columns) => config.With(c => c.HideColumns(columns));
diff --git a/src/BenchmarkDotNet/Configs/DebugConfig.cs b/src/BenchmarkDotNet/Configs/DebugConfig.cs
index 0d66540bb1..0fdda7b08d 100644
--- a/src/BenchmarkDotNet/Configs/DebugConfig.cs
+++ b/src/BenchmarkDotNet/Configs/DebugConfig.cs
@@ -4,6 +4,7 @@
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Diagnosers;
+using BenchmarkDotNet.EventProcessors;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Filters;
using BenchmarkDotNet.Jobs;
@@ -61,6 +62,7 @@ public abstract class DebugConfig : IConfig
public IEnumerable GetDiagnosers() => Array.Empty();
public IEnumerable GetAnalysers() => Array.Empty();
public IEnumerable GetHardwareCounters() => Array.Empty();
+ public IEnumerable GetEventProcessors() => Array.Empty();
public IEnumerable GetFilters() => Array.Empty();
public IEnumerable GetColumnHidingRules() => Array.Empty();
diff --git a/src/BenchmarkDotNet/Configs/DefaultConfig.cs b/src/BenchmarkDotNet/Configs/DefaultConfig.cs
index 8d1df6285b..2323d4fdc4 100644
--- a/src/BenchmarkDotNet/Configs/DefaultConfig.cs
+++ b/src/BenchmarkDotNet/Configs/DefaultConfig.cs
@@ -5,6 +5,7 @@
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Diagnosers;
+using BenchmarkDotNet.EventProcessors;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Exporters.Csv;
using BenchmarkDotNet.Filters;
@@ -108,6 +109,8 @@ public string ArtifactsPath
public IEnumerable GetFilters() => Array.Empty();
+ public IEnumerable GetEventProcessors() => Array.Empty();
+
public IEnumerable GetColumnHidingRules() => Array.Empty();
}
}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Configs/IConfig.cs b/src/BenchmarkDotNet/Configs/IConfig.cs
index ba34cfeddf..b311c235f5 100644
--- a/src/BenchmarkDotNet/Configs/IConfig.cs
+++ b/src/BenchmarkDotNet/Configs/IConfig.cs
@@ -4,6 +4,7 @@
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Diagnosers;
+using BenchmarkDotNet.EventProcessors;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Filters;
using BenchmarkDotNet.Jobs;
@@ -27,6 +28,7 @@ public interface IConfig
IEnumerable GetHardwareCounters();
IEnumerable GetFilters();
IEnumerable GetLogicalGroupRules();
+ IEnumerable GetEventProcessors();
IEnumerable GetColumnHidingRules();
IOrderer? Orderer { get; }
diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs
index b5d84eaf56..1216ef3b16 100644
--- a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs
+++ b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs
@@ -6,6 +6,7 @@
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Diagnosers;
+using BenchmarkDotNet.EventProcessors;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Filters;
using BenchmarkDotNet.Jobs;
@@ -31,6 +32,7 @@ public sealed class ImmutableConfig : IConfig
private readonly ImmutableHashSet hardwareCounters;
private readonly ImmutableHashSet filters;
private readonly ImmutableArray rules;
+ private readonly ImmutableHashSet eventProcessors;
private readonly ImmutableArray columnHidingRules;
internal ImmutableConfig(
@@ -45,6 +47,7 @@ internal ImmutableConfig(
ImmutableArray uniqueRules,
ImmutableArray uniqueColumnHidingRules,
ImmutableHashSet uniqueRunnableJobs,
+ ImmutableHashSet uniqueEventProcessors,
ConfigUnionRule unionRule,
string artifactsPath,
CultureInfo cultureInfo,
@@ -66,6 +69,7 @@ internal ImmutableConfig(
rules = uniqueRules;
columnHidingRules = uniqueColumnHidingRules;
jobs = uniqueRunnableJobs;
+ eventProcessors = uniqueEventProcessors;
UnionRule = unionRule;
ArtifactsPath = artifactsPath;
CultureInfo = cultureInfo;
@@ -96,6 +100,7 @@ internal ImmutableConfig(
public IEnumerable GetHardwareCounters() => hardwareCounters;
public IEnumerable GetFilters() => filters;
public IEnumerable GetLogicalGroupRules() => rules;
+ public IEnumerable GetEventProcessors() => eventProcessors;
public IEnumerable GetColumnHidingRules() => columnHidingRules;
public ILogger GetCompositeLogger() => new CompositeLogger(loggers);
diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs
index 0540dd1426..d7c0b5eb0f 100644
--- a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs
+++ b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs
@@ -53,6 +53,7 @@ public static ImmutableConfig Create(IConfig source)
var uniqueHidingRules = source.GetColumnHidingRules().ToImmutableArray();
var uniqueRunnableJobs = GetRunnableJobs(source.GetJobs()).ToImmutableHashSet();
+ var uniqueEventProcessors = source.GetEventProcessors().ToImmutableHashSet();
return new ImmutableConfig(
uniqueColumnProviders,
@@ -66,6 +67,7 @@ public static ImmutableConfig Create(IConfig source)
uniqueRules,
uniqueHidingRules,
uniqueRunnableJobs,
+ uniqueEventProcessors,
source.UnionRule,
source.ArtifactsPath ?? DefaultConfig.Instance.ArtifactsPath,
source.CultureInfo,
diff --git a/src/BenchmarkDotNet/Configs/ManualConfig.cs b/src/BenchmarkDotNet/Configs/ManualConfig.cs
index 27eca81863..5ea1be24e9 100644
--- a/src/BenchmarkDotNet/Configs/ManualConfig.cs
+++ b/src/BenchmarkDotNet/Configs/ManualConfig.cs
@@ -6,6 +6,7 @@
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Diagnosers;
+using BenchmarkDotNet.EventProcessors;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Filters;
@@ -33,6 +34,7 @@ public class ManualConfig : IConfig
private readonly HashSet hardwareCounters = new HashSet();
private readonly List filters = new List();
private readonly List logicalGroupRules = new List();
+ private readonly List eventProcessors = new List();
private readonly List columnHidingRules = new List();
public IEnumerable GetColumnProviders() => columnProviders;
@@ -45,6 +47,7 @@ public class ManualConfig : IConfig
public IEnumerable GetHardwareCounters() => hardwareCounters;
public IEnumerable GetFilters() => filters;
public IEnumerable GetLogicalGroupRules() => logicalGroupRules;
+ public IEnumerable GetEventProcessors() => eventProcessors;
public IEnumerable GetColumnHidingRules() => columnHidingRules;
[PublicAPI] public ConfigOptions Options { get; set; }
@@ -221,6 +224,12 @@ public ManualConfig AddLogicalGroupRules(params BenchmarkLogicalGroupRule[] rule
return this;
}
+ public ManualConfig AddEventProcessor(params EventProcessor[] newEventProcessors)
+ {
+ this.eventProcessors.AddRange(newEventProcessors);
+ return this;
+ }
+
[PublicAPI]
public ManualConfig HideColumns(params string[] columnNames)
{
@@ -254,6 +263,7 @@ public void Add(IConfig config)
validators.AddRange(config.GetValidators());
hardwareCounters.AddRange(config.GetHardwareCounters());
filters.AddRange(config.GetFilters());
+ eventProcessors.AddRange(config.GetEventProcessors());
Orderer = config.Orderer ?? Orderer;
CategoryDiscoverer = config.CategoryDiscoverer ?? CategoryDiscoverer;
ArtifactsPath = config.ArtifactsPath ?? ArtifactsPath;
diff --git a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs
index 76500bed44..0a125900f4 100644
--- a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs
+++ b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs
@@ -72,7 +72,7 @@ public static class ConfigParser
{ "fullxml", new[] { XmlExporter.Full } }
};
- public static (bool isSuccess, IConfig config, CommandLineOptions options) Parse(string[] args, ILogger logger, IConfig globalConfig = null)
+ public static (bool isSuccess, IConfig config, CommandLineOptions options) Parse(string[] args, ILogger logger, IConfig? globalConfig = null)
{
(bool isSuccess, IConfig config, CommandLineOptions options) result = default;
@@ -532,6 +532,7 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma
case RuntimeMoniker.Net60:
case RuntimeMoniker.Net70:
case RuntimeMoniker.Net80:
+ case RuntimeMoniker.Net90:
return baseJob
.WithRuntime(runtimeMoniker.GetRuntime())
.WithToolchain(CsProjCoreToolchain.From(new NetCoreAppSettings(runtimeId, null, runtimeId, options.CliPath?.FullName, options.RestorePath?.FullName)));
@@ -548,6 +549,9 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma
case RuntimeMoniker.NativeAot80:
return CreateAotJob(baseJob, options, runtimeMoniker, "", "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8/nuget/v3/index.json");
+ case RuntimeMoniker.NativeAot90:
+ return CreateAotJob(baseJob, options, runtimeMoniker, "", "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json");
+
case RuntimeMoniker.Wasm:
return MakeWasmJob(baseJob, options, RuntimeInformation.IsNetCore ? CoreRuntime.GetCurrentVersion().MsBuildMoniker : "net5.0", runtimeMoniker);
@@ -563,6 +567,9 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma
case RuntimeMoniker.WasmNet80:
return MakeWasmJob(baseJob, options, "net8.0", runtimeMoniker);
+ case RuntimeMoniker.WasmNet90:
+ return MakeWasmJob(baseJob, options, "net9.0", runtimeMoniker);
+
case RuntimeMoniker.MonoAOTLLVM:
return MakeMonoAOTLLVMJob(baseJob, options, RuntimeInformation.IsNetCore ? CoreRuntime.GetCurrentVersion().MsBuildMoniker : "net6.0");
@@ -575,6 +582,9 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma
case RuntimeMoniker.MonoAOTLLVMNet80:
return MakeMonoAOTLLVMJob(baseJob, options, "net8.0");
+ case RuntimeMoniker.MonoAOTLLVMNet90:
+ return MakeMonoAOTLLVMJob(baseJob, options, "net9.0");
+
case RuntimeMoniker.Mono60:
return MakeMonoJob(baseJob, options, MonoRuntime.Mono60);
@@ -584,6 +594,9 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma
case RuntimeMoniker.Mono80:
return MakeMonoJob(baseJob, options, MonoRuntime.Mono80);
+ case RuntimeMoniker.Mono90:
+ return MakeMonoJob(baseJob, options, MonoRuntime.Mono90);
+
default:
throw new NotSupportedException($"Runtime {runtimeId} is not supported");
}
diff --git a/src/BenchmarkDotNet/Diagnosers/EventPipeProfiler.cs b/src/BenchmarkDotNet/Diagnosers/EventPipeProfiler.cs
index 211d4a06e2..c1c76e8c9e 100644
--- a/src/BenchmarkDotNet/Diagnosers/EventPipeProfiler.cs
+++ b/src/BenchmarkDotNet/Diagnosers/EventPipeProfiler.cs
@@ -40,7 +40,7 @@ public EventPipeProfiler() :this(profile: EventPipeProfile.CpuSampling, performE
/// A named pre-defined set of provider configurations that allows common tracing scenarios to be specified succinctly.
/// A list of EventPipe providers to be enabled.
/// if set to true, benchmarks will be executed one more time with the profiler attached. If set to false, there will be no extra run but the results will contain overhead. True by default.
- public EventPipeProfiler(EventPipeProfile profile = EventPipeProfile.CpuSampling, IReadOnlyCollection providers = null, bool performExtraBenchmarksRun = true)
+ public EventPipeProfiler(EventPipeProfile profile = EventPipeProfile.CpuSampling, IReadOnlyCollection? providers = null, bool performExtraBenchmarksRun = true)
{
this.performExtraBenchmarksRun = performExtraBenchmarksRun;
eventPipeProviders = MapToProviders(profile, providers);
diff --git a/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs b/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs
index 5b15a1e6ef..16ebbde219 100644
--- a/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs
+++ b/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs
@@ -247,7 +247,7 @@ protected override IEnumerable Decode(byte[] code, ulong startAddress, Stat
}
}
}
- TryTranslateAddressToName(address, isPrestubMD, state, isIndirect, depth, currentMethod);
+ TryTranslateAddressToName(address, isPrestubMD, state, depth, currentMethod);
}
accumulator.Feed(instruction);
diff --git a/src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs b/src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs
index 0087b2b9a5..5a0ddccafb 100644
--- a/src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs
+++ b/src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs
@@ -14,10 +14,32 @@ namespace BenchmarkDotNet.Disassemblers
// This Disassembler uses ClrMd v2x. Please keep it in sync with ClrMdV1Disassembler (if possible).
internal abstract class ClrMdV2Disassembler
{
- // Translating an address to a method can cause AV and a process crash (https://github.com/dotnet/BenchmarkDotNet/issues/2070).
- // It was fixed in https://github.com/dotnet/runtime/pull/79846,
- // and most likely will be backported to 7.0.2 very soon (https://github.com/dotnet/runtime/pull/79862).
- protected static readonly bool IsVulnerableToAvInDac = !RuntimeInformation.IsWindows() && Environment.Version < new Version(7, 0, 2);
+ private static readonly ulong MinValidAddress = GetMinValidAddress();
+
+ private static ulong GetMinValidAddress()
+ {
+ // https://github.com/dotnet/BenchmarkDotNet/pull/2413#issuecomment-1688100117
+ if (RuntimeInformation.IsWindows())
+ return ushort.MaxValue + 1;
+ if (RuntimeInformation.IsLinux())
+ return (ulong) Environment.SystemPageSize;
+ if (RuntimeInformation.IsMacOS())
+ return RuntimeInformation.GetCurrentPlatform() switch
+ {
+ Environments.Platform.X86 or Environments.Platform.X64 => 4096,
+ Environments.Platform.Arm64 => 0x100000000,
+ _ => throw new NotSupportedException($"{RuntimeInformation.GetCurrentPlatform()} is not supported")
+ };
+ throw new NotSupportedException($"{System.Runtime.InteropServices.RuntimeInformation.OSDescription} is not supported");
+ }
+
+ private static bool IsValidAddress(ulong address)
+ // -1 (ulong.MaxValue) address is invalid, and will crash the runtime in older runtimes. https://github.com/dotnet/runtime/pull/90794
+ // 0 is NULL and therefore never valid.
+ // Addresses less than the minimum virtual address are also invalid.
+ => address != ulong.MaxValue
+ && address != 0
+ && address >= MinValidAddress;
internal DisassemblyResult AttachAndDisassemble(Settings settings)
{
@@ -245,13 +267,13 @@ protected static bool TryReadNativeCodeAddresses(ClrRuntime runtime, ClrMethod m
return false;
}
- protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD, State state, bool isIndirectCallOrJump, int depth, ClrMethod currentMethod)
+ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD, State state, int depth, ClrMethod currentMethod)
{
- var runtime = state.Runtime;
-
- if (state.AddressToNameMapping.ContainsKey(address))
+ if (!IsValidAddress(address) || state.AddressToNameMapping.ContainsKey(address))
return;
+ var runtime = state.Runtime;
+
var jitHelperFunctionName = runtime.GetJitHelperFunctionName(address);
if (!string.IsNullOrEmpty(jitHelperFunctionName))
{
@@ -260,9 +282,9 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
}
var method = runtime.GetMethodByInstructionPointer(address);
- if (method is null && (address & ((uint)runtime.DataTarget.DataReader.PointerSize - 1)) == 0)
+ if (method is null && (address & ((uint) runtime.DataTarget.DataReader.PointerSize - 1)) == 0)
{
- if (runtime.DataTarget.DataReader.ReadPointer(address, out ulong newAddress) && newAddress > ushort.MaxValue)
+ if (runtime.DataTarget.DataReader.ReadPointer(address, out ulong newAddress) && IsValidAddress(newAddress))
{
method = runtime.GetMethodByInstructionPointer(newAddress);
@@ -276,31 +298,24 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
if (method is null)
{
- if (isAddressPrecodeMD || !IsVulnerableToAvInDac)
+ var methodDescriptor = runtime.GetMethodByHandle(address);
+ if (methodDescriptor is not null)
{
- var methodDescriptor = runtime.GetMethodByHandle(address);
- if (!(methodDescriptor is null))
+ if (isAddressPrecodeMD)
{
- if (isAddressPrecodeMD)
- {
- state.AddressToNameMapping.Add(address, $"Precode of {methodDescriptor.Signature}");
- }
- else
- {
- state.AddressToNameMapping.Add(address, $"MD_{methodDescriptor.Signature}");
- }
- return;
+ state.AddressToNameMapping.Add(address, $"Precode of {methodDescriptor.Signature}");
}
+ else
+ {
+ state.AddressToNameMapping.Add(address, $"MD_{methodDescriptor.Signature}");
+ }
+ return;
}
- if (!IsVulnerableToAvInDac)
+ var methodTableName = runtime.DacLibrary.SOSDacInterface.GetMethodTableName(address);
+ if (!string.IsNullOrEmpty(methodTableName))
{
- var methodTableName = runtime.DacLibrary.SOSDacInterface.GetMethodTableName(address);
- if (!string.IsNullOrEmpty(methodTableName))
- {
- state.AddressToNameMapping.Add(address, $"MT_{methodTableName}");
- return;
- }
+ state.AddressToNameMapping.Add(address, $"MT_{methodTableName}");
}
return;
}
diff --git a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs
index 2c6eb62f79..31e7570e86 100644
--- a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs
+++ b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs
@@ -109,24 +109,31 @@ public IEnumerable Validate(ValidationParameters validationPara
yield return new ValidationError(true, "Currently NativeAOT has no DisassemblyDiagnoser support", benchmark);
}
- if (RuntimeInformation.IsLinux() && ShouldUseClrMdDisassembler(benchmark))
+ if (ShouldUseClrMdDisassembler(benchmark))
{
- var runtime = benchmark.Job.ResolveValue(EnvironmentMode.RuntimeCharacteristic, EnvironmentResolver.Instance);
-
- if (runtime.RuntimeMoniker < RuntimeMoniker.NetCoreApp30)
- {
- yield return new ValidationError(true, $"{nameof(DisassemblyDiagnoser)} supports only .NET Core 3.0+", benchmark);
- }
-
- if (ptrace_scope.Value == "2")
+ if (RuntimeInformation.IsLinux())
{
- yield return new ValidationError(false, $"ptrace_scope is set to 2, {nameof(DisassemblyDiagnoser)} is going to work only if you run as sudo");
- }
- else if (ptrace_scope.Value == "3")
- {
- yield return new ValidationError(true, $"ptrace_scope is set to 3, {nameof(DisassemblyDiagnoser)} is not going to work");
+ var runtime = benchmark.Job.ResolveValue(EnvironmentMode.RuntimeCharacteristic, EnvironmentResolver.Instance);
+
+ if (runtime.RuntimeMoniker < RuntimeMoniker.NetCoreApp30)
+ {
+ yield return new ValidationError(true, $"{nameof(DisassemblyDiagnoser)} supports only .NET Core 3.0+", benchmark);
+ }
+
+ if (ptrace_scope.Value == "2")
+ {
+ yield return new ValidationError(false, $"ptrace_scope is set to 2, {nameof(DisassemblyDiagnoser)} is going to work only if you run as sudo");
+ }
+ else if (ptrace_scope.Value == "3")
+ {
+ yield return new ValidationError(true, $"ptrace_scope is set to 3, {nameof(DisassemblyDiagnoser)} is not going to work");
+ }
}
}
+ else if (!ShouldUseMonoDisassembler(benchmark))
+ {
+ yield return new ValidationError(true, $"Only Windows and Linux are supported in DisassemblyDiagnoser without Mono. Current OS is {System.Runtime.InteropServices.RuntimeInformation.OSDescription}");
+ }
}
}
diff --git a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoserConfig.cs b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoserConfig.cs
index 7c917c80b0..feff76ef99 100644
--- a/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoserConfig.cs
+++ b/src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoserConfig.cs
@@ -22,8 +22,8 @@ public class DisassemblyDiagnoserConfig
public DisassemblyDiagnoserConfig(
int maxDepth = 1,
DisassemblySyntax syntax = DisassemblySyntax.Masm,
- string[] filters = null,
- FormatterOptions formatterOptions = null,
+ string[]? filters = null,
+ FormatterOptions? formatterOptions = null,
bool printSource = false,
bool printInstructionAddresses = false,
bool exportGithubMarkdown = true,
diff --git a/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs b/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs
index 2c024f587d..f29faae037 100644
--- a/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs
+++ b/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs
@@ -109,14 +109,7 @@ protected override IEnumerable Decode(byte[] code, ulong startAddress, Stat
}
}
}
-
- if (address > ushort.MaxValue)
- {
- if (!IsVulnerableToAvInDac || IsCallOrJump(instruction))
- {
- TryTranslateAddressToName(address, isPrestubMD, state, isIndirect, depth, currentMethod);
- }
- }
+ TryTranslateAddressToName(address, isPrestubMD, state, depth, currentMethod);
}
yield return new IntelAsm
@@ -130,17 +123,6 @@ protected override IEnumerable Decode(byte[] code, ulong startAddress, Stat
}
}
- private static bool IsCallOrJump(Instruction instruction)
- => instruction.FlowControl switch
- {
- FlowControl.Call => true,
- FlowControl.IndirectCall => true,
- FlowControl.ConditionalBranch => true,
- FlowControl.IndirectBranch => true,
- FlowControl.UnconditionalBranch => true,
- _ => false
- };
-
private static bool TryGetReferencedAddress(Instruction instruction, uint pointerSize, out ulong referencedAddress)
{
for (int i = 0; i < instruction.OpCount; i++)
diff --git a/src/BenchmarkDotNet/Disassemblers/MonoDisassembler.cs b/src/BenchmarkDotNet/Disassemblers/MonoDisassembler.cs
index e8bae27870..de0e35b6dd 100644
--- a/src/BenchmarkDotNet/Disassemblers/MonoDisassembler.cs
+++ b/src/BenchmarkDotNet/Disassemblers/MonoDisassembler.cs
@@ -128,7 +128,7 @@ private static DisassemblyResult CreateErrorResult(IReadOnlyList input,
//line example 2: 0000000000000000 subq $0x28, %rsp
private static readonly Regex InstructionRegex = new Regex(@"\s*(?[0-9a-f]+)(\:\s+([0-9a-f]{2}\s+)+)?\s+(?.*)\s*", RegexOptions.Compiled);
- private static bool TryParseInstruction(string line, out MonoCode instruction)
+ private static bool TryParseInstruction(string line, out MonoCode? instruction)
{
instruction = null;
var match = InstructionRegex.Match(line);
diff --git a/src/BenchmarkDotNet/Disassemblers/SourceCodeProvider.cs b/src/BenchmarkDotNet/Disassemblers/SourceCodeProvider.cs
index 55e31cf8b8..777c24370e 100644
--- a/src/BenchmarkDotNet/Disassemblers/SourceCodeProvider.cs
+++ b/src/BenchmarkDotNet/Disassemblers/SourceCodeProvider.cs
@@ -150,7 +150,7 @@ private static ManagedSymbolModule GetReaderForMethod(ClrMethod method)
ClrModule module = method?.Type?.Module;
PdbInfo info = module?.Pdb;
- ManagedSymbolModule reader = null;
+ ManagedSymbolModule? reader = null;
if (info != null)
{
if (!s_pdbReaders.TryGetValue(info, out reader))
diff --git a/src/BenchmarkDotNet/Engines/AnonymousPipesHost.cs b/src/BenchmarkDotNet/Engines/AnonymousPipesHost.cs
index 6add899105..760ebb4057 100644
--- a/src/BenchmarkDotNet/Engines/AnonymousPipesHost.cs
+++ b/src/BenchmarkDotNet/Engines/AnonymousPipesHost.cs
@@ -63,7 +63,7 @@ public void SendSignal(HostSignal hostSignal)
public void ReportResults(RunResults runResults) => runResults.Print(outWriter);
[PublicAPI] // called from generated code
- public static bool TryGetFileHandles(string[] args, out string writeHandle, out string readHandle)
+ public static bool TryGetFileHandles(string[] args, out string? writeHandle, out string? readHandle)
{
for (int i = 0; i < args.Length; i++)
{
diff --git a/src/BenchmarkDotNet/Engines/Consumer.cs b/src/BenchmarkDotNet/Engines/Consumer.cs
index 1abedec0d6..55f8e3b040 100644
--- a/src/BenchmarkDotNet/Engines/Consumer.cs
+++ b/src/BenchmarkDotNet/Engines/Consumer.cs
@@ -31,7 +31,7 @@ private static readonly HashSet SupportedTypes
private double doubleHolder;
private long longHolder;
private ulong ulongHolder;
- private volatile object objectHolder;
+ private volatile object? objectHolder;
private volatile IntPtr ptrHolder;
private volatile UIntPtr uptrHolder;
#pragma warning restore IDE0052 // Remove unread private members
@@ -157,7 +157,7 @@ public void Consume(in T value)
internal static bool IsConsumable(Type type)
=> SupportedTypes.Contains(type) || type.GetTypeInfo().IsClass || type.GetTypeInfo().IsInterface;
- internal static bool HasConsumableField(Type type, out FieldInfo consumableField)
+ internal static bool HasConsumableField(Type type, out FieldInfo? consumableField)
{
var typeInfo = type.GetTypeInfo();
diff --git a/src/BenchmarkDotNet/Environments/OsBrandStringHelper.cs b/src/BenchmarkDotNet/Environments/OsBrandStringHelper.cs
index b82575f35c..d4369120eb 100644
--- a/src/BenchmarkDotNet/Environments/OsBrandStringHelper.cs
+++ b/src/BenchmarkDotNet/Environments/OsBrandStringHelper.cs
@@ -160,6 +160,7 @@ public string ToPrettifiedString(int? ubr)
// Windows 11
new Windows1XVersion("21H2", "Sun Valley", null, 22000),
new Windows1XVersion("22H2", "Sun Valley 2", "2022 Update", 22621),
+ new Windows1XVersion("23H2", "Sun Valley 3", "2023 Update", 22631),
};
public static Windows1XVersion? Resolve(string osVersionString)
diff --git a/src/BenchmarkDotNet/Environments/ProcessorBrandStringHelper.cs b/src/BenchmarkDotNet/Environments/ProcessorBrandStringHelper.cs
index 17f79e3202..882078df74 100644
--- a/src/BenchmarkDotNet/Environments/ProcessorBrandStringHelper.cs
+++ b/src/BenchmarkDotNet/Environments/ProcessorBrandStringHelper.cs
@@ -94,7 +94,7 @@ private static string GetBrandStyledActualFrequency(Frequency? frequency)
{
var data = ResourceHelper.LoadResource("BenchmarkDotNet.Environments.microarchitectures.txt").Split('\r', '\n');
var dictionary = new Dictionary();
- string currentMicroarchitecture = null;
+ string? currentMicroarchitecture = null;
foreach (string line in data)
{
if (line.StartsWith("//") || string.IsNullOrWhiteSpace(line))
diff --git a/src/BenchmarkDotNet/Environments/Runtimes/ClrRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/ClrRuntime.cs
index 35460ce853..be5a84cce2 100644
--- a/src/BenchmarkDotNet/Environments/Runtimes/ClrRuntime.cs
+++ b/src/BenchmarkDotNet/Environments/Runtimes/ClrRuntime.cs
@@ -17,7 +17,7 @@ public class ClrRuntime : Runtime, IEquatable
public string Version { get; }
- private ClrRuntime(RuntimeMoniker runtimeMoniker, string msBuildMoniker, string displayName, string version = null)
+ private ClrRuntime(RuntimeMoniker runtimeMoniker, string msBuildMoniker, string displayName, string? version = null)
: base(runtimeMoniker, msBuildMoniker, displayName)
{
Version = version;
diff --git a/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs
index bd6923d95d..d86e4b0f40 100644
--- a/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs
+++ b/src/BenchmarkDotNet/Environments/Runtimes/CoreRuntime.cs
@@ -20,8 +20,9 @@ public class CoreRuntime : Runtime
public static readonly CoreRuntime Core60 = new (RuntimeMoniker.Net60, "net6.0", ".NET 6.0");
public static readonly CoreRuntime Core70 = new (RuntimeMoniker.Net70, "net7.0", ".NET 7.0");
public static readonly CoreRuntime Core80 = new (RuntimeMoniker.Net80, "net8.0", ".NET 8.0");
+ public static readonly CoreRuntime Core90 = new (RuntimeMoniker.Net90, "net9.0", ".NET 9.0");
- public static CoreRuntime Latest => Core80; // when dotnet/runtime branches for 9.0, this will need to get updated
+ public static CoreRuntime Latest => Core90; // when dotnet/runtime branches for 10.0, this will need to get updated
private CoreRuntime(RuntimeMoniker runtimeMoniker, string msBuildMoniker, string displayName)
: base(runtimeMoniker, msBuildMoniker, displayName)
@@ -72,12 +73,13 @@ internal static CoreRuntime FromVersion(Version version)
case Version v when v.Major == 6 && v.Minor == 0: return GetPlatformSpecific(Core60);
case Version v when v.Major == 7 && v.Minor == 0: return GetPlatformSpecific(Core70);
case Version v when v.Major == 8 && v.Minor == 0: return GetPlatformSpecific(Core80);
+ case Version v when v.Major == 9 && v.Minor == 0: return GetPlatformSpecific(Core90);
default:
return CreateForNewVersion($"net{version.Major}.{version.Minor}", $".NET {version.Major}.{version.Minor}");
}
}
- internal static bool TryGetVersion(out Version version)
+ internal static bool TryGetVersion(out Version? version)
{
// we can't just use System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription
// because it can be null and it reports versions like 4.6.* for .NET Core 2.*
@@ -124,7 +126,7 @@ internal static bool TryGetVersion(out Version version)
// sample input:
// for dotnet run: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.1.12\
// for dotnet publish: C:\Users\adsitnik\source\repos\ConsoleApp25\ConsoleApp25\bin\Release\netcoreapp2.0\win-x64\publish\
- internal static bool TryGetVersionFromRuntimeDirectory(string runtimeDirectory, out Version version)
+ internal static bool TryGetVersionFromRuntimeDirectory(string runtimeDirectory, out Version? version)
{
if (!string.IsNullOrEmpty(runtimeDirectory) && Version.TryParse(GetParsableVersionPart(new DirectoryInfo(runtimeDirectory).Name), out version))
{
@@ -141,7 +143,7 @@ internal static bool TryGetVersionFromRuntimeDirectory(string runtimeDirectory,
// 2.2: 4.6.27817.03 @BuiltBy: dlab14-DDVSOWINAGE101 @Branch: release/2.2 @SrcCode: https://github.com/dotnet/coreclr/tree/ce1d090d33b400a25620c0145046471495067cc7, Microsoft .NET Framework
// 3.0: 3.0.0-preview8.19379.2+ac25be694a5385a6a1496db40de932df0689b742, Microsoft .NET Core
// 5.0: 5.0.0-alpha1.19413.7+0ecefa44c9d66adb8a997d5778dc6c246ad393a7, Microsoft .NET Core
- internal static bool TryGetVersionFromProductInfo(string productVersion, string productName, out Version version)
+ internal static bool TryGetVersionFromProductInfo(string productVersion, string productName, out Version? version)
{
if (!string.IsNullOrEmpty(productVersion) && !string.IsNullOrEmpty(productName))
{
@@ -175,7 +177,7 @@ internal static bool TryGetVersionFromProductInfo(string productVersion, string
// sample input:
// .NETCoreApp,Version=v2.0
// .NETCoreApp,Version=v2.1
- internal static bool TryGetVersionFromFrameworkName(string frameworkName, out Version version)
+ internal static bool TryGetVersionFromFrameworkName(string frameworkName, out Version? version)
{
const string versionPrefix = ".NETCoreApp,Version=v";
if (!string.IsNullOrEmpty(frameworkName) && frameworkName.StartsWith(versionPrefix))
diff --git a/src/BenchmarkDotNet/Environments/Runtimes/MonoRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/MonoRuntime.cs
index 597e18b90c..5862d1390c 100644
--- a/src/BenchmarkDotNet/Environments/Runtimes/MonoRuntime.cs
+++ b/src/BenchmarkDotNet/Environments/Runtimes/MonoRuntime.cs
@@ -9,6 +9,7 @@ public class MonoRuntime : Runtime, IEquatable
public static readonly MonoRuntime Mono60 = new ("Mono with .NET 6.0", RuntimeMoniker.Mono60, "net6.0", isDotNetBuiltIn: true);
public static readonly MonoRuntime Mono70 = new ("Mono with .NET 7.0", RuntimeMoniker.Mono70, "net7.0", isDotNetBuiltIn: true);
public static readonly MonoRuntime Mono80 = new ("Mono with .NET 8.0", RuntimeMoniker.Mono80, "net8.0", isDotNetBuiltIn: true);
+ public static readonly MonoRuntime Mono90 = new ("Mono with .NET 9.0", RuntimeMoniker.Mono90, "net9.0", isDotNetBuiltIn: true);
public string CustomPath { get; }
diff --git a/src/BenchmarkDotNet/Environments/Runtimes/NativeAotRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/NativeAotRuntime.cs
index 063210b37c..f1f49c5dca 100644
--- a/src/BenchmarkDotNet/Environments/Runtimes/NativeAotRuntime.cs
+++ b/src/BenchmarkDotNet/Environments/Runtimes/NativeAotRuntime.cs
@@ -18,6 +18,10 @@ public class NativeAotRuntime : Runtime
/// NativeAOT compiled as net8.0
///
public static readonly NativeAotRuntime Net80 = new NativeAotRuntime(RuntimeMoniker.NativeAot80, "net8.0", "NativeAOT 8.0");
+ ///
+ /// NativeAOT compiled as net9.0
+ ///
+ public static readonly NativeAotRuntime Net90 = new NativeAotRuntime(RuntimeMoniker.NativeAot90, "net9.0", "NativeAOT 9.0");
public override bool IsAOT => true;
@@ -42,6 +46,8 @@ public static NativeAotRuntime GetCurrentVersion()
{
case Version v when v.Major == 6 && v.Minor == 0: return Net60;
case Version v when v.Major == 7 && v.Minor == 0: return Net70;
+ case Version v when v.Major == 8 && v.Minor == 0: return Net80;
+ case Version v when v.Major == 9 && v.Minor == 0: return Net90;
default:
return new NativeAotRuntime(RuntimeMoniker.NotRecognized, $"net{version.Major}.{version.Minor}", $"NativeAOT {version.Major}.{version.Minor}");
}
diff --git a/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs
index bb6c122700..3fcc07f26a 100644
--- a/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs
+++ b/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs
@@ -28,7 +28,7 @@ public class WasmRuntime : Runtime, IEquatable
/// Specifies whether AOT or Interpreter (default) project should be generated.
/// Specifies a wasm data directory surfaced as $(WasmDataDir) for the project
/// Runtime moniker
- public WasmRuntime(string msBuildMoniker = "net5.0", string displayName = "Wasm", string javaScriptEngine = "v8", string javaScriptEngineArguments = "--expose_wasm", bool aot = false, string wasmDataDir = null, RuntimeMoniker moniker = RuntimeMoniker.Wasm) : base(moniker, msBuildMoniker, displayName)
+ public WasmRuntime(string msBuildMoniker = "net5.0", string displayName = "Wasm", string javaScriptEngine = "v8", string javaScriptEngineArguments = "--expose_wasm", bool aot = false, string? wasmDataDir = null, RuntimeMoniker moniker = RuntimeMoniker.Wasm) : base(moniker, msBuildMoniker, displayName)
{
if (!string.IsNullOrEmpty(javaScriptEngine) && javaScriptEngine != "v8" && !File.Exists(javaScriptEngine))
throw new FileNotFoundException($"Provided {nameof(javaScriptEngine)} file: \"{javaScriptEngine}\" doest NOT exist");
diff --git a/src/BenchmarkDotNet/EventProcessors/CompositeEventProcessor.cs b/src/BenchmarkDotNet/EventProcessors/CompositeEventProcessor.cs
new file mode 100644
index 0000000000..a73ea54636
--- /dev/null
+++ b/src/BenchmarkDotNet/EventProcessors/CompositeEventProcessor.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+using BenchmarkDotNet.Extensions;
+using BenchmarkDotNet.Reports;
+using BenchmarkDotNet.Running;
+using BenchmarkDotNet.Toolchains.Results;
+using BenchmarkDotNet.Validators;
+
+namespace BenchmarkDotNet.EventProcessors
+{
+ internal sealed class CompositeEventProcessor : EventProcessor
+ {
+ private readonly HashSet eventProcessors;
+
+ public CompositeEventProcessor(BenchmarkRunInfo[] benchmarkRunInfos)
+ {
+ var eventProcessors = new HashSet();
+
+ foreach (var info in benchmarkRunInfos)
+ eventProcessors.AddRange(info.Config.GetEventProcessors());
+
+ this.eventProcessors = eventProcessors;
+ }
+
+ public override void OnStartValidationStage()
+ {
+ foreach (var eventProcessor in eventProcessors)
+ eventProcessor.OnStartValidationStage();
+ }
+
+ public override void OnValidationError(ValidationError validationError)
+ {
+ foreach (var eventProcessor in eventProcessors)
+ eventProcessor.OnValidationError(validationError);
+ }
+
+ public override void OnEndValidationStage()
+ {
+ foreach (var eventProcessor in eventProcessors)
+ eventProcessor.OnEndValidationStage();
+ }
+
+ public override void OnStartBuildStage(IReadOnlyList partitions)
+ {
+ foreach (var eventProcessor in eventProcessors)
+ eventProcessor.OnStartBuildStage(partitions);
+ }
+
+ public override void OnBuildComplete(BuildPartition buildPartition, BuildResult buildResult)
+ {
+ foreach (var eventProcessor in eventProcessors)
+ eventProcessor.OnBuildComplete(buildPartition, buildResult);
+ }
+
+ public override void OnEndBuildStage()
+ {
+ foreach (var eventProcessor in eventProcessors)
+ eventProcessor.OnEndBuildStage();
+ }
+
+ public override void OnStartRunStage()
+ {
+ foreach (var eventProcessor in eventProcessors)
+ eventProcessor.OnStartRunStage();
+ }
+
+ public override void OnEndRunStage()
+ {
+ foreach (var eventProcessor in eventProcessors)
+ eventProcessor.OnEndRunStage();
+ }
+
+ public override void OnStartRunBenchmarksInType(Type type, IReadOnlyList benchmarks)
+ {
+ foreach (var eventProcessor in eventProcessors)
+ eventProcessor.OnStartRunBenchmarksInType(type, benchmarks);
+ }
+
+ public override void OnEndRunBenchmarksInType(Type type, Summary summary)
+ {
+ foreach (var eventProcessor in eventProcessors)
+ eventProcessor.OnEndRunBenchmarksInType(type, summary);
+ }
+
+ public override void OnEndRunBenchmark(BenchmarkCase benchmarkCase, BenchmarkReport report)
+ {
+ foreach (var eventProcessor in eventProcessors)
+ eventProcessor.OnEndRunBenchmark(benchmarkCase, report);
+ }
+
+ public override void OnStartRunBenchmark(BenchmarkCase benchmarkCase)
+ {
+ foreach (var eventProcessor in eventProcessors)
+ eventProcessor.OnStartRunBenchmark(benchmarkCase);
+ }
+ }
+}
diff --git a/src/BenchmarkDotNet/EventProcessors/EventProcessor.cs b/src/BenchmarkDotNet/EventProcessors/EventProcessor.cs
new file mode 100644
index 0000000000..339b98fa5e
--- /dev/null
+++ b/src/BenchmarkDotNet/EventProcessors/EventProcessor.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using BenchmarkDotNet.Reports;
+using BenchmarkDotNet.Running;
+using BenchmarkDotNet.Toolchains.Results;
+using BenchmarkDotNet.Validators;
+
+namespace BenchmarkDotNet.EventProcessors
+{
+ public abstract class EventProcessor
+ {
+ public virtual void OnStartValidationStage() { }
+ public virtual void OnValidationError(ValidationError validationError) { }
+ public virtual void OnEndValidationStage() { }
+ public virtual void OnStartBuildStage(IReadOnlyList partitions) { }
+ public virtual void OnBuildComplete(BuildPartition partition, BuildResult buildResult) { }
+ public virtual void OnEndBuildStage() { }
+ public virtual void OnStartRunStage() { }
+ public virtual void OnStartRunBenchmarksInType(Type type, IReadOnlyList benchmarks) { }
+ public virtual void OnEndRunBenchmarksInType(Type type, Summary summary) { }
+ public virtual void OnStartRunBenchmark(BenchmarkCase benchmarkCase) { }
+ public virtual void OnEndRunBenchmark(BenchmarkCase benchmarkCase, BenchmarkReport report) { }
+ public virtual void OnEndRunStage() { }
+ }
+}
diff --git a/src/BenchmarkDotNet/Exporters/Csv/CsvMeasurementsExporter.cs b/src/BenchmarkDotNet/Exporters/Csv/CsvMeasurementsExporter.cs
index 74f6130f1c..0d07c9058c 100644
--- a/src/BenchmarkDotNet/Exporters/Csv/CsvMeasurementsExporter.cs
+++ b/src/BenchmarkDotNet/Exporters/Csv/CsvMeasurementsExporter.cs
@@ -20,7 +20,7 @@ public class CsvMeasurementsExporter : ExporterBase
private static readonly Lazy Columns = new Lazy(BuildColumns);
private readonly CsvSeparator separator;
- public CsvMeasurementsExporter(CsvSeparator separator, SummaryStyle style = null)
+ public CsvMeasurementsExporter(CsvSeparator separator, SummaryStyle? style = null)
{
this.separator = separator;
Style = style ?? SummaryStyle.Default;
diff --git a/src/BenchmarkDotNet/Exporters/Xml/XmlSerializer.cs b/src/BenchmarkDotNet/Exporters/Xml/XmlSerializer.cs
index 1f4160393c..9ccbdd2323 100644
--- a/src/BenchmarkDotNet/Exporters/Xml/XmlSerializer.cs
+++ b/src/BenchmarkDotNet/Exporters/Xml/XmlSerializer.cs
@@ -110,7 +110,7 @@ private void WriteCollectionProperty(object source, PropertyInfo property)
writer.WriteStartElement(property.Name);
- string itemName = null;
+ string? itemName = null;
foreach (var item in collection)
{
diff --git a/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs b/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs
index 607e8c33fb..912babeb39 100644
--- a/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs
+++ b/src/BenchmarkDotNet/Extensions/ProcessExtensions.cs
@@ -32,7 +32,7 @@ public static void EnsureHighPriority(this Process process, ILogger logger)
}
catch (Exception ex)
{
- logger.WriteLineError($"Failed to set up high priority. Make sure you have the right permissions. Message: {ex.Message}");
+ logger.WriteLineInfo($"// Failed to set up high priority ({ex.Message}). In order to run benchmarks with high priority, make sure you have the right permissions.");
}
}
diff --git a/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs b/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs
index 60fa23df3e..5ba51eebdd 100644
--- a/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs
+++ b/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs
@@ -45,6 +45,8 @@ internal static Runtime GetRuntime(this RuntimeMoniker runtimeMoniker)
return CoreRuntime.Core70;
case RuntimeMoniker.Net80:
return CoreRuntime.Core80;
+ case RuntimeMoniker.Net90:
+ return CoreRuntime.Core90;
case RuntimeMoniker.Mono:
return MonoRuntime.Default;
case RuntimeMoniker.NativeAot60:
@@ -53,10 +55,16 @@ internal static Runtime GetRuntime(this RuntimeMoniker runtimeMoniker)
return NativeAotRuntime.Net70;
case RuntimeMoniker.NativeAot80:
return NativeAotRuntime.Net80;
+ case RuntimeMoniker.NativeAot90:
+ return NativeAotRuntime.Net90;
case RuntimeMoniker.Mono60:
return MonoRuntime.Mono60;
case RuntimeMoniker.Mono70:
return MonoRuntime.Mono70;
+ case RuntimeMoniker.Mono80:
+ return MonoRuntime.Mono80;
+ case RuntimeMoniker.Mono90:
+ return MonoRuntime.Mono90;
default:
throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, "Runtime Moniker not supported");
}
diff --git a/src/BenchmarkDotNet/Helpers/ProcessHelper.cs b/src/BenchmarkDotNet/Helpers/ProcessHelper.cs
index 114ae287fb..1b75c6392f 100644
--- a/src/BenchmarkDotNet/Helpers/ProcessHelper.cs
+++ b/src/BenchmarkDotNet/Helpers/ProcessHelper.cs
@@ -12,7 +12,7 @@ internal static class ProcessHelper
/// Run external process and return the console output.
/// In the case of any exception, null will be returned.
///
- internal static string? RunAndReadOutput(string fileName, string arguments = "", ILogger logger = null)
+ internal static string? RunAndReadOutput(string fileName, string arguments = "", ILogger? logger = null)
{
var processStartInfo = new ProcessStartInfo
{
@@ -42,7 +42,7 @@ internal static class ProcessHelper
}
internal static (int exitCode, ImmutableArray output) RunAndReadOutputLineByLine(string fileName, string arguments = "", string workingDirectory = "",
- Dictionary environmentVariables = null, bool includeErrors = false, ILogger logger = null)
+ Dictionary? environmentVariables = null, bool includeErrors = false, ILogger? logger = null)
{
var processStartInfo = new ProcessStartInfo
{
diff --git a/src/BenchmarkDotNet/Jobs/JobExtensions.cs b/src/BenchmarkDotNet/Jobs/JobExtensions.cs
index 8cbf7445d7..2a236dc44f 100644
--- a/src/BenchmarkDotNet/Jobs/JobExtensions.cs
+++ b/src/BenchmarkDotNet/Jobs/JobExtensions.cs
@@ -339,7 +339,7 @@ public static Job WithEnvironmentVariable(this Job job, string key, string value
/// (optional)Indicate the URI of the NuGet package source to use during the restore operation.
/// (optional)Allows prerelease packages to be installed.
///
- public static Job WithNuGet(this Job job, string packageName, string packageVersion = null, Uri source = null, bool prerelease = false) =>
+ public static Job WithNuGet(this Job job, string packageName, string? packageVersion = null, Uri? source = null, bool prerelease = false) =>
job.WithCore(j => j.Infrastructure.NuGetReferences =
new NuGetReferenceList(j.Infrastructure.NuGetReferences ?? Array.Empty())
{
diff --git a/src/BenchmarkDotNet/Jobs/NugetReference.cs b/src/BenchmarkDotNet/Jobs/NugetReference.cs
index 3e5819c4ae..0db3a028de 100644
--- a/src/BenchmarkDotNet/Jobs/NugetReference.cs
+++ b/src/BenchmarkDotNet/Jobs/NugetReference.cs
@@ -5,7 +5,7 @@ namespace BenchmarkDotNet.Jobs
{
public class NuGetReference : IEquatable
{
- public NuGetReference(string packageName, string packageVersion, Uri source = null, bool prerelease = false)
+ public NuGetReference(string packageName, string packageVersion, Uri? source = null, bool prerelease = false)
{
if (string.IsNullOrWhiteSpace(packageName))
throw new ArgumentException("message", nameof(packageName));
diff --git a/src/BenchmarkDotNet/Loggers/AsyncProcessOutputReader.cs b/src/BenchmarkDotNet/Loggers/AsyncProcessOutputReader.cs
index a503da8e76..d4a059153d 100644
--- a/src/BenchmarkDotNet/Loggers/AsyncProcessOutputReader.cs
+++ b/src/BenchmarkDotNet/Loggers/AsyncProcessOutputReader.cs
@@ -19,7 +19,7 @@ internal class AsyncProcessOutputReader : IDisposable
private long status;
- internal AsyncProcessOutputReader(Process process, bool logOutput = false, ILogger logger = null, bool readStandardError = true)
+ internal AsyncProcessOutputReader(Process process, bool logOutput = false, ILogger? logger = null, bool readStandardError = true)
{
if (!process.StartInfo.RedirectStandardOutput)
throw new NotSupportedException("set RedirectStandardOutput to true first");
diff --git a/src/BenchmarkDotNet/Loggers/Broker.cs b/src/BenchmarkDotNet/Loggers/Broker.cs
index 9055523340..cec11a3091 100644
--- a/src/BenchmarkDotNet/Loggers/Broker.cs
+++ b/src/BenchmarkDotNet/Loggers/Broker.cs
@@ -78,9 +78,8 @@ private void ProcessDataBlocking()
using StreamWriter writer = new (acknowledgments, AnonymousPipesHost.UTF8NoBOM, bufferSize: 1);
// Flush the data to the Stream after each write, otherwise the client will wait for input endlessly!
writer.AutoFlush = true;
- string line = null;
- while ((line = reader.ReadLine()) is not null)
+ while (reader.ReadLine() is { } line)
{
// TODO: implement Silent mode here
logger.WriteLine(LogKind.Default, line);
diff --git a/src/BenchmarkDotNet/Loggers/ConsoleLogger.cs b/src/BenchmarkDotNet/Loggers/ConsoleLogger.cs
index a54bf88edc..a1c7795d53 100644
--- a/src/BenchmarkDotNet/Loggers/ConsoleLogger.cs
+++ b/src/BenchmarkDotNet/Loggers/ConsoleLogger.cs
@@ -21,7 +21,7 @@ private static readonly bool ConsoleSupportsColors
private readonly Dictionary colorScheme;
[PublicAPI]
- public ConsoleLogger(bool unicodeSupport = false, Dictionary colorScheme = null)
+ public ConsoleLogger(bool unicodeSupport = false, Dictionary? colorScheme = null)
{
this.unicodeSupport = unicodeSupport;
this.colorScheme = colorScheme ?? CreateColorfulScheme();
diff --git a/src/BenchmarkDotNet/Loggers/LinqPadLogger.cs b/src/BenchmarkDotNet/Loggers/LinqPadLogger.cs
index 930d19d4ca..01fa682c99 100644
--- a/src/BenchmarkDotNet/Loggers/LinqPadLogger.cs
+++ b/src/BenchmarkDotNet/Loggers/LinqPadLogger.cs
@@ -16,7 +16,7 @@ public sealed class LinqPadLogger : ILogger
public static readonly Lazy lazyInstance = new Lazy(() =>
{
// Detect if being run from LINQPad; see https://github.com/dotnet/BenchmarkDotNet/issues/445#issuecomment-300723741
- MethodInfo withStyle = null;
+ MethodInfo? withStyle = null;
if (AppDomain.CurrentDomain.FriendlyName.StartsWith("LINQPad", StringComparison.OrdinalIgnoreCase))
{
try
diff --git a/src/BenchmarkDotNet/Order/DefaultOrderer.cs b/src/BenchmarkDotNet/Order/DefaultOrderer.cs
index 0bca3cf3ec..656de7ce98 100644
--- a/src/BenchmarkDotNet/Order/DefaultOrderer.cs
+++ b/src/BenchmarkDotNet/Order/DefaultOrderer.cs
@@ -37,7 +37,7 @@ public DefaultOrderer(
[PublicAPI]
public virtual IEnumerable GetExecutionOrder(
ImmutableArray benchmarkCases,
- IEnumerable order = null)
+ IEnumerable? order = null)
{
var benchmarkComparer = new BenchmarkComparer(categoryComparer, paramsComparer, jobComparer, targetComparer, order);
var list = benchmarkCases.ToList();
@@ -137,7 +137,7 @@ public string GetLogicalGroupKey(ImmutableArray allBenchmarksCase
public virtual IEnumerable> GetLogicalGroupOrder(
IEnumerable> logicalGroups,
- IEnumerable order = null)
+ IEnumerable? order = null)
{
var benchmarkComparer = new BenchmarkComparer(categoryComparer, paramsComparer, jobComparer, targetComparer, order);
var logicalGroupComparer = new LogicalGroupComparer(benchmarkComparer);
diff --git a/src/BenchmarkDotNet/Order/IOrderer.cs b/src/BenchmarkDotNet/Order/IOrderer.cs
index dece54e519..bfc6ba5433 100644
--- a/src/BenchmarkDotNet/Order/IOrderer.cs
+++ b/src/BenchmarkDotNet/Order/IOrderer.cs
@@ -11,7 +11,7 @@ namespace BenchmarkDotNet.Order
public interface IOrderer
{
[PublicAPI]
- IEnumerable GetExecutionOrder(ImmutableArray benchmarksCase, IEnumerable order = null);
+ IEnumerable GetExecutionOrder(ImmutableArray benchmarksCase, IEnumerable? order = null);
[PublicAPI]
IEnumerable GetSummaryOrder(ImmutableArray benchmarksCases, Summary summary);
@@ -24,7 +24,7 @@ public interface IOrderer
[PublicAPI]
IEnumerable> GetLogicalGroupOrder(IEnumerable> logicalGroups,
- IEnumerable order = null);
+ IEnumerable? order = null);
[PublicAPI]
bool SeparateLogicalGroups { get; }
diff --git a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs
index d9ae69010e..6064b24dfa 100644
--- a/src/BenchmarkDotNet/Portability/RuntimeInformation.cs
+++ b/src/BenchmarkDotNet/Portability/RuntimeInformation.cs
@@ -185,7 +185,9 @@ public static string GetOsVersion()
return null;
try
{
- return LinuxOsReleaseHelper.GetNameByOsRelease(File.ReadAllLines("/etc/os-release"));
+ string version = LinuxOsReleaseHelper.GetNameByOsRelease(File.ReadAllLines("/etc/os-release"));
+ bool wsl = IsUnderWsl();
+ return wsl ? version + " WSL" : version;
}
catch (Exception)
{
@@ -193,6 +195,20 @@ public static string GetOsVersion()
}
}
+ private static bool IsUnderWsl()
+ {
+ if (!IsLinux())
+ return false;
+ try
+ {
+ return File.Exists("/proc/sys/fs/binfmt_misc/WSLInterop"); // https://superuser.com/a/1749811
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
// TODO: Introduce a common util API for registry calls, use it also in BenchmarkDotNet.Toolchains.CsProj.GetCurrentVersionBasedOnWindowsRegistry
///
/// On Windows, this method returns UBR (Update Build Revision) based on Registry.
diff --git a/src/BenchmarkDotNet/Properties/BenchmarkDotNetInfo.cs b/src/BenchmarkDotNet/Properties/BenchmarkDotNetInfo.cs
index 0b9d0e6f44..fcb01a0055 100644
--- a/src/BenchmarkDotNet/Properties/BenchmarkDotNetInfo.cs
+++ b/src/BenchmarkDotNet/Properties/BenchmarkDotNetInfo.cs
@@ -11,7 +11,7 @@ public class BenchmarkDotNetInfo
var assembly = typeof(BenchmarkDotNetInfo).GetTypeInfo().Assembly;
var assemblyVersion = assembly.GetName().Version;
string informationVersion = assembly.GetCustomAttribute().InformationalVersion ?? "";
- return new BenchmarkDotNetInfo(assemblyVersion, informationVersion);
+ return new BenchmarkDotNetInfo(assemblyVersion, RemoveVersionMetadata(informationVersion));
});
public static BenchmarkDotNetInfo Instance { get; } = LazyInstance.Value;
@@ -49,6 +49,12 @@ public BenchmarkDotNetInfo(Version assemblyVersion, string fullVersion)
BrandTitle = "BenchmarkDotNet v" + BrandVersion;
}
+ internal static string RemoveVersionMetadata(string version)
+ {
+ int index = version.IndexOf('+');
+ return index >= 0 ? version.Substring(0, index) : version;
+ }
+
internal const string PublicKey =
"00240000048000009400000006020000002400005253413100040000010001002970bbdfca4d12" +
"9fc74b4845b239973f1b183684f0d7db5e1de7e085917e3656cf94884803cb800d85d5aae5838f" +
diff --git a/src/BenchmarkDotNet/Reports/Summary.cs b/src/BenchmarkDotNet/Reports/Summary.cs
index 7a9382f62d..680ded2d5b 100644
--- a/src/BenchmarkDotNet/Reports/Summary.cs
+++ b/src/BenchmarkDotNet/Reports/Summary.cs
@@ -48,7 +48,7 @@ public Summary(
CultureInfo cultureInfo,
ImmutableArray validationErrors,
ImmutableArray columnHidingRules,
- SummaryStyle summaryStyle = null)
+ SummaryStyle? summaryStyle = null)
{
Title = title;
ResultsDirectoryPath = resultsDirectoryPath;
diff --git a/src/BenchmarkDotNet/Reports/SummaryTable.cs b/src/BenchmarkDotNet/Reports/SummaryTable.cs
index 9ab5e983fd..64aae074ec 100644
--- a/src/BenchmarkDotNet/Reports/SummaryTable.cs
+++ b/src/BenchmarkDotNet/Reports/SummaryTable.cs
@@ -24,7 +24,7 @@ public class SummaryTable
public SummaryStyle EffectiveSummaryStyle { get; }
public bool SeparateLogicalGroups { get; }
- internal SummaryTable(Summary summary, SummaryStyle style = null)
+ internal SummaryTable(Summary summary, SummaryStyle? style = null)
{
Summary = summary;
diff --git a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
index 80a3ef0897..56849e7421 100644
--- a/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
+++ b/src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
@@ -11,6 +11,7 @@
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Environments;
+using BenchmarkDotNet.EventProcessors;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Helpers;
@@ -54,6 +55,9 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos)
using (var streamLogger = new StreamLogger(GetLogFileStreamWriter(benchmarkRunInfos, logFilePath)))
{
var compositeLogger = CreateCompositeLogger(benchmarkRunInfos, streamLogger);
+ var eventProcessor = new CompositeEventProcessor(benchmarkRunInfos);
+
+ eventProcessor.OnStartValidationStage();
compositeLogger.WriteLineInfo("// Validating benchmarks:");
@@ -61,6 +65,9 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos)
validationErrors.AddRange(Validate(supportedBenchmarks));
+ foreach (var validationError in validationErrors)
+ eventProcessor.OnValidationError(validationError);
+
PrintValidationErrors(compositeLogger, validationErrors);
if (validationErrors.Any(validationError => validationError.IsCritical))
@@ -69,16 +76,24 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos)
if (!supportedBenchmarks.Any(benchmarks => benchmarks.BenchmarksCases.Any()))
return new[] { Summary.ValidationFailed(title, resultsFolderPath, logFilePath) };
+ eventProcessor.OnEndValidationStage();
+
int totalBenchmarkCount = supportedBenchmarks.Sum(benchmarkInfo => benchmarkInfo.BenchmarksCases.Length);
int benchmarksToRunCount = totalBenchmarkCount - (idToResume + 1); // ids are indexed from 0
compositeLogger.WriteLineHeader("// ***** BenchmarkRunner: Start *****");
compositeLogger.WriteLineHeader($"// ***** Found {totalBenchmarkCount} benchmark(s) in total *****");
var globalChronometer = Chronometer.Start();
+
var buildPartitions = BenchmarkPartitioner.CreateForBuild(supportedBenchmarks, resolver);
- var buildResults = BuildInParallel(compositeLogger, rootArtifactsFolderPath, buildPartitions, in globalChronometer);
+ eventProcessor.OnStartBuildStage(buildPartitions);
+ var buildResults = BuildInParallel(compositeLogger, rootArtifactsFolderPath, buildPartitions, in globalChronometer, eventProcessor);
+
var allBuildsHaveFailed = buildResults.Values.All(buildResult => !buildResult.IsBuildSuccess);
+ eventProcessor.OnEndBuildStage();
+ eventProcessor.OnStartRunStage();
+
try
{
var results = new List();
@@ -102,9 +117,11 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos)
}
}
- var summary = Run(benchmarkRunInfo, benchmarkToBuildResult, resolver, compositeLogger, artifactsToCleanup,
+ eventProcessor.OnStartRunBenchmarksInType(benchmarkRunInfo.Type, benchmarkRunInfo.BenchmarksCases);
+ var summary = Run(benchmarkRunInfo, benchmarkToBuildResult, resolver, compositeLogger, eventProcessor, artifactsToCleanup,
resultsFolderPath, logFilePath, totalBenchmarkCount, in runsChronometer, ref benchmarksToRunCount,
taskbarProgress);
+ eventProcessor.OnEndRunBenchmarksInType(benchmarkRunInfo.Type, summary);
if (!benchmarkRunInfo.Config.Options.IsSet(ConfigOptions.JoinSummary))
PrintSummary(compositeLogger, benchmarkRunInfo.Config, summary);
@@ -148,6 +165,8 @@ internal static Summary[] Run(BenchmarkRunInfo[] benchmarkRunInfos)
Cleanup(compositeLogger, new HashSet(artifactsToCleanup.Distinct()));
compositeLogger.WriteLineInfo("Artifacts cleanup is finished");
compositeLogger.Flush();
+
+ eventProcessor.OnEndRunStage();
}
}
}
@@ -156,6 +175,7 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo,
Dictionary buildResults,
IResolver resolver,
ILogger logger,
+ EventProcessor eventProcessor,
List artifactsToCleanup,
string resultsFolderPath,
string logFilePath,
@@ -198,7 +218,10 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo,
if (!config.Options.IsSet(ConfigOptions.KeepBenchmarkFiles))
artifactsToCleanup.AddRange(buildResult.ArtifactsToCleanup);
+ eventProcessor.OnStartRunBenchmark(benchmark);
var report = RunCore(benchmark, info.benchmarkId, logger, resolver, buildResult);
+ eventProcessor.OnEndRunBenchmark(benchmark, report);
+
if (report.AllMeasurements.Any(m => m.Operations == 0))
throw new InvalidOperationException("An iteration with 'Operations == 0' detected");
reports.Add(report);
@@ -219,7 +242,7 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo,
reports.Add(new BenchmarkReport(false, benchmark, buildResult, buildResult, default, default));
if (buildResult.GenerateException != null)
- logger.WriteLineError($"// Generate Exception: {buildResult.GenerateException.Message}");
+ logger.WriteLineError($"// Generate Exception: {buildResult.GenerateException}");
else if (!buildResult.IsBuildSuccess && buildResult.TryToExplainFailureReason(out string reason))
logger.WriteLineError($"// Build Error: {reason}");
else if (buildResult.ErrorMessage != null)
@@ -347,7 +370,7 @@ private static ImmutableArray Validate(params BenchmarkRunInfo[
return validationErrors.ToImmutableArray();
}
- private static Dictionary BuildInParallel(ILogger logger, string rootArtifactsFolderPath, BuildPartition[] buildPartitions, in StartedClock globalChronometer)
+ private static Dictionary BuildInParallel(ILogger logger, string rootArtifactsFolderPath, BuildPartition[] buildPartitions, in StartedClock globalChronometer, EventProcessor eventProcessor)
{
logger.WriteLineHeader($"// ***** Building {buildPartitions.Length} exe(s) in Parallel: Start *****");
@@ -357,8 +380,18 @@ private static Dictionary BuildInParallel(ILogger l
var buildResults = buildPartitions
.AsParallel()
- .Select(buildPartition => (buildPartition, buildResult: Build(buildPartition, rootArtifactsFolderPath, buildLogger)))
- .ToDictionary(result => result.buildPartition, result => result.buildResult);
+ .Select(buildPartition => (Partition: buildPartition, Result: Build(buildPartition, rootArtifactsFolderPath, buildLogger)))
+ .AsSequential() // Ensure that build completion events are processed sequentially
+ .Select(build =>
+ {
+ // If the generation was successful, but the build was not, we will try building sequentially
+ // so don't send the OnBuildComplete event yet.
+ if (buildPartitions.Length <= 1 || !build.Result.IsGenerateSuccess || build.Result.IsBuildSuccess)
+ eventProcessor.OnBuildComplete(build.Partition, build.Result);
+
+ return build;
+ })
+ .ToDictionary(build => build.Partition, build => build.Result);
var afterParallelBuild = globalChronometer.GetElapsed();
@@ -370,8 +403,15 @@ private static Dictionary BuildInParallel(ILogger l
logger.WriteLineHeader("// ***** Failed to build in Parallel, switching to sequential build *****");
foreach (var buildPartition in buildPartitions)
- if (buildResults[buildPartition].IsGenerateSuccess && !buildResults[buildPartition].IsBuildSuccess && !buildResults[buildPartition].TryToExplainFailureReason(out string _))
- buildResults[buildPartition] = Build(buildPartition, rootArtifactsFolderPath, buildLogger);
+ {
+ if (buildResults[buildPartition].IsGenerateSuccess && !buildResults[buildPartition].IsBuildSuccess)
+ {
+ if (!buildResults[buildPartition].TryToExplainFailureReason(out string _))
+ buildResults[buildPartition] = Build(buildPartition, rootArtifactsFolderPath, buildLogger);
+
+ eventProcessor.OnBuildComplete(buildPartition, buildResults[buildPartition]);
+ }
+ }
var afterSequentialBuild = globalChronometer.GetElapsed();
@@ -672,9 +712,9 @@ private static void LogProgress(ILogger logger, in StartedClock runsChronometer,
$" Estimated finish {estimatedEnd:yyyy-MM-dd H:mm} ({(int)fromNow.TotalHours}h {fromNow.Minutes}m from now) **";
logger.WriteLineHeader(message);
- consoleTitler.UpdateTitle ($"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining - {(int)fromNow.TotalHours}h {fromNow.Minutes}m to finish");
+ consoleTitler.UpdateTitle($"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining - {(int)fromNow.TotalHours}h {fromNow.Minutes}m to finish");
- taskbarProgress.SetProgress((float) executedBenchmarkCount / totalBenchmarkCount);
+ taskbarProgress.SetProgress((float)executedBenchmarkCount / totalBenchmarkCount);
}
private static TimeSpan GetEstimatedFinishTime(in StartedClock runsChronometer, int benchmarksToRunCount, int executedBenchmarkCount)
@@ -729,4 +769,4 @@ private static int GetIdToResume(string rootArtifactsFolderPath, string currentL
return -1;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Running/Descriptor.cs b/src/BenchmarkDotNet/Running/Descriptor.cs
index 2e747bb1cd..a38b1a13d9 100644
--- a/src/BenchmarkDotNet/Running/Descriptor.cs
+++ b/src/BenchmarkDotNet/Running/Descriptor.cs
@@ -31,14 +31,14 @@ public class Descriptor : IEquatable
public Descriptor(
Type type,
MethodInfo workloadMethod,
- MethodInfo globalSetupMethod = null,
- MethodInfo globalCleanupMethod = null,
- MethodInfo iterationSetupMethod = null,
- MethodInfo iterationCleanupMethod = null,
- string description = null,
- string additionalLogic = null,
+ MethodInfo? globalSetupMethod = null,
+ MethodInfo? globalCleanupMethod = null,
+ MethodInfo? iterationSetupMethod = null,
+ MethodInfo? iterationCleanupMethod = null,
+ string? description = null,
+ string? additionalLogic = null,
bool baseline = false,
- string[] categories = null,
+ string[]? categories = null,
int operationsPerInvoke = 1,
int methodIndex = 0)
{
diff --git a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunPublisher.cs b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunPublisher.cs
index b9605867fe..c77b56b039 100644
--- a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunPublisher.cs
+++ b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunPublisher.cs
@@ -11,7 +11,7 @@ namespace BenchmarkDotNet.Toolchains.CoreRun
{
public class CoreRunPublisher : IBuilder
{
- public CoreRunPublisher(FileInfo coreRun, FileInfo customDotNetCliPath = null)
+ public CoreRunPublisher(FileInfo coreRun, FileInfo? customDotNetCliPath = null)
{
CoreRun = coreRun;
DotNetCliPublisher = new DotNetCliPublisher(customDotNetCliPath?.FullName);
diff --git a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs
index 03d677a5aa..7eca3f97f0 100644
--- a/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs
+++ b/src/BenchmarkDotNet/Toolchains/CoreRun/CoreRunToolchain.cs
@@ -21,7 +21,7 @@ public class CoreRunToolchain : IToolchain
/// the directory to restore packages to
public CoreRunToolchain(FileInfo coreRun, bool createCopy = true,
string targetFrameworkMoniker = "netcoreapp2.1",
- FileInfo customDotNetCliPath = null, DirectoryInfo restorePath = null,
+ FileInfo? customDotNetCliPath = null, DirectoryInfo? restorePath = null,
string displayName = "CoreRun")
{
if (coreRun == null) throw new ArgumentNullException(nameof(coreRun));
diff --git a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjClassicNetToolchain.cs b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjClassicNetToolchain.cs
index 4e1fe4a641..e3c3f85125 100644
--- a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjClassicNetToolchain.cs
+++ b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjClassicNetToolchain.cs
@@ -25,7 +25,7 @@ public class CsProjClassicNetToolchain : Toolchain
internal string CustomDotNetCliPath { get; }
- private CsProjClassicNetToolchain(string targetFrameworkMoniker, string name, string packagesPath = null, string customDotNetCliPath = null)
+ private CsProjClassicNetToolchain(string targetFrameworkMoniker, string name, string? packagesPath = null, string? customDotNetCliPath = null)
: base(name,
new CsProjGenerator(targetFrameworkMoniker, customDotNetCliPath, packagesPath, runtimeFrameworkVersion: null, isNetCore: false),
new DotNetCliBuilder(targetFrameworkMoniker, customDotNetCliPath),
@@ -34,7 +34,7 @@ private CsProjClassicNetToolchain(string targetFrameworkMoniker, string name, st
CustomDotNetCliPath = customDotNetCliPath;
}
- public static IToolchain From(string targetFrameworkMoniker, string packagesPath = null, string customDotNetCliPath = null)
+ public static IToolchain From(string targetFrameworkMoniker, string? packagesPath = null, string? customDotNetCliPath = null)
=> new CsProjClassicNetToolchain(targetFrameworkMoniker, targetFrameworkMoniker, packagesPath, customDotNetCliPath);
public override IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver)
diff --git a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs
index 63ec4573b0..98bcd2ae8b 100644
--- a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs
+++ b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjCoreToolchain.cs
@@ -24,6 +24,7 @@ public class CsProjCoreToolchain : Toolchain, IEquatable
[PublicAPI] public static readonly IToolchain NetCoreApp60 = From(NetCoreAppSettings.NetCoreApp60);
[PublicAPI] public static readonly IToolchain NetCoreApp70 = From(NetCoreAppSettings.NetCoreApp70);
[PublicAPI] public static readonly IToolchain NetCoreApp80 = From(NetCoreAppSettings.NetCoreApp80);
+ [PublicAPI] public static readonly IToolchain NetCoreApp90 = From(NetCoreAppSettings.NetCoreApp90);
internal CsProjCoreToolchain(string name, IGenerator generator, IBuilder builder, IExecutor executor, string customDotNetCliPath)
: base(name, generator, builder, executor)
diff --git a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs
index d732275d95..eaf1da4683 100644
--- a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs
+++ b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs
@@ -34,7 +34,8 @@ public class CsProjGenerator : DotNetCliGenerator, IEquatable
"CopyLocalLockFileAssemblies",
"PreserveCompilationContext",
"UserSecretsId",
- "EnablePreviewFeatures"
+ "EnablePreviewFeatures",
+ "RuntimeHostConfigurationOption",
}.ToImmutableArray();
public string RuntimeFrameworkVersion { get; }
@@ -121,7 +122,7 @@ protected virtual string GetRuntimeSettings(GcMode gcMode, IResolver resolver)
// custom SDKs are not added for non-netcoreapp apps (like net471), so when the TFM != netcoreapp we dont parse "
- string sdkName = null;
+ string? sdkName = null;
if (TargetFrameworkMoniker.StartsWith("netcoreapp", StringComparison.InvariantCultureIgnoreCase))
{
foreach (XmlElement importElement in projectElement.GetElementsByTagName("Import"))
@@ -161,8 +162,8 @@ protected virtual string GetRuntimeSettings(GcMode gcMode, IResolver resolver)
sdkName = DefaultSdkName;
}
- XmlDocument itemGroupsettings = null;
- XmlDocument propertyGroupSettings = null;
+ XmlDocument? itemGroupsettings = null;
+ XmlDocument? propertyGroupSettings = null;
GetSettingsThatNeedToBeCopied(projectElement, ref itemGroupsettings, ref propertyGroupSettings, projectFile);
diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliBuilder.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliBuilder.cs
index bad11c91be..e034279f89 100644
--- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliBuilder.cs
+++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliBuilder.cs
@@ -17,7 +17,7 @@ public class DotNetCliBuilder : IBuilder
private bool RetryFailedBuildWithNoDeps { get; }
[PublicAPI]
- public DotNetCliBuilder(string targetFrameworkMoniker, string customDotNetCliPath = null, bool logOutput = false, bool retryFailedBuildWithNoDeps = true)
+ public DotNetCliBuilder(string targetFrameworkMoniker, string? customDotNetCliPath = null, bool logOutput = false, bool retryFailedBuildWithNoDeps = true)
{
TargetFrameworkMoniker = targetFrameworkMoniker;
CustomDotNetCliPath = customDotNetCliPath;
diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs
index 31546d3112..9967b4ef92 100644
--- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs
+++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs
@@ -166,7 +166,7 @@ public DotNetCliCommandResult PublishNoBuildAndNoRestore()
internal static IEnumerable GetAddPackagesCommands(BuildPartition buildPartition)
=> GetNuGetAddPackageCommands(buildPartition.RepresentativeBenchmarkCase, buildPartition.Resolver);
- internal static string GetRestoreCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string extraArguments = null, string binLogSuffix = null)
+ internal static string GetRestoreCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string? extraArguments = null, string? binLogSuffix = null)
=> new StringBuilder()
.AppendArgument("restore")
.AppendArgument(string.IsNullOrEmpty(artifactsPaths.PackagesDirectoryName) ? string.Empty : $"--packages \"{artifactsPaths.PackagesDirectoryName}\"")
@@ -176,7 +176,7 @@ internal static string GetRestoreCommand(ArtifactsPaths artifactsPaths, BuildPar
.AppendArgument(GetMsBuildBinLogArgument(buildPartition, binLogSuffix))
.ToString();
- internal static string GetBuildCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string extraArguments = null, string binLogSuffix = null)
+ internal static string GetBuildCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string? extraArguments = null, string? binLogSuffix = null)
=> new StringBuilder()
.AppendArgument($"build -c {buildPartition.BuildConfiguration}") // we don't need to specify TFM, our auto-generated project contains always single one
.AppendArgument(GetCustomMsBuildArguments(buildPartition.RepresentativeBenchmarkCase, buildPartition.Resolver))
@@ -186,7 +186,7 @@ internal static string GetBuildCommand(ArtifactsPaths artifactsPaths, BuildParti
.AppendArgument(GetMsBuildBinLogArgument(buildPartition, binLogSuffix))
.ToString();
- internal static string GetPublishCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string extraArguments = null, string binLogSuffix = null)
+ internal static string GetPublishCommand(ArtifactsPaths artifactsPaths, BuildPartition buildPartition, string? extraArguments = null, string? binLogSuffix = null)
=> new StringBuilder()
.AppendArgument($"publish -c {buildPartition.BuildConfiguration}") // we don't need to specify TFM, our auto-generated project contains always single one
.AppendArgument(GetCustomMsBuildArguments(buildPartition.RepresentativeBenchmarkCase, buildPartition.Resolver))
diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs
index bfe9a97585..3532fa1e07 100644
--- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs
+++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs
@@ -99,7 +99,7 @@ internal static void LogEnvVars(DotNetCliCommand command)
}
internal static ProcessStartInfo BuildStartInfo(string customDotNetCliPath, string workingDirectory, string arguments,
- IReadOnlyList environmentVariables = null, bool redirectStandardInput = false, bool redirectStandardError = true, bool redirectStandardOutput = true)
+ IReadOnlyList? environmentVariables = null, bool redirectStandardInput = false, bool redirectStandardError = true, bool redirectStandardOutput = true)
{
const string dotnetMultiLevelLookupEnvVarName = "DOTNET_MULTILEVEL_LOOKUP";
diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliGenerator.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliGenerator.cs
index 47ff074fc3..a432fbf881 100644
--- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliGenerator.cs
+++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliGenerator.cs
@@ -61,17 +61,25 @@ internal static bool GetProjectRootDirectory(out DirectoryInfo directoryInfo)
return GetRootDirectory(IsRootProjectFolder, out directoryInfo);
}
- internal static bool GetRootDirectory(Func condition, out DirectoryInfo directoryInfo)
+ internal static bool GetRootDirectory(Func condition, out DirectoryInfo? directoryInfo)
{
- directoryInfo = new DirectoryInfo(Directory.GetCurrentDirectory());
- while (directoryInfo != null)
+ directoryInfo = null;
+ try
{
- if (condition(directoryInfo))
+ directoryInfo = new DirectoryInfo(Directory.GetCurrentDirectory());
+ while (directoryInfo != null)
{
- return true;
- }
+ if (condition(directoryInfo))
+ {
+ return true;
+ }
- directoryInfo = directoryInfo.Parent;
+ directoryInfo = directoryInfo.Parent;
+ }
+ }
+ catch
+ {
+ return false;
}
return false;
diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliPublisher.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliPublisher.cs
index 8292619e49..960558d5c0 100644
--- a/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliPublisher.cs
+++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliPublisher.cs
@@ -8,7 +8,7 @@ namespace BenchmarkDotNet.Toolchains.DotNetCli
{
public class DotNetCliPublisher : IBuilder
{
- public DotNetCliPublisher(string customDotNetCliPath = null, string extraArguments = null, IReadOnlyList environmentVariables = null)
+ public DotNetCliPublisher(string? customDotNetCliPath = null, string? extraArguments = null, IReadOnlyList environmentVariables = null)
{
CustomDotNetCliPath = customDotNetCliPath;
ExtraArguments = extraArguments;
diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/MsBuildErrorMapper.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/MsBuildErrorMapper.cs
index 6f0e28834b..bd5fba4123 100644
--- a/src/BenchmarkDotNet/Toolchains/DotNetCli/MsBuildErrorMapper.cs
+++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/MsBuildErrorMapper.cs
@@ -30,7 +30,7 @@ internal static class MsBuildErrorMapper
),
};
- internal static bool TryToExplainFailureReason(BuildResult buildResult, out string reason)
+ internal static bool TryToExplainFailureReason(BuildResult buildResult, out string? reason)
{
reason = null;
diff --git a/src/BenchmarkDotNet/Toolchains/DotNetCli/NetCoreAppSettings.cs b/src/BenchmarkDotNet/Toolchains/DotNetCli/NetCoreAppSettings.cs
index 50b1f7b614..664d1333f1 100644
--- a/src/BenchmarkDotNet/Toolchains/DotNetCli/NetCoreAppSettings.cs
+++ b/src/BenchmarkDotNet/Toolchains/DotNetCli/NetCoreAppSettings.cs
@@ -18,6 +18,7 @@ public class NetCoreAppSettings
[PublicAPI] public static readonly NetCoreAppSettings NetCoreApp60 = new ("net6.0", null, ".NET 6.0");
[PublicAPI] public static readonly NetCoreAppSettings NetCoreApp70 = new ("net7.0", null, ".NET 7.0");
[PublicAPI] public static readonly NetCoreAppSettings NetCoreApp80 = new ("net8.0", null, ".NET 8.0");
+ [PublicAPI] public static readonly NetCoreAppSettings NetCoreApp90 = new ("net9.0", null, ".NET 9.0");
///
///
@@ -44,10 +45,10 @@ public NetCoreAppSettings(
string targetFrameworkMoniker,
string runtimeFrameworkVersion,
string name,
- string customDotNetCliPath = null,
- string packagesPath = null,
- string customRuntimePack = null,
- string aotCompilerPath = null,
+ string? customDotNetCliPath = null,
+ string? packagesPath = null,
+ string? customRuntimePack = null,
+ string? aotCompilerPath = null,
MonoAotCompilerMode aotCompilerMode = MonoAotCompilerMode.mini
)
{
@@ -96,10 +97,10 @@ public NetCoreAppSettings(
///
public MonoAotCompilerMode AOTCompilerMode { get; }
- public NetCoreAppSettings WithCustomDotNetCliPath(string customDotNetCliPath, string displayName = null)
+ public NetCoreAppSettings WithCustomDotNetCliPath(string customDotNetCliPath, string? displayName = null)
=> new NetCoreAppSettings(TargetFrameworkMoniker, RuntimeFrameworkVersion, displayName ?? Name, customDotNetCliPath, PackagesPath);
- public NetCoreAppSettings WithCustomPackagesRestorePath(string packagesPath, string displayName = null)
+ public NetCoreAppSettings WithCustomPackagesRestorePath(string packagesPath, string? displayName = null)
=> new NetCoreAppSettings(TargetFrameworkMoniker, RuntimeFrameworkVersion, displayName ?? Name, CustomDotNetCliPath, packagesPath);
}
}
diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ConsumeEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ConsumeEmitter.cs
index 62fe06c649..9767cb8263 100644
--- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ConsumeEmitter.cs
+++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/ConsumeEmitter.cs
@@ -31,9 +31,9 @@ protected ConsumeEmitter(ConsumableTypeInfo consumableTypeInfo)
protected ConsumableTypeInfo ConsumableInfo { get; }
- protected ILGenerator IlBuilder { get; private set; }
- protected MethodBuilder ActionMethodBuilder { get; private set; }
- protected MethodInfo ActionInvokeMethod { get; private set; }
+ protected ILGenerator? IlBuilder { get; private set; }
+ protected MethodBuilder? ActionMethodBuilder { get; private set; }
+ protected MethodInfo? ActionInvokeMethod { get; private set; }
protected RunnableActionKind? ActionKind { get; private set; }
[AssertionMethod]
diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs
index c6c6d8e05c..9048474329 100644
--- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs
+++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/Implementation/Emitters/RunnableEmitter.cs
@@ -434,7 +434,7 @@ private void DefineFields()
Type argLocalsType;
Type argFieldType;
- MethodInfo opConversion = null;
+ MethodInfo? opConversion = null;
if (parameterType.IsByRef)
{
argLocalsType = parameterType;
@@ -833,8 +833,8 @@ .locals init (
var skipFirstArg = workloadMethod.IsStatic;
var argLocals = EmitDeclareArgLocals(ilBuilder, skipFirstArg);
- LocalBuilder callResultLocal = null;
- LocalBuilder awaiterLocal = null;
+ LocalBuilder? callResultLocal = null;
+ LocalBuilder? awaiterLocal = null;
if (consumableInfo.IsAwaitable)
{
var callResultType = consumableInfo.OriginMethodReturnType;
diff --git a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitBuilder.cs b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitBuilder.cs
index f46f5c99b6..7a75eb3263 100644
--- a/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitBuilder.cs
+++ b/src/BenchmarkDotNet/Toolchains/InProcess/Emit/InProcessEmitBuilder.cs
@@ -11,8 +11,8 @@ public class InProcessEmitBuilder : IBuilder
{
public BuildResult Build(GenerateResult generateResult, BuildPartition buildPartition, ILogger logger)
{
- Assembly assembly = null;
- Exception buildError = null;
+ Assembly? assembly = null;
+ Exception? buildError = null;
try
{
assembly = RunnableEmitter.EmitPartitionAssembly(generateResult, buildPartition, logger);
diff --git a/src/BenchmarkDotNet/Toolchains/Mono/MonoToolchain.cs b/src/BenchmarkDotNet/Toolchains/Mono/MonoToolchain.cs
index ba2ae2ac96..56f2ebaa56 100644
--- a/src/BenchmarkDotNet/Toolchains/Mono/MonoToolchain.cs
+++ b/src/BenchmarkDotNet/Toolchains/Mono/MonoToolchain.cs
@@ -11,6 +11,7 @@ public class MonoToolchain : CsProjCoreToolchain, IEquatable
[PublicAPI] public static readonly IToolchain Mono60 = From(new NetCoreAppSettings("net6.0", null, "mono60"));
[PublicAPI] public static readonly IToolchain Mono70 = From(new NetCoreAppSettings("net7.0", null, "mono70"));
[PublicAPI] public static readonly IToolchain Mono80 = From(new NetCoreAppSettings("net8.0", null, "mono80"));
+ [PublicAPI] public static readonly IToolchain Mono90 = From(new NetCoreAppSettings("net9.0", null, "mono90"));
private MonoToolchain(string name, IGenerator generator, IBuilder builder, IExecutor executor, string customDotNetCliPath)
: base(name, generator, builder, executor, customDotNetCliPath)
diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs
index 0beeff9627..4703c4b657 100644
--- a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs
+++ b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs
@@ -29,6 +29,14 @@ public class NativeAotToolchain : Toolchain
.TargetFrameworkMoniker("net8.0")
.ToToolchain();
+ ///
+ /// compiled as net9.0, targets latest NativeAOT build from the .NET 9 feed: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json
+ ///
+ public static readonly IToolchain Net90 = CreateBuilder()
+ .UseNuGet("", "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json")
+ .TargetFrameworkMoniker("net9.0")
+ .ToToolchain();
+
internal NativeAotToolchain(string displayName,
string ilCompilerVersion,
string runtimeFrameworkVersion, string targetFrameworkMoniker, string runtimeIdentifier,
diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchainBuilder.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchainBuilder.cs
index 4d37882007..9cdc679716 100644
--- a/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchainBuilder.cs
+++ b/src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchainBuilder.cs
@@ -17,7 +17,7 @@ public class NativeAotToolchainBuilder : CustomDotNetCliToolchainBuilder
private bool ilcGenerateCompleteTypeMetadata = true;
private bool ilcGenerateStackTraceData = true;
private string ilcOptimizationPreference = "Speed";
- private string ilcInstructionSet = null;
+ private string? ilcInstructionSet = null;
private bool isIlCompilerConfigured;
diff --git a/src/BenchmarkDotNet/Toolchains/Parameters/ExecuteParameters.cs b/src/BenchmarkDotNet/Toolchains/Parameters/ExecuteParameters.cs
index bdfbd78a37..e10c81b49b 100644
--- a/src/BenchmarkDotNet/Toolchains/Parameters/ExecuteParameters.cs
+++ b/src/BenchmarkDotNet/Toolchains/Parameters/ExecuteParameters.cs
@@ -11,7 +11,7 @@ public class ExecuteParameters
{
internal static readonly TimeSpan ProcessExitTimeout = TimeSpan.FromSeconds(2);
- public ExecuteParameters(BuildResult buildResult, BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, ILogger logger, IResolver resolver, int launchIndex, IDiagnoser diagnoser = null)
+ public ExecuteParameters(BuildResult buildResult, BenchmarkCase benchmarkCase, BenchmarkId benchmarkId, ILogger logger, IResolver resolver, int launchIndex, IDiagnoser? diagnoser = null)
{
BuildResult = buildResult;
BenchmarkCase = benchmarkCase;
diff --git a/src/BenchmarkDotNet/Toolchains/Results/GenerateResult.cs b/src/BenchmarkDotNet/Toolchains/Results/GenerateResult.cs
index b6de670b8d..8763fcbb7a 100644
--- a/src/BenchmarkDotNet/Toolchains/Results/GenerateResult.cs
+++ b/src/BenchmarkDotNet/Toolchains/Results/GenerateResult.cs
@@ -22,7 +22,7 @@ public GenerateResult(ArtifactsPaths artifactsPaths, bool isGenerateSuccess, Exc
public static GenerateResult Success(ArtifactsPaths artifactsPaths, IReadOnlyCollection artifactsToCleanup)
=> new GenerateResult(artifactsPaths, true, null, artifactsToCleanup);
- public static GenerateResult Failure(ArtifactsPaths artifactsPaths, IReadOnlyCollection artifactsToCleanup, Exception exception = null)
+ public static GenerateResult Failure(ArtifactsPaths artifactsPaths, IReadOnlyCollection artifactsToCleanup, Exception? exception = null)
=> new GenerateResult(artifactsPaths, false, exception, artifactsToCleanup);
public override string ToString() => "GenerateResult: " + (IsGenerateSuccess ? "Success" : "Fail");
diff --git a/src/BenchmarkDotNet/Toolchains/Toolchain.cs b/src/BenchmarkDotNet/Toolchains/Toolchain.cs
index bbe5f1d94f..e680424de7 100644
--- a/src/BenchmarkDotNet/Toolchains/Toolchain.cs
+++ b/src/BenchmarkDotNet/Toolchains/Toolchain.cs
@@ -57,7 +57,7 @@ public virtual IEnumerable Validate(BenchmarkCase benchmarkCase
}
}
- internal static bool IsCliPathInvalid(string customDotNetCliPath, BenchmarkCase benchmarkCase, out ValidationError validationError)
+ internal static bool IsCliPathInvalid(string customDotNetCliPath, BenchmarkCase benchmarkCase, out ValidationError? validationError)
{
validationError = null;
diff --git a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs
index ad3b4a5a6b..837c757cd7 100644
--- a/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs
+++ b/src/BenchmarkDotNet/Toolchains/ToolchainExtensions.cs
@@ -32,7 +32,7 @@ private static IToolchain GetToolchain(Job job, Descriptor descriptor)
|| job.HasValue(InfrastructureMode.BuildConfigurationCharacteristic)
|| job.HasValue(InfrastructureMode.ArgumentsCharacteristic));
- internal static IToolchain GetToolchain(this Runtime runtime, Descriptor descriptor = null, bool preferMsBuildToolchains = false)
+ internal static IToolchain GetToolchain(this Runtime runtime, Descriptor? descriptor = null, bool preferMsBuildToolchains = false)
{
switch (runtime)
{
@@ -66,6 +66,7 @@ internal static IToolchain GetToolchain(this Runtime runtime, Descriptor descrip
RuntimeMoniker.Mono60 => GetToolchain(RuntimeMoniker.Net60),
RuntimeMoniker.Mono70 => GetToolchain(RuntimeMoniker.Net70),
RuntimeMoniker.Mono80 => GetToolchain(RuntimeMoniker.Net80),
+ RuntimeMoniker.Mono90 => GetToolchain(RuntimeMoniker.Net90),
_ => CsProjCoreToolchain.From(new NetCoreAppSettings(mono.MsBuildMoniker, null, mono.Name))
};
}
@@ -152,6 +153,9 @@ private static IToolchain GetToolchain(RuntimeMoniker runtimeMoniker)
case RuntimeMoniker.Net80:
return CsProjCoreToolchain.NetCoreApp80;
+ case RuntimeMoniker.Net90:
+ return CsProjCoreToolchain.NetCoreApp90;
+
case RuntimeMoniker.NativeAot60:
return NativeAotToolchain.Net60;
@@ -161,6 +165,9 @@ private static IToolchain GetToolchain(RuntimeMoniker runtimeMoniker)
case RuntimeMoniker.NativeAot80:
return NativeAotToolchain.Net80;
+ case RuntimeMoniker.NativeAot90:
+ return NativeAotToolchain.Net90;
+
case RuntimeMoniker.Mono60:
return MonoToolchain.Mono60;
@@ -170,6 +177,9 @@ private static IToolchain GetToolchain(RuntimeMoniker runtimeMoniker)
case RuntimeMoniker.Mono80:
return MonoToolchain.Mono80;
+ case RuntimeMoniker.Mono90:
+ return MonoToolchain.Mono90;
+
default:
throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, "RuntimeMoniker not supported");
}
diff --git a/src/BenchmarkDotNet/Validators/ExecutionValidatorBase.cs b/src/BenchmarkDotNet/Validators/ExecutionValidatorBase.cs
index 92d7422ae2..11af691440 100644
--- a/src/BenchmarkDotNet/Validators/ExecutionValidatorBase.cs
+++ b/src/BenchmarkDotNet/Validators/ExecutionValidatorBase.cs
@@ -52,7 +52,7 @@ public IEnumerable Validate(ValidationParameters validationPara
return errors;
}
- private bool TryCreateBenchmarkTypeInstance(Type type, List errors, out object instance)
+ private bool TryCreateBenchmarkTypeInstance(Type type, List errors, out object? instance)
{
try
{
diff --git a/src/BenchmarkDotNet/Validators/ValidationError.cs b/src/BenchmarkDotNet/Validators/ValidationError.cs
index e8f5e40f6b..23faff5a54 100644
--- a/src/BenchmarkDotNet/Validators/ValidationError.cs
+++ b/src/BenchmarkDotNet/Validators/ValidationError.cs
@@ -6,7 +6,7 @@ namespace BenchmarkDotNet.Validators
{
public class ValidationError : IEquatable
{
- public ValidationError(bool isCritical, string message, BenchmarkCase benchmarkCase = null)
+ public ValidationError(bool isCritical, string message, BenchmarkCase? benchmarkCase = null)
{
IsCritical = isCritical;
Message = message;
diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json
index 567259b34d..d16cc38bf0 100644
--- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json
+++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.CSharp/.template.config/template.json
@@ -90,35 +90,22 @@
"datatype": "string",
"cases": [
{
- "condition": "(framework == '' && consoleApp == true)",
+ "condition": "(consoleApp == true)",
"value": "net6.0"
},
{
- "condition": "(framework == '' && consoleApp == false)",
+ "condition": "(consoleApp == false)",
"value": "netstandard2.0"
- },
- {
- "condition": "(framework != '')",
- "value": ""
}
]
}
},
"frameworkValue": {
"type": "generated",
- "generator": "join",
- "description": "join framework and frameworkDefault",
+ "generator": "coalesce",
"parameters": {
- "symbols": [
- {
- "type": "ref",
- "value": "framework"
- },
- {
- "type": "ref",
- "value": "frameworkDefault"
- }
- ]
+ "sourceVariableName": "framework",
+ "fallbackVariableName": "frameworkDefault"
},
"replaces": "$(Framework)"
},
@@ -144,7 +131,7 @@
"type": "parameter",
"datatype": "string",
"description": "Version of BenchmarkDotNet that will be referenced.",
- "defaultValue": "0.13.8",
+ "defaultValue": "0.13.10",
"replaces": "$(BenchmarkDotNetVersion)"
}
},
diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json
index 12f8c5b9bd..2a1e275699 100644
--- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json
+++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.FSharp/.template.config/template.json
@@ -90,35 +90,22 @@
"datatype": "string",
"cases": [
{
- "condition": "(framework == '' && consoleApp == true)",
+ "condition": "(consoleApp == true)",
"value": "net6.0"
},
{
- "condition": "(framework == '' && consoleApp == false)",
+ "condition": "(consoleApp == false)",
"value": "netstandard2.0"
- },
- {
- "condition": "(framework != '')",
- "value": ""
}
]
}
},
"frameworkValue": {
"type": "generated",
- "generator": "join",
- "description": "join framework and frameworkDefault",
+ "generator": "coalesce",
"parameters": {
- "symbols": [
- {
- "type": "ref",
- "value": "framework"
- },
- {
- "type": "ref",
- "value": "frameworkDefault"
- }
- ]
+ "sourceVariableName": "framework",
+ "fallbackVariableName": "frameworkDefault"
},
"replaces": "$(Framework)"
},
@@ -144,7 +131,7 @@
"type": "parameter",
"datatype": "string",
"description": "Version of BenchmarkDotNet that will be referenced.",
- "defaultValue": "0.13.8",
+ "defaultValue": "0.13.10",
"replaces": "$(BenchmarkDotNetVersion)"
}
},
diff --git a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json
index 0e8597a653..feb22ba4dd 100644
--- a/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json
+++ b/templates/templates/BenchmarkDotNet.BenchmarkProjectTemplate.VB/.template.config/template.json
@@ -90,35 +90,22 @@
"datatype": "string",
"cases": [
{
- "condition": "(framework == '' && consoleApp == true)",
+ "condition": "(consoleApp == true)",
"value": "net6.0"
},
{
- "condition": "(framework == '' && consoleApp == false)",
+ "condition": "(consoleApp == false)",
"value": "netstandard2.0"
- },
- {
- "condition": "(framework != '')",
- "value": ""
}
]
}
},
"frameworkValue": {
"type": "generated",
- "generator": "join",
- "description": "join framework and frameworkDefault",
+ "generator": "coalesce",
"parameters": {
- "symbols": [
- {
- "type": "ref",
- "value": "framework"
- },
- {
- "type": "ref",
- "value": "frameworkDefault"
- }
- ]
+ "sourceVariableName": "framework",
+ "fallbackVariableName": "frameworkDefault"
},
"replaces": "$(Framework)"
},
@@ -144,7 +131,7 @@
"type": "parameter",
"datatype": "string",
"description": "Version of BenchmarkDotNet that will be referenced.",
- "defaultValue": "0.13.8",
+ "defaultValue": "0.13.10",
"replaces": "$(BenchmarkDotNetVersion)"
}
},
diff --git a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks.csproj b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks.csproj
index f5f83b5cda..e7fadcfc16 100644
--- a/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks.csproj
+++ b/tests/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks/BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks.csproj
@@ -3,6 +3,7 @@
BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks
+ false
net461;net48;netcoreapp2.0;net7.0
true
BenchmarkDotNet.IntegrationTests.ManualRunning.MultipleFrameworks
diff --git a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkSwitcherTest.cs b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkSwitcherTest.cs
index 695a3baeb6..e9a95fce81 100644
--- a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkSwitcherTest.cs
+++ b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkSwitcherTest.cs
@@ -135,7 +135,7 @@ public void WhenDisableLogFileWeDontWriteToFile()
var logger = new OutputLogger(Output);
var config = ManualConfig.CreateEmpty().AddLogger(logger).WithOptions(ConfigOptions.DisableLogFile).AddJob(Job.Dry);
- string logFilePath = null;
+ string? logFilePath = null;
try
{
var summaries = BenchmarkSwitcher
@@ -161,7 +161,7 @@ public void EnsureLogFileIsWritten()
var logger = new OutputLogger(Output);
var config = ManualConfig.CreateEmpty().AddLogger(logger).AddJob(Job.Dry);
- string logFilePath = null;
+ string? logFilePath = null;
try
{
var summaries = BenchmarkSwitcher
diff --git a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkTestExecutor.cs b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkTestExecutor.cs
index 1171b0f014..e835065b27 100644
--- a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkTestExecutor.cs
+++ b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkTestExecutor.cs
@@ -36,7 +36,7 @@ protected BenchmarkTestExecutor(ITestOutputHelper output)
/// Optional custom config to be used instead of the default
/// Optional: disable validation (default = true/enabled)
/// The summary from the benchmark run
- public Reports.Summary CanExecute(IConfig config = null, bool fullValidation = true)
+ public Reports.Summary CanExecute(IConfig? config = null, bool fullValidation = true)
{
return CanExecute(typeof(TBenchmark), config, fullValidation);
}
@@ -50,7 +50,7 @@ public Reports.Summary CanExecute(IConfig config = null, bool fullVa
/// Optional custom config to be used instead of the default
/// Optional: disable validation (default = true/enabled)
/// The summary from the benchmark run
- public Reports.Summary CanExecute(Type type, IConfig config = null, bool fullValidation = true)
+ public Reports.Summary CanExecute(Type type, IConfig? config = null, bool fullValidation = true)
{
// Add logging, so the Benchmark execution is in the TestRunner output (makes Debugging easier)
if (config == null)
@@ -90,7 +90,7 @@ public Reports.Summary CanExecute(Type type, IConfig config = null, bool fullVal
return summary;
}
- protected IConfig CreateSimpleConfig(OutputLogger logger = null, Job job = null)
+ protected IConfig CreateSimpleConfig(OutputLogger? logger = null, Job? job = null)
{
var baseConfig = job == null ? (IConfig)new SingleRunFastConfig() : new SingleJobConfig(job);
return baseConfig
diff --git a/tests/BenchmarkDotNet.IntegrationTests/EventProcessorTests.cs b/tests/BenchmarkDotNet.IntegrationTests/EventProcessorTests.cs
new file mode 100644
index 0000000000..4cede6835b
--- /dev/null
+++ b/tests/BenchmarkDotNet.IntegrationTests/EventProcessorTests.cs
@@ -0,0 +1,312 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Characteristics;
+using BenchmarkDotNet.Columns;
+using BenchmarkDotNet.Configs;
+using BenchmarkDotNet.EventProcessors;
+using BenchmarkDotNet.Jobs;
+using BenchmarkDotNet.Loggers;
+using BenchmarkDotNet.Reports;
+using BenchmarkDotNet.Running;
+using BenchmarkDotNet.Toolchains;
+using BenchmarkDotNet.Toolchains.Results;
+using BenchmarkDotNet.Validators;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+
+namespace BenchmarkDotNet.IntegrationTests
+{
+ public class EventProcessorTests
+ {
+ [Fact]
+ public void WhenUsingEventProcessorAndNoBenchmarks()
+ {
+ var events = RunBenchmarksAndRecordEvents(new[] { typeof(ClassEmpty) });
+ Assert.Single(events);
+ Assert.Equal(nameof(EventProcessor.OnStartValidationStage), events[0].EventType);
+ }
+
+ [Fact]
+ public void WhenUsingEventProcessorOnSingleClass()
+ {
+ var events = RunBenchmarksAndRecordEvents(new[] { typeof(ClassA) });
+
+ Assert.Equal(13, events.Count);
+
+ Assert.Equal(nameof(EventProcessor.OnStartValidationStage), events[0].EventType);
+ Assert.Equal(nameof(EventProcessor.OnEndValidationStage), events[1].EventType);
+ Assert.Equal(nameof(EventProcessor.OnStartBuildStage), events[2].EventType);
+ Assert.Equal(nameof(EventProcessor.OnBuildComplete), events[3].EventType);
+ Assert.Equal(nameof(EventProcessor.OnEndBuildStage), events[4].EventType);
+ Assert.Equal(nameof(EventProcessor.OnStartRunStage), events[5].EventType);
+
+ var benchmarkTypeAndMethods = new List<(Type Type, string[] MethodNames)>
+ {
+ (typeof(ClassA), new[]{ nameof(ClassA.Method1), nameof(ClassA.Method2) })
+ };
+
+ int eventIndex = 6;
+ foreach ((var type, var methodNames) in benchmarkTypeAndMethods)
+ {
+ Assert.Equal(nameof(EventProcessor.OnStartRunBenchmarksInType), events[eventIndex].EventType);
+ Assert.Equal(type, events[eventIndex++].Args[0] as Type);
+
+ foreach (var method in methodNames)
+ {
+ var methodDescriptor = type.GetMethod(method);
+ Assert.Equal(nameof(EventProcessor.OnStartRunBenchmark), events[eventIndex].EventType);
+ Assert.Equal(methodDescriptor, (events[eventIndex++].Args[0] as BenchmarkCase).Descriptor.WorkloadMethod);
+
+ Assert.Equal(nameof(EventProcessor.OnEndRunBenchmark), events[eventIndex].EventType);
+ Assert.Equal(methodDescriptor, (events[eventIndex++].Args[0] as BenchmarkCase).Descriptor.WorkloadMethod);
+ }
+
+ Assert.Equal(nameof(EventProcessor.OnEndRunBenchmarksInType), events[eventIndex].EventType);
+ Assert.Equal(type, events[eventIndex++].Args[0] as Type);
+ }
+
+ Assert.Equal(nameof(EventProcessor.OnEndRunStage), events[eventIndex].EventType);
+ }
+
+ [Fact]
+ public void WhenUsingEventProcessorOnMultipleClasses()
+ {
+ var events = RunBenchmarksAndRecordEvents(new[] { typeof(ClassA), typeof(ClassB) });
+
+ Assert.Equal(23, events.Count);
+
+ Assert.Equal(nameof(EventProcessor.OnStartValidationStage), events[0].EventType);
+ Assert.Equal(nameof(EventProcessor.OnEndValidationStage), events[1].EventType);
+ Assert.Equal(nameof(EventProcessor.OnStartBuildStage), events[2].EventType);
+ Assert.Equal(nameof(EventProcessor.OnBuildComplete), events[3].EventType);
+ Assert.Equal(nameof(EventProcessor.OnEndBuildStage), events[4].EventType);
+ Assert.Equal(nameof(EventProcessor.OnStartRunStage), events[5].EventType);
+
+ var benchmarkTypeAndMethods = new List<(Type Type, string[] MethodNames)>
+ {
+ (typeof(ClassA), new[]{ nameof(ClassA.Method1), nameof(ClassA.Method2) }),
+ (typeof(ClassB), new[]{ nameof(ClassB.Method1), nameof(ClassB.Method2), nameof(ClassB.Method3), nameof(ClassB.Method4) })
+ };
+
+ int eventIndex = 6;
+ foreach ((var type, var methodNames) in benchmarkTypeAndMethods)
+ {
+ Assert.Equal(nameof(EventProcessor.OnStartRunBenchmarksInType), events[eventIndex].EventType);
+ Assert.Equal(type, events[eventIndex++].Args[0] as Type);
+
+ foreach (var method in methodNames)
+ {
+ var methodDescriptor = type.GetMethod(method);
+ Assert.Equal(nameof(EventProcessor.OnStartRunBenchmark), events[eventIndex].EventType);
+ Assert.Equal(methodDescriptor, (events[eventIndex++].Args[0] as BenchmarkCase).Descriptor.WorkloadMethod);
+
+ Assert.Equal(nameof(EventProcessor.OnEndRunBenchmark), events[eventIndex].EventType);
+ Assert.Equal(methodDescriptor, (events[eventIndex++].Args[0] as BenchmarkCase).Descriptor.WorkloadMethod);
+ }
+
+ Assert.Equal(nameof(EventProcessor.OnEndRunBenchmarksInType), events[eventIndex].EventType);
+ Assert.Equal(type, events[eventIndex++].Args[0] as Type);
+ }
+
+ Assert.Equal(nameof(EventProcessor.OnEndRunStage), events[eventIndex].EventType);
+ }
+
+ [Fact]
+ public void WhenUsingEventProcessorWithValidationErrors()
+ {
+ var validator = new ErrorAllCasesValidator();
+ var events = RunBenchmarksAndRecordEvents(new[] { typeof(ClassA) }, validator);
+
+ Assert.Equal(15, events.Count);
+ Assert.Equal(nameof(EventProcessor.OnStartValidationStage), events[0].EventType);
+ Assert.Equal(nameof(EventProcessor.OnValidationError), events[1].EventType);
+ Assert.Equal(typeof(ClassA).GetMethod(nameof(ClassA.Method1)), (events[1].Args[0] as ValidationError).BenchmarkCase.Descriptor.WorkloadMethod);
+ Assert.Equal(nameof(EventProcessor.OnValidationError), events[2].EventType);
+ Assert.Equal(typeof(ClassA).GetMethod(nameof(ClassA.Method2)), (events[2].Args[0] as ValidationError).BenchmarkCase.Descriptor.WorkloadMethod);
+ Assert.Equal(nameof(EventProcessor.OnEndValidationStage), events[3].EventType);
+ Assert.Equal(nameof(EventProcessor.OnStartBuildStage), events[4].EventType);
+ }
+
+ [Fact]
+ public void WhenUsingEventProcessorWithUnsupportedBenchmark()
+ {
+ var toolchain = new AllUnsupportedToolchain();
+ var events = RunBenchmarksAndRecordEvents(new[] { typeof(ClassA) }, toolchain: toolchain);
+
+ Assert.Equal(3, events.Count);
+ Assert.Equal(nameof(EventProcessor.OnStartValidationStage), events[0].EventType);
+ Assert.Equal(nameof(EventProcessor.OnValidationError), events[1].EventType);
+ Assert.Equal(typeof(ClassA).GetMethod(nameof(ClassA.Method1)), (events[1].Args[0] as ValidationError).BenchmarkCase.Descriptor.WorkloadMethod);
+ Assert.Equal(nameof(EventProcessor.OnValidationError), events[2].EventType);
+ Assert.Equal(typeof(ClassA).GetMethod(nameof(ClassA.Method2)), (events[2].Args[0] as ValidationError).BenchmarkCase.Descriptor.WorkloadMethod);
+ }
+
+ [Fact]
+ public void WhenUsingEventProcessorWithBuildFailures()
+ {
+ var toolchain = new Toolchain("Build Failure", new AllFailsGenerator(), null, null);
+ var events = RunBenchmarksAndRecordEvents(new[] { typeof(ClassA) }, toolchain: toolchain);
+
+ Assert.Equal(9, events.Count);
+ Assert.Equal(nameof(EventProcessor.OnStartValidationStage), events[0].EventType);
+ Assert.Equal(nameof(EventProcessor.OnEndValidationStage), events[1].EventType);
+ Assert.Equal(nameof(EventProcessor.OnStartBuildStage), events[2].EventType);
+ Assert.Equal(nameof(EventProcessor.OnBuildComplete), events[3].EventType);
+ Assert.False((events[3].Args[1] as BuildResult).IsGenerateSuccess);
+ Assert.Equal(nameof(EventProcessor.OnEndBuildStage), events[4].EventType);
+ Assert.Equal(nameof(EventProcessor.OnStartRunStage), events[5].EventType);
+ }
+
+ private List RunBenchmarksAndRecordEvents(Type[] types, IValidator? validator = null, IToolchain? toolchain = null)
+ {
+ var eventProcessor = new LoggingEventProcessor();
+ var job = new Job(Job.Dry);
+ if (toolchain != null)
+ job.Infrastructure.Toolchain = toolchain;
+
+ var config = new ManualConfig()
+ .AddJob(job)
+ .AddEventProcessor(eventProcessor)
+ .WithOptions(ConfigOptions.DisableOptimizationsValidator)
+ .AddExporter(new MockExporter()) // only added to prevent validation warnings about a lack of exporters
+ .AddLogger(ConsoleLogger.Default)
+ .AddColumnProvider(DefaultColumnProviders.Instance)
+ .AddAnalyser(DefaultConfig.Instance.GetAnalysers().ToArray());
+ if (validator != null)
+ config = config.AddValidator(validator);
+ _ = BenchmarkRunner.Run(types, config);
+ return eventProcessor.Events;
+ }
+
+ public class ClassA
+ {
+ [Benchmark]
+ public void Method1() { }
+ [Benchmark]
+ public void Method2() { }
+ }
+
+ public class ClassB
+ {
+ [Benchmark]
+ public void Method1() { }
+ [Benchmark]
+ public void Method2() { }
+ [Benchmark]
+ public void Method3() { }
+ [Benchmark]
+ public void Method4() { }
+ }
+
+ public class ClassEmpty { }
+
+ public class ErrorAllCasesValidator : IValidator
+ {
+ public bool TreatsWarningsAsErrors => true;
+
+ public IEnumerable Validate(ValidationParameters validationParameters)
+ {
+ foreach (var benchmark in validationParameters.Benchmarks)
+ yield return new ValidationError(false, "Mock Validation", benchmark);
+ }
+ }
+
+ public class AllUnsupportedToolchain : Toolchain
+ {
+ public AllUnsupportedToolchain() : base("AllUnsupported", null, null, null)
+ {
+ }
+
+ public override IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver)
+ {
+ yield return new ValidationError(true, "Unsupported Benchmark", benchmarkCase);
+ }
+ }
+
+ public class AllFailsGenerator : IGenerator
+ {
+ public GenerateResult GenerateProject(BuildPartition buildPartition, ILogger logger, string rootArtifactsFolderPath)
+ {
+ return GenerateResult.Failure(ArtifactsPaths.Empty, new List(), new Exception("Generation Failed"));
+ }
+ }
+
+ public class LoggingEventProcessor : EventProcessor
+ {
+ public class EventData
+ {
+ public EventData(string eventType, IReadOnlyList