Skip to content

Commit ea607f5

Browse files
committed
Enable MacOS disassembly.
1 parent 3ad6a27 commit ea607f5

File tree

4 files changed

+58
-21
lines changed

4 files changed

+58
-21
lines changed

src/BenchmarkDotNet/Disassemblers/ClrMdDisassembler.cs

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using BenchmarkDotNet.Portability;
1111
using JetBrains.Annotations;
1212
using System.ComponentModel;
13+
using Microsoft.Diagnostics.NETCore.Client;
1314

1415
namespace BenchmarkDotNet.Disassemblers
1516
{
@@ -58,7 +59,7 @@ private static ulong GetMinValidAddress()
5859
{
5960
Environments.Platform.X86 or Environments.Platform.X64 => 4096,
6061
Environments.Platform.Arm64 => 0x100000000,
61-
_ => throw new NotSupportedException($"{RuntimeInformation.GetCurrentPlatform()} is not supported")
62+
var platform => throw new NotSupportedException($"{platform} is not supported")
6263
};
6364
throw new NotSupportedException($"{System.Runtime.InteropServices.RuntimeInformation.OSDescription} is not supported");
6465
}
@@ -71,14 +72,53 @@ private static bool IsValidAddress(ulong address)
7172
&& address != 0
7273
&& address >= MinValidAddress;
7374

