Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detecting virtual machine hypervisor, #167 #527

Merged
merged 15 commits into from Aug 31, 2017
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/guide/FAQ.md
Expand Up @@ -36,4 +36,7 @@ For example, you can use the `SimpleJob` or `ShortRunJob` attributes:
[ShortRunJob]
```
* **Q** My benchmark unexpectedly stopped and I saw the information about error code. What can I do?
**A** BenchmarkDotNet generates, builds and runs new process for every benchmark. This behavior is sometimes interpreted by anti-virus as dangerous, and the process is killed. Use `EnvironmentAnalyser` to detect antivirus software and configure your benchmark to use `InProcessToolchain`.
**A** BenchmarkDotNet generates, builds and runs new process for every benchmark. This behavior is sometimes interpreted by anti-virus as dangerous, and the process is killed. Use `EnvironmentAnalyser` to detect antivirus software and configure your benchmark to use `InProcessToolchain`.

* **Q** Can I run benchmark on the virtual machine?
**A** Yes, of course. However, it can affect results because of the shared, physical machine, virtualization process and incorrect `Stopwatch.Frequency`. If you are unsure whether an application is running on virtual environment, use `EnvironmentAnalyser` to detect VM hypervisor.
10 changes: 8 additions & 2 deletions src/BenchmarkDotNet.Core/Analysers/EnvironmentAnalyser.cs
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
Expand Down Expand Up @@ -37,6 +36,13 @@ public override IEnumerable<Conclusion> AnalyseSummary(Summary summary)
if (avProducts.Any())
yield return CreateWarning(CreateWarningAboutAntivirus(avProducts));
}

var vmHypervisor = summary.HostEnvironmentInfo.VirtualMachineHypervisor.Value;
if (vmHypervisor != null)
{
yield return CreateWarning($"Benchmark was executed on the virtual machine with {vmHypervisor.Name} hypervisor. " +
"Virtualization can affect the measurement result.");
}
}

private static string CreateWarningAboutAntivirus(ICollection<Antivirus> avProducts)
Expand Down
3 changes: 3 additions & 0 deletions src/BenchmarkDotNet.Core/Environments/HostEnvironmentInfo.cs
Expand Up @@ -58,6 +58,8 @@ public class HostEnvironmentInfo : BenchmarkEnvironmentInfo

public Lazy<ICollection<Antivirus>> AntivirusProducts { get; protected set; }

public Lazy<VirtualMachineHypervisor> VirtualMachineHypervisor { get; protected set; }

static HostEnvironmentInfo()
{
MainCultureInfo = (CultureInfo)CultureInfo.InvariantCulture.Clone();
Expand All @@ -75,6 +77,7 @@ protected HostEnvironmentInfo()
JitModules = RuntimeInformation.GetJitModulesInfo();
DotNetSdkVersion = new Lazy<string>(DotNetCliCommandExecutor.GetDotNetSdkVersion);
AntivirusProducts = new Lazy<ICollection<Antivirus>>(RuntimeInformation.GetAntivirusProducts);
VirtualMachineHypervisor = new Lazy<VirtualMachineHypervisor>(RuntimeInformation.GetVirtualMachineHypervisor);
}

public new static HostEnvironmentInfo GetCurrent() => Current ?? (Current = new HostEnvironmentInfo());
Expand Down
16 changes: 16 additions & 0 deletions src/BenchmarkDotNet.Core/Portability/HyperV.cs
@@ -0,0 +1,16 @@
namespace BenchmarkDotNet.Portability
{
public class HyperV : VirtualMachineHypervisor
{
public static HyperV Default { get; } = new HyperV();

private HyperV() { }

public override string Name => "Hyper-V";

public override bool IsVirtualMachine(string manufacturer, string model)
{
return ContainsVmIdentifier(manufacturer, "microsoft") && ContainsVmIdentifier(model, "virtual");
}
}
}
30 changes: 30 additions & 0 deletions src/BenchmarkDotNet.Core/Portability/RuntimeInformation.cs
Expand Up @@ -321,5 +321,35 @@ internal static ICollection<Antivirus> GetAntivirusProducts()
return Array.Empty<Antivirus>();
#endif
}


internal static VirtualMachineHypervisor GetVirtualMachineHypervisor()
{
VirtualMachineHypervisor[] hypervisors = { HyperV.Default, VirtualBox.Default, VMware.Default };

if (IsWindows())
{
#if !CORE
try
{
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select * from Win32_ComputerSystem"))
{
using (ManagementObjectCollection items = searcher.Get())
{
foreach (ManagementBaseObject item in items)
{
string manufacturer = item["Manufacturer"]?.ToString();
string model = item["Model"]?.ToString();
return hypervisors.FirstOrDefault(x => x.IsVirtualMachine(manufacturer, model));
}
}
}
}
catch { }
#endif
}

return null;
}
}
}
19 changes: 16 additions & 3 deletions src/BenchmarkDotNet.Core/Portability/StringExtensions.cs
Expand Up @@ -6,12 +6,25 @@ internal static class StringExtensions
{
internal static bool EqualsWithIgnoreCase(this string left, string right)
{
return left.Equals(right, IgnoreCaseStringComparison);
}

internal static bool ContainsWithIgnoreCase(this string text, string word)
{
return text.IndexOf(word, IgnoreCaseStringComparison) >= 0;
}

private static StringComparison IgnoreCaseStringComparison
{
get
{
#if !CORE
return left.Equals(right, StringComparison.InvariantCultureIgnoreCase);
return StringComparison.InvariantCultureIgnoreCase;
#else
// http://stackoverflow.com/questions/14600694/where-has-stringcomparison-invariantcultureignorecase-gone
return left.Equals(right, StringComparison.OrdinalIgnoreCase);
// http://stackoverflow.com/questions/14600694/where-has-stringcomparison-invariantcultureignorecase-gone
return StringComparison.OrdinalIgnoreCase;
#endif
}
}

This comment was marked as spam.

This comment was marked as spam.

}
}
16 changes: 16 additions & 0 deletions src/BenchmarkDotNet.Core/Portability/VMware.cs
@@ -0,0 +1,16 @@
namespace BenchmarkDotNet.Portability
{
public class VMware : VirtualMachineHypervisor
{
public static VMware Default { get; } = new VMware();

private VMware() { }

public override string Name => "VMware";

public override bool IsVirtualMachine(string manufacturer, string model)
{
return ContainsVmIdentifier(model, "vmware");
}
}
}
16 changes: 16 additions & 0 deletions src/BenchmarkDotNet.Core/Portability/VirtualBox.cs
@@ -0,0 +1,16 @@
namespace BenchmarkDotNet.Portability
{
public class VirtualBox : VirtualMachineHypervisor
{
public static VirtualBox Default { get; } = new VirtualBox();

private VirtualBox() { }

public override string Name => "VirtualBox";

public override bool IsVirtualMachine(string manufacturer, string model)
{
return ContainsVmIdentifier(model, "virtualbox");
}
}
}
14 changes: 14 additions & 0 deletions src/BenchmarkDotNet.Core/Portability/VirtualMachineHypervisor.cs
@@ -0,0 +1,14 @@
namespace BenchmarkDotNet.Portability
{
public abstract class VirtualMachineHypervisor
{
public abstract string Name { get; }

public abstract bool IsVirtualMachine(string manufacturer, string model);

protected bool ContainsVmIdentifier(string systemInformation, string vmIdentifier)
{
return systemInformation != null && systemInformation.ContainsWithIgnoreCase(vmIdentifier);
}
}
}
30 changes: 30 additions & 0 deletions tests/BenchmarkDotNet.Tests/Portability/HyperVTests.cs
@@ -0,0 +1,30 @@
using BenchmarkDotNet.Portability;
using Xunit;

namespace BenchmarkDotNet.Tests.Portability
{
public class HyperVTests
{
private readonly VirtualMachineHypervisor hypervisor = HyperV.Default;

[Fact]
public void ContainsCorrectName()
{
Assert.Equal("Hyper-V", hypervisor.Name);
}

[Theory]
[InlineData("Microsoft Corporation", "Virtual Machine", true)]
[InlineData("microsoft corporation", "virtual machine", true)]
[InlineData("microsoft", "virtual", true)]
[InlineData("Dell", "virtual", false)]
[InlineData("Dell", "ubuntu", false)]
[InlineData("microsoft corporation", null, false)]
[InlineData(null, "virtual machine", false)]
public void DetectsVirtualMachine(string manufacturer, string model, bool expectedResult)
{
bool result = hypervisor.IsVirtualMachine(manufacturer, model);
Assert.Equal(expectedResult, result);
}
}
}
17 changes: 17 additions & 0 deletions tests/BenchmarkDotNet.Tests/Portability/RuntimeInformationTests.cs
@@ -0,0 +1,17 @@
using BenchmarkDotNet.Portability;
using BenchmarkDotNet.Tests.XUnit;
using Xunit;

namespace BenchmarkDotNet.Tests.Portability
{
public class RuntimeInformationTests
{
[AppVeyorWithClassicFrameworkOnlyFact]
public void DetectsHyperVOnAppveyor()
{
VirtualMachineHypervisor hypervisor = RuntimeInformation.GetVirtualMachineHypervisor();

Assert.Equal(hypervisor, HyperV.Default);
}
}
}
29 changes: 29 additions & 0 deletions tests/BenchmarkDotNet.Tests/Portability/VMWareTests.cs
@@ -0,0 +1,29 @@
using BenchmarkDotNet.Portability;
using Xunit;

namespace BenchmarkDotNet.Tests.Portability
{
public class VMwareTests
{
private readonly VirtualMachineHypervisor hypervisor = VMware.Default;

[Fact]
public void ContainsCorrectName()
{
Assert.Equal("VMware", hypervisor.Name);
}

[Theory]
[InlineData("VMWare Inc", "VMWare", true)]
[InlineData("VMWare Inc", "vmWare", true)]
[InlineData("redundant", "vmWare", true)]
[InlineData(null, "vmWare", true)]
[InlineData("VMWare Inc", "redundant", false)]
[InlineData("VMWare Inc", null, false)]
public void DetectsVirtualMachine(string manufacturer, string model, bool expectedResult)
{
bool result = hypervisor.IsVirtualMachine(manufacturer, model);
Assert.Equal(expectedResult, result);
}
}
}
27 changes: 27 additions & 0 deletions tests/BenchmarkDotNet.Tests/Portability/VirtualBoxTests.cs
@@ -0,0 +1,27 @@
using BenchmarkDotNet.Portability;
using Xunit;

namespace BenchmarkDotNet.Tests.Portability
{
public class VirtualBoxTests
{
private readonly VirtualMachineHypervisor hypervisor = VirtualBox.Default;

[Fact]
public void ContainsCorrectName()
{
Assert.Equal("VirtualBox", hypervisor.Name);
}

[Theory]
[InlineData("redundant", "virtualbox", true)]
[InlineData("redundant", "VirtualBox", true)]
[InlineData("redundant", "vmware", false)]
[InlineData("redundant", null, false)]
public void DetectsVirtualMachine(string manufacturer, string model, bool expectedResult)
{
bool result = hypervisor.IsVirtualMachine(manufacturer, model);
Assert.Equal(expectedResult, result);
}
}
}
21 changes: 21 additions & 0 deletions tests/BenchmarkDotNet.Tests/XUnit/AppveyorOnlyFactAttribute.cs
@@ -0,0 +1,21 @@
using System;
using BenchmarkDotNet.Portability;
using Xunit;

namespace BenchmarkDotNet.Tests.XUnit
{
public class AppVeyorOnlyFactAttribute : FactAttribute
{
private const string Message = "Test is available only on the AppVeyor";
private static readonly string skip;

static AppVeyorOnlyFactAttribute()
{
string value = Environment.GetEnvironmentVariable("APPVEYOR");
bool appVeyorDetected = !string.IsNullOrEmpty(value) && value.EqualsWithIgnoreCase("true");
skip = appVeyorDetected ? null : Message;
}

public override string Skip => skip;
}
}
@@ -0,0 +1,21 @@
namespace BenchmarkDotNet.Tests.XUnit
{
public class AppVeyorWithClassicFrameworkOnlyFactAttribute : AppVeyorOnlyFactAttribute

This comment was marked as spam.

{
private const string Message = "Test requires classic .NET Framework";

private bool classicDotNet
{
get
{
#if !CORE

This comment was marked as spam.

return true;
#else
return false;
#endif
}
}

public override string Skip => base.Skip ?? (classicDotNet ? null : Message);
}
}