Skip to content

Instantly share code, notes, and snippets.

@mjs3339
Created March 24, 2020 09:21
Show Gist options
  • Save mjs3339/cc7b6ee2856d1bfa459119e96d8a5a2e to your computer and use it in GitHub Desktop.
Save mjs3339/cc7b6ee2856d1bfa459119e96d8a5a2e to your computer and use it in GitHub Desktop.
Rdtsc 32 and 64 Bit In C#
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
public static class Rdtsc
{
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate ulong FuncUInt64();
private const uint PAGE_READWRITE = 0x04;
private const uint PAGE_EXECUTE = 0x10;
private const uint PAGE_EXECUTE_READWRITE = 0x40;
private const uint MEM_COMMIT = 0x1000;
private const uint MEM_RELEASE = 0x8000;
public static readonly FuncUInt64 Timestamp;
public static readonly FuncUInt64 TimestampP;
public static readonly bool IsRdtscSupported;
public static readonly bool IsRdtscPSupported;
static Rdtsc()
{
SystemInfo systemInfo;
GetNativeSystemInfo(out systemInfo);
if (systemInfo.wProcessorArchitecture != 0 && systemInfo.wProcessorArchitecture != 9)
{
Timestamp = StopwatchGetTimestamp;
TimestampP = StopwatchGetTimestamp;
IsRdtscSupported = false;
IsRdtscPSupported = false;
return;
}
byte[] cpuid;
byte[] rdtsc;
byte[] rdtscp;
byte[] rdtsccpuid;
IsRdtscSupported = true;
if (Is64Bit())
{
cpuid = new byte[]
{
0x53, 0xB8, 0x00, 0x00, 0x00, 0x80, 0x0F, 0xA2, 0xBB, 0x01, 0x00, 0x00, 0x80, 0x39, 0xD8, 0x72, 0x16, 0x89, 0xD8,
0x48, 0xC7, 0xC2, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xA2, 0x89, 0xC8, 0x48, 0xC1, 0xE0, 0x20, 0x48, 0x09, 0xD0, 0xEB,
0x03, 0x48, 0x31, 0xC0, 0x5B, 0xC3
};
rdtsc = new byte[] {0x0F, 0x31, 0x48, 0xC1, 0xE2, 0x20, 0x48, 0x09, 0xD0, 0xC3};
rdtscp = new byte[] {0x0F, 0x01, 0xF9, 0x48, 0xC1, 0xE2, 0x20, 0x48, 0x09, 0xD0, 0xC3};
rdtsccpuid = new byte[]
{0x53, 0x31, 0xC0, 0x0F, 0xA2, 0x0F, 0x31, 0x48, 0xC1, 0xE2, 0x20, 0x48, 0x09, 0xD0, 0x5B, 0xC3};
}
else
{
cpuid = new byte[]
{
0x53, 0xB8, 0x00, 0x00, 0x00, 0x80, 0x0F, 0xA2, 0xBB, 0x01, 0x00, 0x00, 0x80, 0x39, 0xD8, 0x72, 0x0A, 0x89, 0xD8,
0x0F, 0xA2, 0x89, 0xD0, 0x89, 0xCA, 0xEB, 0x04, 0x31, 0xC0, 0x31, 0xD2, 0x5B, 0xC3
};
rdtsc = new byte[] {0x0F, 0x31, 0xC3};
rdtscp = new byte[] {0x0F, 0x01, 0xF9, 0xC3};
rdtsccpuid = new byte[] {0x53, 0x31, 0xC0, 0x0F, 0xA2, 0x0F, 0x31, 0x5B, 0xC3};
}
var buf = IntPtr.Zero;
try
{
var cpuidLength = (cpuid.Length & 63) != 0 ? (cpuid.Length | 63) + 1 : cpuid.Length;
var rdtscLength = (rdtsc.Length & 63) != 0 ? (rdtsc.Length | 63) + 1 : rdtsc.Length;
var rdtscpLength = (rdtscp.Length & 63) != 0 ? (rdtscp.Length | 63) + 1 : rdtscp.Length;
var rdtsccpuidLength = (rdtsccpuid.Length & 63) != 0 ? (rdtsccpuid.Length | 63) + 1 : rdtsccpuid.Length;
var totalLength = cpuidLength + rdtscLength + Math.Max(rdtscpLength, rdtsccpuidLength);
buf = VirtualAlloc(IntPtr.Zero, (IntPtr) totalLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (buf != IntPtr.Zero)
{
Marshal.Copy(cpuid, 0, buf, cpuid.Length);
for (var i = cpuid.Length; i < cpuidLength; i++)
Marshal.WriteByte(buf, i, 0x90);
Marshal.Copy(rdtsc, 0, buf + cpuidLength, rdtsc.Length);
for (var i = rdtsc.Length; i < rdtscLength; i++)
Marshal.WriteByte(buf, cpuidLength + i, 0x90);
var cpuidFunc = (FuncUInt64) Marshal.GetDelegateForFunctionPointer(buf, typeof(FuncUInt64));
var supportedFeatures = cpuidFunc();
byte[] rdtscpSelected;
int rdtscpSelectedLength;
if ((supportedFeatures & (1L << 27)) != 0)
{
rdtscpSelected = rdtscp;
rdtscpSelectedLength = rdtscpLength;
IsRdtscPSupported = true;
}
else
{
rdtscpSelected = rdtsccpuid;
rdtscpSelectedLength = rdtsccpuidLength;
IsRdtscPSupported = false;
}
Marshal.Copy(rdtscpSelected, 0, buf + cpuidLength + rdtscLength, rdtscpSelected.Length);
for (var i = rdtscpSelected.Length; i < rdtscpSelectedLength; i++)
Marshal.WriteByte(buf, cpuidLength + rdtscLength + i, 0x90);
var result = VirtualProtect(buf, (IntPtr) totalLength, PAGE_EXECUTE, out var oldProtection);
if (result)
{
Timestamp = (FuncUInt64) Marshal.GetDelegateForFunctionPointer(buf + cpuidLength, typeof(FuncUInt64));
TimestampP = (FuncUInt64) Marshal.GetDelegateForFunctionPointer(buf + cpuidLength + rdtscLength,
typeof(FuncUInt64));
buf = IntPtr.Zero;
}
}
}
finally
{
if (buf != IntPtr.Zero)
VirtualFree(buf, IntPtr.Zero, MEM_RELEASE);
}
}
private static bool Is64Bit()
{
return Marshal.SizeOf(typeof(IntPtr)) == 8;
}
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern void GetNativeSystemInfo(out SystemInfo lpSystemInfo);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
private static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, out uint lpflOldProtect);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, uint dwFreeType);
private static ulong StopwatchGetTimestamp()
{
return unchecked((ulong) Stopwatch.GetTimestamp());
}
[StructLayout(LayoutKind.Sequential)]
private struct SystemInfo
{
public readonly ushort wProcessorArchitecture;
public readonly ushort wReserved;
public readonly uint dwPageSize;
public readonly IntPtr lpMinimumApplicationAddress;
public readonly IntPtr lpMaximumApplicationAddress;
public readonly IntPtr dwActiveProcessorMask;
public readonly uint dwNumberOfProcessors;
public readonly uint dwProcessorType;
public readonly uint dwAllocationGranularity;
public readonly ushort wProcessorLevel;
public readonly ushort wProcessorRevision;
}
}
@ethanfischer
Copy link

Is this windows-only?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment