383 lines
12 KiB
C#
383 lines
12 KiB
C#
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: Windows.cs
|
|
//
|
|
// Note: P/Invoke definitions for manipulating threads, processes, and jobs.
|
|
//
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.ComponentModel;
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
// This only works on kernels later than Vista, XP64, and W2003S.
|
|
// Notably, it does not work on XP 32-bit. So, we can't learn which
|
|
// processors are virtual processors (HyperThreading), so we can't
|
|
// make sure that we schedule jobs only on the main virtual processor
|
|
// of each physical core yet. The reason to do this is because
|
|
// HyperThreading really doesn't work for CPU-bound tasks; in nearly
|
|
// all tests, running CPU-bound tasks on both virtual processors of
|
|
// a single hyper-threaded core results in *worse* performance, largely
|
|
// due to the increased cache pressure.
|
|
//
|
|
// #define ENABLE_LOGICAL_PROCESSOR_INFORMATION
|
|
|
|
|
|
namespace Windows
|
|
{
|
|
public class Kernel32
|
|
{
|
|
const string DllName = "KERNEL32.DLL";
|
|
|
|
[DllImport(DllName, CharSet = CharSet.Unicode)]
|
|
static extern IntPtr CreateJobObject(
|
|
IntPtr SecurityAttributes,
|
|
string Name);
|
|
|
|
public static IntPtr CreateJobObject()
|
|
{
|
|
IntPtr handle = CreateJobObject(IntPtr.Zero, null);
|
|
if (handle == IntPtr.Zero || handle == ((IntPtr)~0)) {
|
|
int error = Marshal.GetLastWin32Error();
|
|
throw new Exception("The job object could not be created.",
|
|
new Win32Exception(error));
|
|
}
|
|
|
|
return handle;
|
|
}
|
|
|
|
[DllImport(DllName, SetLastError = true)]
|
|
public static extern bool CloseHandle(IntPtr ObjectHandle);
|
|
|
|
[DllImport(DllName, SetLastError = true)]
|
|
public static extern bool AssignProcessToJobObject(
|
|
IntPtr JobHandle,
|
|
IntPtr ProcessHandle);
|
|
|
|
[DllImport(DllName, SetLastError=true)]
|
|
static unsafe extern bool QueryInformationJobObject(
|
|
IntPtr JobHandle,
|
|
JobObjectInformationClass InformationClass,
|
|
void* Buffer,
|
|
int BufferLength,
|
|
out int ReturnLength);
|
|
|
|
public static JOBOBJECT_BASIC_ACCOUNTING_INFORMATION
|
|
QueryJobObjectBasicAccountingInformation(IntPtr JobHandle)
|
|
{
|
|
unsafe
|
|
{
|
|
JOBOBJECT_BASIC_ACCOUNTING_INFORMATION buffer;
|
|
int length;
|
|
|
|
if (!QueryInformationJobObject(
|
|
JobHandle,
|
|
JobObjectInformationClass.Accounting,
|
|
&buffer,
|
|
sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION),
|
|
out length))
|
|
{
|
|
int error = Marshal.GetLastWin32Error();
|
|
throw new Exception("Failed to query information from job object.",
|
|
new Win32Exception(error));
|
|
}
|
|
return buffer;
|
|
}
|
|
}
|
|
|
|
public static bool IsCurrentProcessInJob()
|
|
{
|
|
unsafe
|
|
{
|
|
JOBOBJECT_BASIC_ACCOUNTING_INFORMATION information = new JOBOBJECT_BASIC_ACCOUNTING_INFORMATION();
|
|
int returnLength = 0;
|
|
|
|
if (QueryInformationJobObject(IntPtr.Zero, JobObjectInformationClass.Accounting,
|
|
&information, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION), out returnLength))
|
|
{
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[DllImport(DllName, SetLastError = true, CharSet=CharSet.Unicode)]
|
|
static unsafe extern int FormatMessage(
|
|
FormatMessageFlag flags,
|
|
void* Source,
|
|
int MessageId,
|
|
int LanguageId,
|
|
void* Buffer,
|
|
int BufferLength,
|
|
void* Arguments);
|
|
|
|
public static unsafe string FormatMessageFromSystem(int messageId, int languageId)
|
|
{
|
|
char* buffer = null;
|
|
|
|
int length = FormatMessage(
|
|
FormatMessageFlag.AllocateBuffer | FormatMessageFlag.FromSystem,
|
|
null,
|
|
messageId,
|
|
languageId,
|
|
(void*)&buffer,
|
|
0,
|
|
null);
|
|
if (length == 0)
|
|
return null;
|
|
|
|
string result = new String(buffer, 0, length);
|
|
Marshal.FreeHGlobal((IntPtr)(void*)buffer);
|
|
return result;
|
|
}
|
|
|
|
public unsafe static string FormatMessageFromSystem(int messageId)
|
|
{
|
|
return FormatMessageFromSystem(messageId, 0);
|
|
}
|
|
|
|
public static string GetErrorText(int error)
|
|
{
|
|
string text = FormatMessageFromSystem(error);
|
|
if (text == null) {
|
|
if (error < 0x10000 && error >= 0)
|
|
return String.Format("Unknown error: {0]", error);
|
|
else
|
|
return String.Format("Unknown error: 0x{0:x}", error);
|
|
}
|
|
else {
|
|
return text;
|
|
}
|
|
}
|
|
|
|
[DllImport(DllName, SetLastError = true)]
|
|
public static extern uint SetThreadIdealProcessor(
|
|
IntPtr ThreadHandle,
|
|
int IdealProcessor);
|
|
|
|
[DllImport(DllName, SetLastError = true)]
|
|
public static extern bool GetProcessAffinityMask(
|
|
IntPtr ProcessHandle,
|
|
out UIntPtr ProcessAffinityMask,
|
|
out UIntPtr SystemAffinityMask);
|
|
|
|
[DllImport(DllName)]
|
|
public static extern IntPtr GetCurrentThread();
|
|
|
|
[DllImport(DllName)]
|
|
public static extern IntPtr GetCurrentProcess();
|
|
|
|
[DllImport(DllName, SetLastError = true)]
|
|
public static extern bool GetThreadTimes(
|
|
IntPtr hThread,
|
|
out /* FILETIME */ long lpCreationTime,
|
|
out /* FILETIME */ long lpExitTime,
|
|
out /* FILETIME */ long lpKernelTime,
|
|
out /* FILETIME */ long lpUserTime
|
|
);
|
|
|
|
public void GetThreadTimes(
|
|
IntPtr ThreadHandle,
|
|
out DateTime creationTime,
|
|
out DateTime exitTime,
|
|
out TimeSpan kernelTime,
|
|
out TimeSpan userTime)
|
|
{
|
|
long ticks_creationTime;
|
|
long ticks_exitTime;
|
|
long ticks_kernelTime;
|
|
long ticks_userTime;
|
|
|
|
if (GetThreadTimes(ThreadHandle,
|
|
out ticks_creationTime,
|
|
out ticks_exitTime,
|
|
out ticks_kernelTime,
|
|
out ticks_userTime))
|
|
{
|
|
creationTime = new DateTime(ticks_creationTime);
|
|
exitTime = new DateTime(ticks_exitTime);
|
|
kernelTime = new TimeSpan(ticks_kernelTime);
|
|
userTime = new TimeSpan(ticks_userTime);
|
|
}
|
|
else {
|
|
throw new Exception("The thread times could not be retrieved.",
|
|
new Win32Exception(Marshal.GetLastWin32Error()));
|
|
}
|
|
}
|
|
|
|
|
|
public static int GetProcessorCount()
|
|
{
|
|
UIntPtr processAffinityMask;
|
|
UIntPtr systemAffinityMask;
|
|
|
|
if (!GetProcessAffinityMask(GetCurrentProcess(), out processAffinityMask, out systemAffinityMask)) {
|
|
Console.WriteLine("nib: Failed to get process affinity mask! Assuming 1 processor.");
|
|
return 1;
|
|
}
|
|
|
|
ulong mask = (ulong)processAffinityMask;
|
|
int count = 0;
|
|
while (mask != 0) {
|
|
if ((mask & 1) != 0)
|
|
count++;
|
|
mask >>= 1;
|
|
}
|
|
|
|
if (count == 0)
|
|
return 1;
|
|
|
|
Console.WriteLine("nib: Found {0} processors.", count);
|
|
return count;
|
|
}
|
|
|
|
public static string GetLastErrorText()
|
|
{
|
|
int error = Marshal.GetLastWin32Error();
|
|
return GetErrorText(error);
|
|
}
|
|
|
|
#if ENABLE_LOGICAL_PROCESSOR_INFORMATION
|
|
[DllImport(DllName, SetLastError=true)]
|
|
static unsafe extern bool GetLogicalProcessorInformation(
|
|
SYSTEM_LOGICAL_PROCESSOR_INFORMATION* buffer,
|
|
ref int length);
|
|
|
|
public static SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] GetLogicalProcessorInformation()
|
|
{
|
|
unsafe
|
|
{
|
|
int length = 0;
|
|
|
|
GetLogicalProcessorInformation(null, ref length);
|
|
if (length == 0)
|
|
return new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[0];
|
|
|
|
int count = length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
|
|
SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] array = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[count];
|
|
|
|
bool result;
|
|
fixed (SYSTEM_LOGICAL_PROCESSOR_INFORMATION* array_pinned = &array[0])
|
|
{
|
|
int length2 = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) * count;
|
|
result = GetLogicalProcessorInformation(array_pinned, ref length2);
|
|
}
|
|
if (result)
|
|
return array;
|
|
else
|
|
throw new Exception("Failed to query logical processor information.",
|
|
new Win32Exception(Marshal.GetLastWin32Error()));
|
|
}
|
|
}
|
|
|
|
|
|
public static int[] GetPhysicalProcessorList()
|
|
{
|
|
SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] logical_info = GetLogicalProcessorInformation();
|
|
|
|
for (int i = 0; i < logical_info.Length; i++) {
|
|
Debug.WriteLine(logical_info.ToString());
|
|
}
|
|
|
|
return null;
|
|
}
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
[Flags]
|
|
enum FormatMessageFlag
|
|
{
|
|
AllocateBuffer = 0x100,
|
|
FromSystem = 0x1000,
|
|
FromModule = 0x800,
|
|
}
|
|
|
|
public enum JobObjectInformationClass
|
|
{
|
|
Accounting = 1, // JOBOBJECT_BASIC_ACCOUNTING_INFORMATION
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct JOBOBJECT_BASIC_ACCOUNTING_INFORMATION
|
|
{
|
|
public TimeSpan TotalUserTime;
|
|
public TimeSpan TotalKernelTime;
|
|
public TimeSpan ThisPeriodTotalUserTime;
|
|
public TimeSpan ThisPeriodTotalKernelTime;
|
|
public uint TotalPageFaultCount;
|
|
public uint TotalProcesses;
|
|
public uint ActiveProcesses;
|
|
public uint TotalTerminatedProcesses;
|
|
}
|
|
|
|
|
|
#if ENABLE_LOGICAL_PROCESSOR_INFORMATION
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION
|
|
{
|
|
public UIntPtr ProcessorMask;
|
|
public LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
|
|
|
|
//
|
|
//union {
|
|
// struct
|
|
// {
|
|
// BYTE Flags;
|
|
// } ProcessorCore;
|
|
// struct
|
|
// {
|
|
// DWORD NodeNumber;
|
|
// } NumaNode;
|
|
// CACHE_DESCRIPTOR Cache;
|
|
// ULONGLONG Reserved[2];
|
|
//}
|
|
//
|
|
|
|
public ProcessorRelationUnion RelationUnion;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Explicit)]
|
|
struct ProcessorRelationUnion
|
|
{
|
|
[FieldOffset(0)] public CACHE_DESCRIPTOR Cache;
|
|
[FieldOffset(0)] public uint NumaNodeNumber;
|
|
[FieldOffset(0)] public byte ProcessorCoreFlags;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
struct CACHE_DESCRIPTOR
|
|
{
|
|
public byte Level;
|
|
public byte Associativity;
|
|
public ushort LineSize;
|
|
public uint Size;
|
|
public PROCESSOR_CACHE_TYPE Type;
|
|
}
|
|
|
|
enum PROCESSOR_CACHE_TYPE
|
|
{
|
|
Unified = 0,
|
|
Instruction = 1,
|
|
Data = 2,
|
|
Trace = 3,
|
|
}
|
|
|
|
enum LOGICAL_PROCESSOR_RELATIONSHIP : uint
|
|
{
|
|
ProcessorCore = 0,
|
|
NumaNode = 1,
|
|
RelationCache = 2,
|
|
}
|
|
#endif
|
|
}
|