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
8 changes: 8 additions & 0 deletions src/BenchmarkDotNet/BenchmarkDotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,17 @@
<ItemGroup>
<ProjectReference Include="..\BenchmarkDotNet.Annotations\BenchmarkDotNet.Annotations.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.298">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
<!-- Settings for PolySharp -->
<PropertyGroup>
<PolySharpIncludeRuntimeSupportedAttributes>true</PolySharpIncludeRuntimeSupportedAttributes>
<!-- Required to avoid conflicts with CSWin32-->
<PolySharpExcludeGeneratedTypes>System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute</PolySharpExcludeGeneratedTypes>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="PolySharp" Version="1.16.0">
Expand Down
9 changes: 8 additions & 1 deletion src/BenchmarkDotNet/Detectors/OsDetector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ internal static bool IsWindows() =>
RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
#endif


[SupportedOSPlatformGuard("linux")]
internal static bool IsLinux() =>
#if NET6_0_OR_GREATER
Expand Down Expand Up @@ -170,4 +169,12 @@ internal static bool IsTvOS() =>
#else
RuntimeInformation.IsOSPlatform(OSPlatform.Create("TVOS"));
#endif

[SupportedOSPlatformGuard("windows6.1")]
internal static bool IsWindows7OrLater() =>
#if NET6_0_OR_GREATER
OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(6, 1);
#else
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version >= new Version(6, 1);
#endif
}
41 changes: 11 additions & 30 deletions src/BenchmarkDotNet/Environments/PhysicalMemoryInfo.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using BenchmarkDotNet.Detectors;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using Windows.Win32;
using Windows.Win32.System.SystemInformation;

namespace BenchmarkDotNet.Environments
{
Expand Down Expand Up @@ -35,13 +39,13 @@ public static class SystemMemory
{
try
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
if (OsDetector.IsWindows7OrLater())
return GetWindowsMemory();

if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
if (OsDetector.IsLinux())
return GetLinuxMemory();

if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
if (OsDetector.IsMacOS())
return GetMacMemory();
}
catch (Exception)
Expand All @@ -52,36 +56,13 @@ public static class SystemMemory
return null;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private class MEMORYSTATUSEX
{
public uint dwLength;
public uint dwMemoryLoad;
public ulong ullTotalPhys;
public ulong ullAvailPhys;
public ulong ullTotalPageFile;
public ulong ullAvailPageFile;
public ulong ullTotalVirtual;
public ulong ullAvailVirtual;
public ulong ullAvailExtendedVirtual;

public MEMORYSTATUSEX()
{
dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
}
}

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX lpBuffer);

[SupportedOSPlatform("windows5.1.2600")]
Comment thread
timcassell marked this conversation as resolved.
private static PhysicalMemoryInfo? GetWindowsMemory()
{
var memStatus = new MEMORYSTATUSEX();
if (GlobalMemoryStatusEx(memStatus))
{
var memStatus = new MEMORYSTATUSEX { dwLength = (uint)Marshal.SizeOf<MEMORYSTATUSEX>() };
if (PInvoke.GlobalMemoryStatusEx(ref memStatus))
return new PhysicalMemoryInfo((long)memStatus.ullTotalPhys, (long)memStatus.ullAvailPhys);
}

return null;
}

Expand Down
64 changes: 32 additions & 32 deletions src/BenchmarkDotNet/Helpers/PowerManagementHelper.cs
Original file line number Diff line number Diff line change
@@ -1,64 +1,64 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;
using System.Runtime.Versioning;
using Windows.Win32;
using Windows.Win32.Foundation;

