/
DisassemblyDiagnoser.cs
175 lines (148 loc) · 7.73 KB
/
DisassemblyDiagnoser.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
using System;
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Disassemblers;
using BenchmarkDotNet.Disassemblers.Exporters;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Helpers;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Portability;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Toolchains.InProcess.NoEmit;
using BenchmarkDotNet.Validators;
namespace BenchmarkDotNet.Diagnosers
{
public class DisassemblyDiagnoser : IDiagnoser
{
private static readonly Lazy<string> ptrace_scope = new Lazy<string>(() => ProcessHelper.RunAndReadOutput("cat", "/proc/sys/kernel/yama/ptrace_scope").Trim());
private readonly WindowsDisassembler windowsDisassembler;
private readonly LinuxDisassembler linuxDisassembler;
private readonly MonoDisassembler monoDisassembler;
private readonly Dictionary<BenchmarkCase, DisassemblyResult> results;
public DisassemblyDiagnoser(DisassemblyDiagnoserConfig config)
{
Config = config;
windowsDisassembler = new WindowsDisassembler(config);
linuxDisassembler = new LinuxDisassembler(config);
monoDisassembler = new MonoDisassembler(config);
results = new Dictionary<BenchmarkCase, DisassemblyResult>();
Exporters = GetExporters(results, config);
}
public DisassemblyDiagnoserConfig Config { get; }
public IReadOnlyDictionary<BenchmarkCase, DisassemblyResult> Results => results;
public IEnumerable<string> Ids => new[] { nameof(DisassemblyDiagnoser) };
public IEnumerable<IExporter> Exporters { get; }
public IEnumerable<IAnalyser> Analysers => new IAnalyser[] { new DisassemblyAnalyzer(results) };
public IEnumerable<Metric> ProcessResults(DiagnoserResults diagnoserResults)
{
if (results.TryGetValue(diagnoserResults.BenchmarkCase, out var disassemblyResult))
yield return new Metric(NativeCodeSizeMetricDescriptor.Instance, SumNativeCodeSize(disassemblyResult));
}
public RunMode GetRunMode(BenchmarkCase benchmarkCase)
{
if (ShouldUseWindowsDisassembler(benchmarkCase) || ShouldUseLinuxDisassembler(benchmarkCase))
return RunMode.NoOverhead;
if (ShouldUseMonoDisassembler(benchmarkCase))
return RunMode.SeparateLogic;
return RunMode.None;
}
public void Handle(HostSignal signal, DiagnoserActionParameters parameters)
{
var benchmark = parameters.BenchmarkCase;
switch (signal)
{
case HostSignal.AfterAll when ShouldUseWindowsDisassembler(benchmark):
results.Add(benchmark, windowsDisassembler.Disassemble(parameters));
break;
case HostSignal.AfterAll when ShouldUseLinuxDisassembler(benchmark):
results.Add(benchmark, linuxDisassembler.Disassemble(parameters));
break;
case HostSignal.SeparateLogic when ShouldUseMonoDisassembler(benchmark):
results.Add(benchmark, monoDisassembler.Disassemble(benchmark, benchmark.Job.Environment.Runtime as MonoRuntime));
break;
}
}
public void DisplayResults(ILogger logger)
=> logger.WriteInfo(
results.Any()
? "Disassembled benchmarks got exported to \".\\BenchmarkDotNet.Artifacts\\results\\*asm.md\""
: "No benchmarks were disassembled");
public IEnumerable<ValidationError> Validate(ValidationParameters validationParameters)
{
var currentPlatform = RuntimeInformation.GetCurrentPlatform();
if (currentPlatform != Platform.X64 && currentPlatform != Platform.X86)
{
yield return new ValidationError(true, $"{currentPlatform} is not supported (Iced library limitation)");
yield break;
}
foreach (var benchmark in validationParameters.Benchmarks)
{
if (benchmark.Job.Infrastructure.HasValue(InfrastructureMode.ToolchainCharacteristic) && benchmark.Job.Infrastructure.Toolchain is InProcessNoEmitToolchain)
{
yield return new ValidationError(true, "InProcessToolchain has no DisassemblyDiagnoser support", benchmark);
}
if (ShouldUseLinuxDisassembler(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")
{
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");
}
}
}
}
private static bool ShouldUseMonoDisassembler(BenchmarkCase benchmarkCase)
=> benchmarkCase.Job.Environment.Runtime is MonoRuntime || RuntimeInformation.IsMono;
private static bool ShouldUseWindowsDisassembler(BenchmarkCase benchmarkCase)
=> !(benchmarkCase.Job.Environment.Runtime is MonoRuntime) && RuntimeInformation.IsWindows();
private static bool ShouldUseLinuxDisassembler(BenchmarkCase benchmarkCase)
=> !(benchmarkCase.Job.Environment.Runtime is MonoRuntime) && RuntimeInformation.IsLinux();
private static IEnumerable<IExporter> GetExporters(Dictionary<BenchmarkCase, DisassemblyResult> results, DisassemblyDiagnoserConfig config)
{
if (config.ExportGithubMarkdown)
{
yield return new GithubMarkdownDisassemblyExporter(results, config);
}
if (config.ExportHtml)
{
yield return new HtmlDisassemblyExporter(results, config);
}
if (config.ExportCombinedDisassemblyReport)
{
yield return new CombinedDisassemblyExporter(results, config);
}
if (config.ExportDiff)
{
yield return new GithubMarkdownDiffDisassemblyExporter(results, config);
}
}
private static long SumNativeCodeSize(DisassemblyResult disassembly)
=> disassembly.Methods.Sum(method => method.Maps.Sum(map => map.SourceCodes.OfType<Asm>().Sum(asm => asm.Instruction.Length)));
private class NativeCodeSizeMetricDescriptor : IMetricDescriptor
{
internal static readonly IMetricDescriptor Instance = new NativeCodeSizeMetricDescriptor();
public string Id => "Native Code Size";
public string DisplayName => "Code Size";
public string Legend => "Native code size of the disassembled method(s)";
public string NumberFormat => "N0";
public UnitType UnitType => UnitType.Size;
public string Unit => SizeUnit.B.Name;
public bool TheGreaterTheBetter => false;
public int PriorityInCategory => 0;
}
}
}