Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
}
}
}
Loading