Skip to content

Commit

Permalink
Add BenchmarkDotNet.Diagnostics.dotTrace
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyAkinshin committed Jun 12, 2023
1 parent a260bd3 commit ff6e8d9
Show file tree
Hide file tree
Showing 20 changed files with 661 additions and 11 deletions.
7 changes: 7 additions & 0 deletions BenchmarkDotNet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BenchmarkDotNet.Templates", "templates\BenchmarkDotNet.Templates.csproj", "{B620D10A-CD8E-4A34-8B27-FD6257E63AD0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Diagnostics.dotTrace", "src\BenchmarkDotNet.Diagnostics.dotTrace\BenchmarkDotNet.Diagnostics.dotTrace.csproj", "{C5BDA61F-3A56-4B59-901D-0A17E78F4076}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -125,6 +127,10 @@ Global
{B620D10A-CD8E-4A34-8B27-FD6257E63AD0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B620D10A-CD8E-4A34-8B27-FD6257E63AD0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B620D10A-CD8E-4A34-8B27-FD6257E63AD0}.Release|Any CPU.Build.0 = Release|Any CPU
{C5BDA61F-3A56-4B59-901D-0A17E78F4076}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C5BDA61F-3A56-4B59-901D-0A17E78F4076}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5BDA61F-3A56-4B59-901D-0A17E78F4076}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5BDA61F-3A56-4B59-901D-0A17E78F4076}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -148,6 +154,7 @@ Global
{B4405781-40D3-42B8-B168-00E711FABA15} = {14195214-591A-45B7-851A-19D3BA2413F9}
{D9F5065B-6190-431B-850C-117E3D64AB33} = {D6597E3A-6892-4A68-8E14-042FC941FDA2}
{B620D10A-CD8E-4A34-8B27-FD6257E63AD0} = {63B94FD6-3F3D-4E04-9727-48E86AC4384C}
{C5BDA61F-3A56-4B59-901D-0A17E78F4076} = {D6597E3A-6892-4A68-8E14-042FC941FDA2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4D9AF12B-1F7F-45A7-9E8C-E4E46ADCBD1F}
Expand Down
22 changes: 22 additions & 0 deletions docs/articles/samples/IntroDotTraceDiagnoser.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
uid: BenchmarkDotNet.Samples.IntroDotTraceDiagnoser
---

## Sample: IntroDotTraceDiagnoser