75+
private DataTarget Attach(int processId)
76+
{
77+
bool isSelf = processId == System.Diagnostics.Process.GetCurrentProcess().Id;
78+
if (OsDetector.IsWindows())
79+
{
80+
// Windows CoreCLR fails to disassemble generic types when using CreateSnapshotAndAttach, and succeeds with AttachToProcess. https://github.com/microsoft/clrmd/issues/1334
81+
return isSelf && !RuntimeInformation.IsNetCore
82+
? DataTarget.CreateSnapshotAndAttach(processId)
83+
: DataTarget.AttachToProcess(processId, suspend: false);
84+
}
85+
if (OsDetector.IsLinux())
86+
{
87+
// Linux crashes when using AttachToProcess in the same process.
88+
return isSelf
89+
? DataTarget.CreateSnapshotAndAttach(processId)
90+
: DataTarget.AttachToProcess(processId, suspend: false);
91+
}
92+
if (OsDetector.IsMacOS())
93+
{
94+
// ClrMD does not support CreateSnapshotAndAttach on MacOS, and AttachToProcess is unreliable, so we have to create a dump file and load it.
95+
string? dumpPath = Path.GetTempFileName();
96+
try
97+
{
98+
try
99+
{
100+
new DiagnosticsClient(processId).WriteDump(DumpType.Full, dumpPath, logDumpGeneration: false);
101+
}
102+
catch (ServerErrorException sxe)
103+
{
104+
throw new ArgumentException($"Unable to create a snapshot of process {processId:x}.", sxe);
105+
}
106+
return DataTarget.LoadDump(dumpPath);
107+
}
108+
finally
109+
{
110+
if (dumpPath != null)
111+
{
112+
File.Delete(dumpPath);
113+
}
114+
}
115+
}
116+
throw new NotSupportedException($"{System.Runtime.InteropServices.RuntimeInformation.OSDescription} is not supported");
117+
}
118+
74119
internal DisassemblyResult AttachAndDisassemble(ClrMdArgs settings)
75120
{
76-
// Windows CoreCLR fails to disassemble generic types when using CreateSnapshotAndAttach, and succeeds with AttachToProcess. https://github.com/microsoft/clrmd/issues/1334
77-
// Non-Windows (Linux) crashes when using AttachToProcess in the same process.
78-
bool createSnapshot = (!OsDetector.IsWindows() || !RuntimeInformation.IsNetCore) && settings.ProcessId == System.Diagnostics.Process.GetCurrentProcess().Id;
79-
using var dataTarget = createSnapshot
80-
? DataTarget.CreateSnapshotAndAttach(settings.ProcessId)
81-
: DataTarget.AttachToProcess(settings.ProcessId, suspend: false);
121+
using var dataTarget = Attach(settings.ProcessId);
82122

83123
var runtime = dataTarget.ClrVersions.Single().CreateRuntime();
84124

src/BenchmarkDotNet/Disassemblers/DataContracts.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ internal override void Deserialize(JsonObject json)
195195
// Use the Capstone disassembler to recreate the instruction from the bytes.
196196
using var disassembler = CapstoneDisassembler.CreateArm64Disassembler(Arm64DisassembleMode.Arm);
197197
disassembler.EnableInstructionDetails = true;
198-
disassembler.DisassembleSyntax = (DisassembleSyntax) (int) json[SyntaxKey];
198+
disassembler.DisassembleSyntax = (DisassembleSyntax) Convert.ToInt32(json[SyntaxKey]);
199199
byte[] bytes = Convert.FromBase64String((string) bytes64);
200200
Instruction = disassembler.Disassemble(bytes, long.Parse((string) json[AddressKey])).Single();
201201
}
@@ -356,7 +356,7 @@ internal JsonObject Serialize()
356356
var addressToNameMapping = new JsonObject();
357357
foreach (var kvp in SerializedAddressToNameMapping)
358358
{
359-
addressToNameMapping[$"${kvp.Key}"] = kvp.Value;
359+
addressToNameMapping[kvp.Key.ToString()] = kvp.Value;
360360
}
361361
return new JsonObject
362362
{
@@ -389,7 +389,7 @@ internal void Deserialize(JsonObject json)
389389
int addressIndex = 0;
390390
foreach (var kvp in addressToNameMapping)
391391
{
392-
serializedAddressToNameMapping[addressIndex].Key = ulong.Parse(kvp.Key.Substring(1));
392+
serializedAddressToNameMapping[addressIndex].Key = ulong.Parse(kvp.Key);
393393
serializedAddressToNameMapping[addressIndex].Value = (string) kvp.Value;
394394
++addressIndex;
395395
}

src/BenchmarkDotNet/Disassemblers/DisassemblyDiagnoser.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ public IEnumerable<ValidationError> Validate(ValidationParameters validationPara
124124
yield break;
125125
}
126126

127+
if (Config.RunInHost && OsDetector.IsMacOS())
128+
{
129+
yield return new ValidationError(true, "Disassembling in the host process is not supported on MacOS");
130+
yield break;
131+
}
132+
127133
foreach (var benchmark in validationParameters.Benchmarks)
128134
{
129135
if (benchmark.Job.Infrastructure.TryGetToolchain(out var toolchain) && toolchain is InProcessNoEmitToolchain)
@@ -184,9 +190,8 @@ private static bool ShouldUseMonoDisassembler(BenchmarkCase benchmarkCase)
184190
=> benchmarkCase.Job.Environment.Runtime is MonoRuntime
185191
|| (RuntimeInformation.IsMono && benchmarkCase.Job.Infrastructure.TryGetToolchain(out var toolchain) && toolchain.IsInProcess);
186192

187-
// when we add macOS support, RuntimeInformation.IsMacOS() needs to be added here
188193
private static bool ShouldUseClrMdDisassembler(BenchmarkCase benchmarkCase)
189-
=> !ShouldUseMonoDisassembler(benchmarkCase) && (OsDetector.IsWindows() || OsDetector.IsLinux());
194+
=> !ShouldUseMonoDisassembler(benchmarkCase) && (OsDetector.IsWindows() || OsDetector.IsLinux() || OsDetector.IsMacOS());
190195

191196
private static IEnumerable<IExporter> GetExporters(Dictionary<BenchmarkCase, DisassemblyResult> results, DisassemblyDiagnoserConfig config)
192197
{

tests/BenchmarkDotNet.IntegrationTests/DisassemblyDiagnoserTests.cs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public static IEnumerable<object[]> GetAllJits()
4343
yield return [Jit.RyuJit, Platform.X64, CsProjCoreToolchain.NetCoreApp80]; // .NET Core x64
4444
// We could add Platform.X86 here, but it would make our CI more complicated.
4545
}
46-
else if (RuntimeInformation.GetCurrentPlatform() is Platform.Arm64 && OsDetector.IsLinux())
46+
else if (RuntimeInformation.GetCurrentPlatform() is Platform.Arm64)
4747
{
4848
yield return [Jit.RyuJit, Platform.Arm64, CsProjCoreToolchain.NetCoreApp80]; // .NET Core arm64
4949
}
@@ -90,8 +90,6 @@ public void Recursive()
9090
[Trait(Constants.Category, Constants.BackwardCompatibilityCategory)]
9191
public void CanDisassembleAllMethodCalls(Jit jit, Platform platform, IToolchain toolchain)
9292
{
93-
if (OsDetector.IsMacOS()) return; // currently not supported
94-
9593
var disassemblyDiagnoser = new DisassemblyDiagnoser(
9694
new DisassemblyDiagnoserConfig(printSource: true, maxDepth: 3));
9795

@@ -112,8 +110,6 @@ public void CanDisassembleAllMethodCalls(Jit jit, Platform platform, IToolchain
112110
[Trait(Constants.Category, Constants.BackwardCompatibilityCategory)]
113111
public void CanDisassembleAllMethodCallsUsingFilters(Jit jit, Platform platform, IToolchain toolchain)
114112
{
115-
if (OsDetector.IsMacOS()) return; // currently not supported
116-
117113
var disassemblyDiagnoser = new DisassemblyDiagnoser(
118114
new DisassemblyDiagnoserConfig(printSource: true, maxDepth: 1, filters: new[] { "*WithCalls*" }));
119115

@@ -140,8 +136,6 @@ public void CanDisassembleAllMethodCallsUsingFilters(Jit jit, Platform platform,
140136
[Trait(Constants.Category, Constants.BackwardCompatibilityCategory)]
141137
public void CanDisassembleGenericTypes(Jit jit, Platform platform, IToolchain toolchain)
142138
{
143-
if (OsDetector.IsMacOS()) return; // currently not supported
144-
145139
var disassemblyDiagnoser = new DisassemblyDiagnoser(
146140
new DisassemblyDiagnoserConfig(printSource: true, maxDepth: 3));
147141

@@ -163,8 +157,6 @@ [Benchmark] public void JustReturn() { }
163157
[Trait(Constants.Category, Constants.BackwardCompatibilityCategory)]
164158
public void CanDisassembleInlinableBenchmarks(Jit jit, Platform platform, IToolchain toolchain)
165159
{
166-
if (OsDetector.IsMacOS()) return; // currently not supported
167-
168160
var disassemblyDiagnoser = new DisassemblyDiagnoser(
169161
new DisassemblyDiagnoserConfig(printSource: true, maxDepth: 3));
170162

0 commit comments

Comments
 (0)