namespace BenchmarkDotNet.Helpers
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SupportedOSPlatform("windows6.0.6000")]
internal class PowerManagementHelper
{
private const uint ErrorMoreData = 234;
private const uint SuccessCode = 0;

internal static Guid? CurrentPlan
internal static unsafe Guid? CurrentPlan
{
get
{
IntPtr activeGuidPtr = IntPtr.Zero;
uint res = PowerGetActiveScheme(IntPtr.Zero, ref activeGuidPtr);
if (res != SuccessCode)
WIN32_ERROR res = PInvoke.PowerGetActiveScheme(null, out var activePolicyGuidPtr);
if (res != WIN32_ERROR.NO_ERROR)
return null;

return Marshal.PtrToStructure<Guid>(activeGuidPtr);
var activePolicyGuid = *activePolicyGuidPtr;

PInvoke.LocalFree((HLOCAL)activePolicyGuidPtr);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


return activePolicyGuid;
}
}

internal static string CurrentPlanFriendlyName
internal unsafe static string CurrentPlanFriendlyName
{
get
{
uint buffSize = 0;
StringBuilder buffer = new StringBuilder();
IntPtr activeGuidPtr = IntPtr.Zero;
uint res = PowerGetActiveScheme(IntPtr.Zero, ref activeGuidPtr);
if (res != SuccessCode)
WIN32_ERROR res = PInvoke.PowerGetActiveScheme(null, out var activeGuidPtr);
if (res != WIN32_ERROR.NO_ERROR)
return "";
res = PowerReadFriendlyName(IntPtr.Zero, activeGuidPtr, IntPtr.Zero, IntPtr.Zero, buffer, ref buffSize);
if (res == ErrorMoreData)

Guid activeSchemaGuid = *activeGuidPtr;
PInvoke.LocalFree((HLOCAL)activeGuidPtr);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add LocalFree API call here


Span<byte> buffer = stackalloc byte[260];
uint bufferSize = (uint)buffer.Length;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original code don't set buffSize.
So it looks like always returns ERROR_MORE_DATA.


res = PInvoke.PowerReadFriendlyName(null, activeSchemaGuid, null, null, buffer, ref bufferSize);
if (res == WIN32_ERROR.ERROR_MORE_DATA)
{
buffer.Capacity = (int)buffSize;
res = PowerReadFriendlyName(IntPtr.Zero, activeGuidPtr,
IntPtr.Zero, IntPtr.Zero, buffer, ref buffSize);
buffer = new byte[(int)bufferSize];
res = PInvoke.PowerReadFriendlyName(null, activeSchemaGuid, null, null, buffer, ref bufferSize);
bufferSize = (uint)buffer.Length;
}
if (res != SuccessCode)

if (res != WIN32_ERROR.NO_ERROR)
return "";

return buffer.ToString();
ReadOnlySpan<char> chars = MemoryMarshal.Cast<byte, char>(buffer.Slice(0, (int)bufferSize));
return chars.TrimEnd('\0').ToString(); // Trim null terminator of PWSTR.
}
}

internal static bool Set(Guid newPolicy)
{
return PowerSetActiveScheme(IntPtr.Zero, ref newPolicy) == 0;
return PInvoke.PowerSetActiveScheme(null, newPolicy) == WIN32_ERROR.NO_ERROR;
}

[DllImport("powrprof.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern uint PowerReadFriendlyName(IntPtr RootPowerKey, IntPtr SchemeGuid, IntPtr SubGroupOfPowerSettingGuid, IntPtr PowerSettingGuid, StringBuilder Buffer, ref uint BufferSize);

[DllImport("powrprof.dll", ExactSpelling = true)]
private static extern int PowerSetActiveScheme(IntPtr ReservedZero, ref Guid policyGuid);

[DllImport("powrprof.dll", ExactSpelling = true)]
private static extern uint PowerGetActiveScheme(IntPtr UserRootPowerKey, ref IntPtr ActivePolicyGuid);
}
}
65 changes: 17 additions & 48 deletions src/BenchmarkDotNet/Helpers/TaskbarProgress.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using BenchmarkDotNet.Detectors;
using System.Runtime.InteropServices;
using Windows.Win32.System.Console;
using Windows.Win32;

namespace BenchmarkDotNet.Helpers
{
Expand All @@ -15,9 +17,7 @@ internal enum TaskbarProgressState

internal class TaskbarProgress : DisposeAtProcessTermination
{
private static readonly bool OsVersionIsSupported = OsDetector.IsWindows()
// Must be windows 7 or greater
&& Environment.OSVersion.Version >= new Version(6, 1);
private static readonly bool OsVersionIsSupported = OsDetector.IsWindows7OrLater();

private Com? com;
private Terminal? terminal;
Expand Down Expand Up @@ -97,9 +97,6 @@ private interface ITaskbarList3
[ComImport]
private class TaskbarInstance { }

[DllImport("kernel32.dll")]
private static extern IntPtr GetConsoleWindow();

private readonly ITaskbarList3 taskbarInstance;
private readonly IntPtr consoleWindowHandle;

Expand All @@ -113,7 +110,7 @@ private Com(IntPtr handle)
{
try
{
IntPtr handle = GetConsoleWindow();
IntPtr handle = PInvoke.GetConsoleWindow();
if (handle == IntPtr.Zero)
{
return null;
Expand Down Expand Up @@ -142,49 +139,21 @@ internal void SetValue(uint progressValue)

private sealed class Terminal
{
[Flags]
private enum ConsoleModes : uint
{
ENABLE_PROCESSED_INPUT = 0x0001,
ENABLE_LINE_INPUT = 0x0002,
ENABLE_ECHO_INPUT = 0x0004,
ENABLE_WINDOW_INPUT = 0x0008,
ENABLE_MOUSE_INPUT = 0x0010,
ENABLE_INSERT_MODE = 0x0020,
ENABLE_QUICK_EDIT_MODE = 0x0040,
ENABLE_EXTENDED_FLAGS = 0x0080,
ENABLE_AUTO_POSITION = 0x0100,

ENABLE_PROCESSED_OUTPUT = 0x0001,
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002,
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004,
DISABLE_NEWLINE_AUTO_RETURN = 0x0008,
ENABLE_LVB_GRID_WORLDWIDE = 0x0010
}

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleModes lpMode);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleMode(IntPtr hConsoleHandle, ConsoleModes dwMode);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(int nStdHandle);
private const int STD_OUTPUT_HANDLE = -11;

private readonly IntPtr consoleHandle;
private readonly SafeHandle consoleHandle;
private uint currentProgress;

private Terminal(IntPtr handle)
private Terminal(SafeHandle handle)
=> consoleHandle = handle;

internal static Terminal? MaybeCreateInstanceAndSetInitialState(TaskbarProgressState initialTaskbarState)
{
IntPtr handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (handle == IntPtr.Zero)
var handle = PInvoke.GetStdHandle_SafeHandle(STD_HANDLE.STD_OUTPUT_HANDLE);
if (handle.IsInvalid)
{
return null;
}
if (!GetConsoleMode(handle, out ConsoleModes previousConsoleMode)
|| !SetConsoleMode(handle, ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING | ConsoleModes.ENABLE_PROCESSED_OUTPUT))
if (!PInvoke.GetConsoleMode(handle, out CONSOLE_MODE previousConsoleMode)
|| !PInvoke.SetConsoleMode(handle, CONSOLE_MODE.ENABLE_VIRTUAL_TERMINAL_PROCESSING | CONSOLE_MODE.ENABLE_PROCESSED_OUTPUT))
{
// If we failed to set virtual terminal processing mode, it is likely due to an older Windows version that does not support it,
// or legacy console. In either case the TaskbarProgressCom will take care of the progress. See https://stackoverflow.com/a/44574463/5703407.
Expand All @@ -193,16 +162,16 @@ private Terminal(IntPtr handle)
}
var terminal = new Terminal(handle);
terminal.WriteStateSequence(initialTaskbarState);
SetConsoleMode(handle, previousConsoleMode);
PInvoke.SetConsoleMode(handle, previousConsoleMode);
return terminal;
}

internal void SetState(TaskbarProgressState taskbarState)
{
GetConsoleMode(consoleHandle, out ConsoleModes previousConsoleMode);
SetConsoleMode(consoleHandle, ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING | ConsoleModes.ENABLE_PROCESSED_OUTPUT);
PInvoke.GetConsoleMode(consoleHandle, out CONSOLE_MODE previousConsoleMode);
PInvoke.SetConsoleMode(consoleHandle, CONSOLE_MODE.ENABLE_VIRTUAL_TERMINAL_PROCESSING | CONSOLE_MODE.ENABLE_PROCESSED_OUTPUT);
WriteStateSequence(taskbarState);
SetConsoleMode(consoleHandle, previousConsoleMode);
PInvoke.SetConsoleMode(consoleHandle, previousConsoleMode);
}

private void WriteStateSequence(TaskbarProgressState taskbarState)
Expand Down Expand Up @@ -238,10 +207,10 @@ internal void SetValue(uint progressValue)
{
currentProgress = progressValue;
// Write progress sequence to console for Windows Terminal (https://github.com/microsoft/terminal/discussions/14268).
GetConsoleMode(consoleHandle, out ConsoleModes previousConsoleMode);
SetConsoleMode(consoleHandle, ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING | ConsoleModes.ENABLE_PROCESSED_OUTPUT);
PInvoke.GetConsoleMode(consoleHandle, out CONSOLE_MODE previousConsoleMode);
PInvoke.SetConsoleMode(consoleHandle, CONSOLE_MODE.ENABLE_VIRTUAL_TERMINAL_PROCESSING | CONSOLE_MODE.ENABLE_PROCESSED_OUTPUT);
WriteProgressSequence(progressValue);
SetConsoleMode(consoleHandle, previousConsoleMode);
PInvoke.SetConsoleMode(consoleHandle, previousConsoleMode);
}

private static void WriteProgressSequence(uint progressValue)
Expand Down
5 changes: 5 additions & 0 deletions src/BenchmarkDotNet/NativeMethods.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"$schema": "https://aka.ms/CsWin32.schema.json",
"allowMarshaling": false,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NativeAot requires this setting to be false. (Default: true)

"multiTargetingFriendlyAPIs": true
}
14 changes: 14 additions & 0 deletions src/BenchmarkDotNet/NativeMethods.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
PowerGetActiveScheme
PowerSetActiveScheme
PowerReadFriendlyName
GetConsoleWindow
GetConsoleMode
GetStdHandle
SetConsoleMode
CloseHandle
PowerCreateRequest
PowerClearRequest
PowerSetRequest
PowerGetActiveScheme
LocalFree
GlobalMemoryStatusEx
Loading