If you want to get a performance profile of your benchmarks, just add the `[DotTraceDiagnoser]` attribute, as shown below.
As a result, BenchmarkDotNet performs bonus benchmark runs using attached
[dotTrace Command-Line Profiler](https://www.jetbrains.com/help/profiler/Performance_Profiling__Profiling_Using_the_Command_Line.html).
The obtained snapshots are saved to the `artifacts` folder.
These snapshots can be opened using the [standalone dotTrace](https://www.jetbrains.com/profiler/),
or [dotTrace in Rider](https://www.jetbrains.com/help/rider/Performance_Profiling.html).

### Source code

[!code-csharp[IntroDotTraceDiagnoser.cs](../../../samples/BenchmarkDotNet.Samples/IntroDotTraceDiagnoser.cs)]

### Links

* The permanent link to this sample: @BenchmarkDotNet.Samples.IntroDotTraceDiagnoser

---
2 changes: 2 additions & 0 deletions docs/articles/samples/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
href: IntroDisassemblyDry.md
- name: IntroDisassemblyRyuJit
href: IntroDisassemblyRyuJit.md
- name: IntroDotTraceDiagnoser
href: IntroDotTraceDiagnoser.md
- name: IntroEnvVars
href: IntroEnvVars.md
- name: IntroEventPipeProfiler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<PackageReference Include="System.Drawing.Common" Version="4.7.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\BenchmarkDotNet.Diagnostics.dotTrace\BenchmarkDotNet.Diagnostics.dotTrace.csproj" />
<ProjectReference Include="..\..\src\BenchmarkDotNet\BenchmarkDotNet.csproj" />
<ProjectReference Include="..\..\src\BenchmarkDotNet.Diagnostics.Windows\BenchmarkDotNet.Diagnostics.Windows.csproj" />
</ItemGroup>
Expand Down
26 changes: 26 additions & 0 deletions samples/BenchmarkDotNet.Samples/IntroDotTraceDiagnoser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnostics.dotTrace;

namespace BenchmarkDotNet.Samples
{
// Enables dotTrace profiling for all jobs
[DotTraceDiagnoser]
// Adds the default "external-process" job
// Profiling is performed using dotTrace command-line Tools
// See: https://www.jetbrains.com/help/profiler/Performance_Profiling__Profiling_Using_the_Command_Line.html
[SimpleJob]
// Adds an "in-process" job
// Profiling is performed using dotTrace SelfApi
// NuGet reference: https://www.nuget.org/packages/JetBrains.Profiler.SelfApi
[InProcess]
public class IntroDotTraceDiagnoser
{
[Benchmark]
public void Fibonacci() => Fibonacci(30);

private static int Fibonacci(int n)
{
return n <= 1 ? n : Fibonacci(n - 1) + Fibonacci(n - 2);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<TargetFrameworks>net6.0;net462;netcoreapp3.1</TargetFrameworks>
<NoWarn>$(NoWarn);1591</NoWarn>
<AssemblyTitle>BenchmarkDotNet.Diagnostics.dotTrace</AssemblyTitle>
<AssemblyName>BenchmarkDotNet.Diagnostics.dotTrace</AssemblyName>
<PackageId>BenchmarkDotNet.Diagnostics.dotTrace</PackageId>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\BenchmarkDotNet\BenchmarkDotNet.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="JetBrains.Profiler.SelfApi" Version="2.4.2" />
</ItemGroup>

</Project>
148 changes: 148 additions & 0 deletions src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Extensions;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Portability;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Toolchains;
using BenchmarkDotNet.Validators;
using RunMode = BenchmarkDotNet.Diagnosers.RunMode;

namespace BenchmarkDotNet.Diagnostics.dotTrace
{
public class DotTraceDiagnoser : IProfiler
{
private readonly Uri nugetUrl;
private readonly string toolsDownloadFolder;

public DotTraceDiagnoser(Uri nugetUrl = null, string toolsDownloadFolder = null)
{
this.nugetUrl = nugetUrl;
this.toolsDownloadFolder = toolsDownloadFolder;
}

public IEnumerable<string> Ids => new[] { "DotTrace" };
public string ShortName => "dotTrace";

public RunMode GetRunMode(BenchmarkCase benchmarkCase)
{
return IsSupported(benchmarkCase.Job.Environment.GetRuntime().RuntimeMoniker) ? RunMode.ExtraRun : RunMode.None;
}

private readonly List<string> snapshotFilePaths = new ();

public void Handle(HostSignal signal, DiagnoserActionParameters parameters)
{
var job = parameters.BenchmarkCase.Job;
bool isInProcess = job.GetToolchain().IsInProcess;
var logger = parameters.Config.GetCompositeLogger();
DotTraceToolBase tool = isInProcess
? new InProcessDotTraceTool(logger, nugetUrl, downloadTo: toolsDownloadFolder)
: new ExternalDotTraceTool(logger, nugetUrl, downloadTo: toolsDownloadFolder);

var runtimeMoniker = job.Environment.GetRuntime().RuntimeMoniker;
if (!IsSupported(runtimeMoniker))
{
logger.WriteLineError($"Runtime '{runtimeMoniker}' is not supported by dotTrace");
return;
}

switch (signal)
{
case HostSignal.BeforeAnythingElse:
tool.Init(parameters);
break;
case HostSignal.BeforeActualRun:
snapshotFilePaths.Add(tool.Start(parameters));
break;
case HostSignal.AfterActualRun:
tool.Stop(parameters);
break;
}
}

public IEnumerable<IExporter> Exporters => Enumerable.Empty<IExporter>();
public IEnumerable<IAnalyser> Analysers => Enumerable.Empty<IAnalyser>();

public IEnumerable<ValidationError> Validate(ValidationParameters validationParameters)
{
var runtimeMonikers = validationParameters.Benchmarks.Select(b => b.Job.Environment.GetRuntime().RuntimeMoniker).Distinct();
foreach (var runtimeMoniker in runtimeMonikers)
{
if (!IsSupported(runtimeMoniker))
yield return new ValidationError(true, $"Runtime '{runtimeMoniker}' is not supported by dotTrace");
}
}

internal static bool IsSupported(RuntimeMoniker runtimeMoniker)
{
switch (runtimeMoniker)
{
case RuntimeMoniker.HostProcess:
case RuntimeMoniker.Net461:
case RuntimeMoniker.Net462:
case RuntimeMoniker.Net47:
case RuntimeMoniker.Net471:
case RuntimeMoniker.Net472:
case RuntimeMoniker.Net48:
case RuntimeMoniker.Net481:
case RuntimeMoniker.Net50:
case RuntimeMoniker.Net60:
case RuntimeMoniker.Net70:
case RuntimeMoniker.Net80:
return true;
case RuntimeMoniker.NotRecognized:
case RuntimeMoniker.Mono:
case RuntimeMoniker.NativeAot60:
case RuntimeMoniker.NativeAot70:
case RuntimeMoniker.NativeAot80:
case RuntimeMoniker.Wasm:
case RuntimeMoniker.WasmNet50:
case RuntimeMoniker.WasmNet60:
case RuntimeMoniker.WasmNet70:
case RuntimeMoniker.WasmNet80:
case RuntimeMoniker.MonoAOTLLVM:
case RuntimeMoniker.MonoAOTLLVMNet60:
case RuntimeMoniker.MonoAOTLLVMNet70:
case RuntimeMoniker.MonoAOTLLVMNet80:
case RuntimeMoniker.Mono60:
case RuntimeMoniker.Mono70:
case RuntimeMoniker.Mono80:
#pragma warning disable CS0618 // Type or member is obsolete
case RuntimeMoniker.NetCoreApp50:
#pragma warning restore CS0618 // Type or member is obsolete
return false;
case RuntimeMoniker.NetCoreApp20:
case RuntimeMoniker.NetCoreApp21:
case RuntimeMoniker.NetCoreApp22:
return RuntimeInformation.IsWindows();
case RuntimeMoniker.NetCoreApp30:
case RuntimeMoniker.NetCoreApp31:
return RuntimeInformation.IsWindows() || RuntimeInformation.IsLinux();
default:
throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, $"Runtime moniker {runtimeMoniker} is not supported");
}
}

public IEnumerable<Metric> ProcessResults(DiagnoserResults results) => ImmutableArray<Metric>.Empty;

public void DisplayResults(ILogger logger)
{
if (snapshotFilePaths.Any())
{
logger.WriteLineInfo("The following dotTrace snapshots were generated:");
foreach (string snapshotFilePath in snapshotFilePaths)
logger.WriteLineInfo($"* {snapshotFilePath}");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using BenchmarkDotNet.Configs;

namespace BenchmarkDotNet.Diagnostics.dotTrace
{
[AttributeUsage(AttributeTargets.Class)]
public class DotTraceDiagnoserAttribute : Attribute, IConfigSource
{
public IConfig Config { get; }

public DotTraceDiagnoserAttribute()
{
Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotTraceDiagnoser());
}

public DotTraceDiagnoserAttribute(Uri nugetUrl = null, string toolsDownloadFolder = null)
{
Config = ManualConfig.CreateEmpty().AddDiagnoser(new DotTraceDiagnoser(nugetUrl, toolsDownloadFolder));
}
}
}

0 comments on commit ff6e8d9

Please sign in to comment.