Files
zodiacon-WindowsInternals/CpuSet/CpuSet.cpp
T
Pavel Yosifovich 6036a5b619 Added CpuSet tool
2016-12-05 10:47:41 +02:00

399 lines
9.5 KiB
C++

// CpuSet.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemAllowedCpuSetsInformation = 168,
SystemCpuSetInformation = 175,
SystemCpuSetTagInformation = 176,
} SYSTEM_INFORMATION_CLASS;
typedef enum _PROCESSINFOCLASS {
ProcessDefaultCpuSetsInformation = 66,
ProcessAllowedCpuSetsInformation = 67,
} PROCESSINFOCLASS;
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
#define STATUS_SUCCESS ((NTSTATUS)0)
extern "C"
NTSTATUS
NTAPI
NtSetInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ PROCESSINFOCLASS ProcessInformationClass,
_In_reads_bytes_opt_(ProcessInformationLength) PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength
);
extern "C"
NTSTATUS
NTAPI
NtQuerySystemInformationEx(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_In_reads_bytes_(InputBufferLength) PVOID InputBuffer,
_In_ ULONG InputBufferLength,
_Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
extern "C"
NTSTATUS
NTAPI
NtSetSystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_In_reads_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
_In_ ULONG SystemInformationLength
);
extern "C"
NTSTATUS
NTAPI
NtQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Out_writes_bytes_to_opt_(SystemInformationLength, *ReturnLength) PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
extern "C"
NTSTATUS
NTAPI
NtQueryInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ PROCESSINFOCLASS ProcessInformationClass,
_Out_writes_bytes_opt_(ProcessInformationLength) PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
);
#pragma comment(lib, "ntdll")
void PrintTitle() {
printf("CpuSet v1.0: Examine and change CPU Sets\nCopyright (C)2016 Pavel Yosifovich\n\n");
}
void PrintUsage() {
printf("Usage: cpuset [-?] [-n index] [-p pid ] [-s cpusets]\n");
printf("\t-p\tShow/Change CPU Set for the specified process (otherwise System CPU set)\n");
printf("\t-n\tShow CPU Set with the specified index (System CPU set only)\n");
printf("\t-s\tSet the allowed CPU sets. Pass CPU mask.\n");
printf("\t\tdecimal and hex values supported (prefix hex with 0x)\n");
}
void PrintDecHex(const char* title, unsigned value, bool cr = true) {
printf("%s%u (0x%X)", title, value, value);
if (cr)
printf("\n");
}
void PrintBool(const char* title, BYTE value) {
printf("%s%s", title, value ? "True" : "False");
}
void PrintSystemCpuSet(SYSTEM_CPU_SET_INFORMATION& cpuSet, bool process = false) {
PrintDecHex(" Id: ", cpuSet.CpuSet.Id);
printf(" Group: %d\n", cpuSet.CpuSet.Group);
printf(" Logical Processor: %d\n", (int)cpuSet.CpuSet.LogicalProcessorIndex);
printf(" Core: %d\n", (int)cpuSet.CpuSet.CoreIndex);
printf(" Last Level Cache: %d\n", cpuSet.CpuSet.LastLevelCacheIndex);
printf(" NUMA Node: %d\n", (int)cpuSet.CpuSet.NumaNodeIndex);
PrintDecHex(" Flags: ", cpuSet.CpuSet.AllFlags, false);
PrintBool(" Parked: ", cpuSet.CpuSet.Parked);
PrintBool(" Allocated: ", cpuSet.CpuSet.Allocated);
PrintBool(" Realtime: ", cpuSet.CpuSet.RealTime);
printf(" Tag: %llu", cpuSet.CpuSet.AllocationTag);
if (process)
PrintBool(" Allocated to process: ", cpuSet.CpuSet.AllocatedToTargetProcess);
printf("\n");
}
bool ArgumentIs(int index, const char* value) {
if (index >= __argc)
return false;
auto arg = __argv[index];
if (*arg != '-' && *arg != '/')
return false;
return ::_strcmpi(value, arg + 1) == 0;
}
void PrintSystemCpuSets(int pid, int index = -1) {
HANDLE hProcess = nullptr;
if (pid) {
hProcess = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
}
printf("System CPU Sets\n");
printf("---------------\n");
auto size = 0x20 * 64;
auto cpuSet = static_cast<PSYSTEM_CPU_SET_INFORMATION>(::malloc(size));
ULONG length;
auto success = ::GetSystemCpuSetInformation(cpuSet, size, &length, hProcess, 0);
int count = length / cpuSet->Size;;
printf("Total CPU Sets: %d\n", count);
for (int i = 0; i < count; i++) {
if (index < 0 || index == i) {
printf("\nCPU Set %d\n", i);
PrintSystemCpuSet(cpuSet[i], hProcess != nullptr);
}
}
if (hProcess)
::CloseHandle(hProcess);
::free(cpuSet);
}
void PrintAllowedCpuSet(int pid) {
NTSTATUS status;
ULONG64 sets[20];
ULONG size;
if (pid) {
HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (!hProcess) {
printf("Error opening process (%d)\n", ::GetLastError());
return;
}
ULONG ids[32];
DWORD count;
auto success = ::GetProcessDefaultCpuSets(hProcess, ids, 256, &count);
if (success) {
for (ULONG i = 0; i < count; i++) {
printf("%d: ", i);
PrintDecHex("", ids[i]);
}
}
status = ::NtQueryInformationProcess(hProcess, ProcessDefaultCpuSetsInformation, sets, sizeof(sets), &size);
}
else {
HANDLE hProcess = nullptr;
status = ::NtQuerySystemInformationEx(SystemCpuSetInformation, &hProcess, sizeof(hProcess), sets, sizeof(sets), &size);
}
if (status == STATUS_SUCCESS) {
if (size == 0)
printf("Empty CPU set\n");
printf("CPU Masks: ");
for (ULONG i = 0; i < size / sizeof(ULONG64); i++) {
printf("0x%02I64X ", sets[i]);
}
printf("\n");
//for (ULONG i = 0; i < count; i++) {
// printf("%d: ", i);
// PrintDecHex("", ids[i]);
//}
}
else {
printf("Failed to get allowed CPU sets (%d)\n", ::GetLastError());
return;
}
}
template<typename T>
bool ParseCPUSet(T* pSet, const char* text, int& count) {
bool last = false;
for (int i = 0; !last && i < 32; i++) {
if (*text == '\0')
return true;
auto comma = strchr(text, ',');
if (comma == text)
return false;
if (comma == nullptr) {
comma = text + strlen(text);
last = true;
}
size_t len = comma - text;
bool hex = false;
if (len > 2 && (::strncmp(text, "0x", 2) == 0 || ::strncmp(text, "0X", 2) == 0))
hex = true;
int value = std::stoi(std::string(text + (hex ? 2 : 0), comma), nullptr, hex ? 16 : 10);
pSet[i] = value;
text = comma + 1;
count = i + 1;
}
return true;
}
int Error(const char* text) {
auto code = ::GetLastError();
printf("%s (%d)\n", text, code);
return code;
}
BOOL SetPrivilege(
HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
bool bEnablePrivilege // to enable or disable privilege
) {
TOKEN_PRIVILEGES tp;
LUID luid;
if (!::LookupPrivilegeValue(nullptr, lpszPrivilege, &luid))
return FALSE;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if (!::AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr)) {
return FALSE;
}
return TRUE;
}
std::vector<ULONG> BuildIdsFromMask(ULONG64* set, ULONG count) {
int index = 0x100; // first set
std::vector<ULONG> ids;
for (ULONG i = 0; i < count; i++) {
auto mask = set[i];
while (mask) {
if (mask & 1)
ids.push_back(index);
index++;
mask >>= 1;
}
}
return ids;
}
int main(int argc, char** argv) {
PrintTitle();
if (argc == 1) {
PrintSystemCpuSets(0);
return 0;
}
HANDLE hToken = nullptr;
::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
if (!SetPrivilege(hToken, SE_INC_BASE_PRIORITY_NAME, TRUE)) {
printf("Access denied. Try running from an elevated command prompt.\n");
return 1;
}
::CloseHandle(hToken);
int index = 1;
bool system = false;
DWORD pid = 0;
int n = -1;
ULONG cpuSet[64];
ULONG64 systemCpuSet[20];
int count = 0;
bool change = false;
bool interrupt = false;
while (index < argc) {
if (ArgumentIs(index, "?")) {
PrintUsage();
return 0;
}
else if (ArgumentIs(index, "k")) {
system = true;
index++;
}
else if (ArgumentIs(index, "n")) {
n = atoi(argv[index + 1]);
index += 2;
}
else if (ArgumentIs(index, "p")) {
pid = atoi(argv[index + 1]);
index += 2;
}
else if (ArgumentIs(index, "s")) {
if (index == argc - 1) {
printf("Error: missing CPU set after -s\n");
PrintUsage();
return 1;
}
if (!ParseCPUSet(systemCpuSet, argv[index + 1], count)) {
printf("System CPU set list error\n");
return 1;
}
change = true;
index += 2;
}
else {
printf("Unknown argument.\n");
return 1;
}
}
if (change) {
HANDLE hProcess = nullptr;
if (pid) {
hProcess = ::OpenProcess(PROCESS_SET_LIMITED_INFORMATION, FALSE, pid);
if (!hProcess)
return Error("Failed to open process");
}
auto pSet = systemCpuSet;
if (count == 1 && cpuSet[0] == 0) {
pSet = nullptr;
count = 0;
}
BOOL success;
if (hProcess) {
if (pSet) {
auto ids = BuildIdsFromMask(pSet, count);
success = ::SetProcessDefaultCpuSets(hProcess, ids.data(), (ULONG)ids.size());
}
else {
success = ::SetProcessDefaultCpuSets(hProcess, nullptr, 0);
}
if (success)
printf("Process CPU set changed successfully.\n");
else
return Error("Failed to change process CPU set");
::CloseHandle(hProcess);
}
else {
// set system CPU set
auto status = ::NtSetSystemInformation(SystemAllowedCpuSetsInformation, pSet, count * sizeof(ULONG64));
if (status == STATUS_SUCCESS)
printf("System CPU set changed successfully.\n");
else
printf("Failed to change system CPU set (%d)\n", status);
}
}
else {
PrintSystemCpuSets(pid, n);
if (pid) {
printf("\n");
PrintAllowedCpuSet(pid);
}
}
return 0;
}