Skip to content

Commit 6992856

Browse files
authored
Add support for MonoVM to MemoryDiagnoser (#2227)
* simplify GetRuntimeVersion logic, introduce IsOldMono and IsNewMono helper methods * add a failing test * fix MemoryDiagnoser for MonoVM on .NET 6+ keep it a nop for WASM (I have no ability to test it quickly)
1 parent 530d001 commit 6992856

File tree

4 files changed

+48
-22
lines changed

4 files changed

+48
-22
lines changed

src/BenchmarkDotNet/Engines/Engine.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ private static void ForceGcCollect()
267267

268268
private static void EnableMonitoring()
269269
{
270-
if (RuntimeInformation.IsMono) // Monitoring is not available in Mono, see http://stackoverflow.com/questions/40234948/how-to-get-the-number-of-allocated-bytes-in-mono
270+
if (RuntimeInformation.IsOldMono) // Monitoring is not available in Mono, see http://stackoverflow.com/questions/40234948/how-to-get-the-number-of-allocated-bytes-in-mono
271271
return;
272272

273273
if (RuntimeInformation.IsFullFramework)

src/BenchmarkDotNet/Engines/GcStats.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,11 @@ public static GcStats FromForced(int forcedFullGarbageCollections)
132132

133133
private static long GetAllocatedBytes()
134134
{
135-
if (RuntimeInformation.IsMono) // Monitoring is not available in Mono, see http://stackoverflow.com/questions/40234948/how-to-get-the-number-of-allocated-bytes-
135+
if (RuntimeInformation.IsOldMono) // Monitoring is not available in Mono, see http://stackoverflow.com/questions/40234948/how-to-get-the-number-of-allocated-bytes-
136+
return 0;
137+
138+
// we have no tests for WASM and don't want to risk introducing a new bug (https://github.com/dotnet/BenchmarkDotNet/issues/2226)
139+
if (RuntimeInformation.IsWasm)
136140
return 0;
137141

138142
// "This instance Int64 property returns the number of bytes that have been allocated by a specific

src/BenchmarkDotNet/Portability/RuntimeInformation.cs

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,15 @@ internal static class RuntimeInformation
2525
internal const string ReleaseConfigurationName = "RELEASE";
2626
internal const string Unknown = "?";
2727

28+
/// <summary>
29+
/// returns true for both the old (implementation of .NET Framework) and new Mono (.NET 6+ flavour)
30+
/// </summary>
2831
public static bool IsMono { get; } = Type.GetType("Mono.RuntimeStructs") != null; // it allocates a lot of memory, we need to check it once in order to keep Engine non-allocating!
2932

33+
public static bool IsOldMono { get; } = Type.GetType("Mono.Runtime") != null;
34+
35+
public static bool IsNewMono { get; } = IsMono && !IsOldMono;
36+
3037
public static bool IsFullFramework =>
3138
#if NET6_0_OR_GREATER
3239
false;
@@ -200,7 +207,24 @@ internal static CpuInfo GetCpuInfo()
200207

201208
internal static string GetRuntimeVersion()
202209
{
203-
if (IsMono && !IsWasm)
210+
if (IsWasm)
211+
{
212+
// code copied from https://github.com/dotnet/runtime/blob/2c573b59aaaf3fd17e2ecab95ad3769f195d2dbc/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.cs#L20-L30
213+
string versionString = typeof(object).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
214+
215+
// Strip the git hash if there is one
216+
if (versionString != null)
217+
{
218+
int plusIndex = versionString.IndexOf('+');
219+
if (plusIndex != -1)
220+
{
221+
versionString = versionString.Substring(0, plusIndex);
222+
}
223+
}
224+
225+
return $".NET Core (Mono) {versionString}";
226+
}
227+
else if (IsMono)
204228
{
205229
var monoRuntimeType = Type.GetType("Mono.Runtime");
206230
var monoDisplayName = monoRuntimeType?.GetMethod("GetDisplayName", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
@@ -221,7 +245,7 @@ internal static string GetRuntimeVersion()
221245

222246
return "Mono " + version;
223247
}
224-
else
248+
else if (IsNewMono)
225249
{
226250
return $"{GetNetCoreVersion()} using MonoVM";
227251
}
@@ -230,23 +254,6 @@ internal static string GetRuntimeVersion()
230254
{
231255
return FrameworkVersionHelper.GetFrameworkDescription();
232256
}
233-
else if (IsWasm)
234-
{
235-
// code copied from https://github.com/dotnet/runtime/blob/2c573b59aaaf3fd17e2ecab95ad3769f195d2dbc/src/libraries/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.cs#L20-L30
236-
string versionString = typeof(object).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
237-
238-
// Strip the git hash if there is one
239-
if (versionString != null)
240-
{
241-
int plusIndex = versionString.IndexOf('+');
242-
if (plusIndex != -1)
243-
{
244-
versionString = versionString.Substring(0, plusIndex);
245-
}
246-
}
247-
248-
return $".NET Core (Mono) {versionString}";
249-
}
250257
else if (IsNetCore)
251258
{
252259
return GetNetCoreVersion();

tests/BenchmarkDotNet.IntegrationTests/MemoryDiagnoserTests.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
using BenchmarkDotNet.Toolchains.InProcess.Emit;
2424
using Xunit;
2525
using Xunit.Abstractions;
26+
using BenchmarkDotNet.Toolchains.Mono;
2627

2728
namespace BenchmarkDotNet.IntegrationTests
2829
{
@@ -34,7 +35,7 @@ public class MemoryDiagnoserTests
3435

3536
public static IEnumerable<object[]> GetToolchains()
3637
{
37-
if (RuntimeInformation.IsMono) // https://github.com/mono/mono/issues/8397
38+
if (RuntimeInformation.IsOldMono) // https://github.com/mono/mono/issues/8397
3839
yield break;
3940

4041
yield return new object[] { Job.Default.GetToolchain() };
@@ -56,6 +57,11 @@ public void MemoryDiagnoserIsAccurate(IToolchain toolchain)
5657
long objectAllocationOverhead = IntPtr.Size * 2; // pointer to method table + object header word
5758
long arraySizeOverhead = IntPtr.Size; // array length
5859

60+
if (toolchain is MonoToolchain)
61+
{
62+
objectAllocationOverhead += IntPtr.Size;
63+
}
64+
5965
AssertAllocations(toolchain, typeof(AccurateAllocations), new Dictionary<string, long>
6066
{
6167
{ nameof(AccurateAllocations.EightBytesArray), 8 + objectAllocationOverhead + arraySizeOverhead },
@@ -78,6 +84,15 @@ public void MemoryDiagnoserSupportsNativeAOT()
7884
.ToToolchain());
7985
}
8086

87+
[FactDotNetCoreOnly("We don't want to test MonoVM twice (for .NET Framework 4.6.2 and .NET 7.0)")]
88+
public void MemoryDiagnoserSupportsModernMono()
89+
{
90+
if (ContinuousIntegration.IsAppVeyorOnWindows())
91+
return; // timeouts
92+
93+
MemoryDiagnoserIsAccurate(MonoToolchain.Mono70);
94+
}
95+
8196
public class AllocatingGlobalSetupAndCleanup
8297
{
8398
private List<int> list;

0 commit comments

Comments
 (0)