mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
6942632: Hotspot should be able to use more than 64 logical processors on Windows
Backport-of: f15d423fb5
This commit is contained in:
committed by
Vitaly Provodin
parent
ae011c4eb3
commit
920afb8aa8
@@ -1512,7 +1512,7 @@ else
|
||||
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libnativeStack += -lpthread
|
||||
BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeGetCreatedJavaVMs := -ljvm -lpthread
|
||||
|
||||
BUILD_HOTSPOT_JTREG_EXCLUDE += libNativeException.c
|
||||
BUILD_HOTSPOT_JTREG_EXCLUDE += libNativeException.c exeGetProcessorInfo.c
|
||||
endif
|
||||
|
||||
ifeq ($(ASAN_ENABLED), true)
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
range, \
|
||||
constraint) \
|
||||
\
|
||||
product(bool, UseAllWindowsProcessorGroups, false, \
|
||||
"Use all processor groups on supported Windows versions") \
|
||||
\
|
||||
product(bool, UseOSErrorReporting, false, \
|
||||
"Let VM fatal error propagate to the OS (ie. WER on Windows)") \
|
||||
\
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
#include "utilities/defaultStream.hpp"
|
||||
#include "utilities/events.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
#include "utilities/population_count.hpp"
|
||||
#include "utilities/vmError.hpp"
|
||||
#include "windbghelp.hpp"
|
||||
#if INCLUDE_JFR
|
||||
@@ -876,21 +877,119 @@ int os::active_processor_count() {
|
||||
return ActiveProcessorCount;
|
||||
}
|
||||
|
||||
DWORD_PTR lpProcessAffinityMask = 0;
|
||||
DWORD_PTR lpSystemAffinityMask = 0;
|
||||
int proc_count = processor_count();
|
||||
if (proc_count <= sizeof(UINT_PTR) * BitsPerByte &&
|
||||
GetProcessAffinityMask(GetCurrentProcess(), &lpProcessAffinityMask, &lpSystemAffinityMask)) {
|
||||
// Nof active processors is number of bits in process affinity mask
|
||||
int bitcount = 0;
|
||||
while (lpProcessAffinityMask != 0) {
|
||||
lpProcessAffinityMask = lpProcessAffinityMask & (lpProcessAffinityMask-1);
|
||||
bitcount++;
|
||||
}
|
||||
return bitcount;
|
||||
} else {
|
||||
return proc_count;
|
||||
bool schedules_all_processor_groups = win32::is_windows_11_or_greater() || win32::is_windows_server_2022_or_greater();
|
||||
if (UseAllWindowsProcessorGroups && !schedules_all_processor_groups && !win32::processor_group_warning_displayed()) {
|
||||
win32::set_processor_group_warning_displayed(true);
|
||||
FLAG_SET_DEFAULT(UseAllWindowsProcessorGroups, false);
|
||||
warning("The UseAllWindowsProcessorGroups flag is not supported on this Windows version and will be ignored.");
|
||||
}
|
||||
|
||||
DWORD active_processor_groups = 0;
|
||||
DWORD processors_in_job_object = win32::active_processors_in_job_object(&active_processor_groups);
|
||||
|
||||
if (processors_in_job_object > 0) {
|
||||
if (schedules_all_processor_groups) {
|
||||
// If UseAllWindowsProcessorGroups is enabled then all the processors in the job object
|
||||
// can be used. Otherwise, we will fall through to inspecting the process affinity mask.
|
||||
// This will result in using only the subset of the processors in the default processor
|
||||
// group allowed by the job object i.e. only 1 processor group will be used and only
|
||||
// the processors in that group that are allowed by the job object will be used.
|
||||
// This preserves the behavior where older OpenJDK versions always used one processor
|
||||
// group regardless of whether they were launched in a job object.
|
||||
if (!UseAllWindowsProcessorGroups && active_processor_groups > 1) {
|
||||
if (!win32::job_object_processor_group_warning_displayed()) {
|
||||
win32::set_job_object_processor_group_warning_displayed(true);
|
||||
warning("The Windows job object has enabled multiple processor groups (%d) but the UseAllWindowsProcessorGroups flag is off. Some processors might not be used.", active_processor_groups);
|
||||
}
|
||||
} else {
|
||||
return processors_in_job_object;
|
||||
}
|
||||
} else {
|
||||
if (active_processor_groups > 1 && !win32::job_object_processor_group_warning_displayed()) {
|
||||
win32::set_job_object_processor_group_warning_displayed(true);
|
||||
warning("The Windows job object has enabled multiple processor groups (%d) but only 1 is supported on this Windows version. Some processors might not be used.", active_processor_groups);
|
||||
}
|
||||
return processors_in_job_object;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD logical_processors = 0;
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
|
||||
USHORT group_count = 0;
|
||||
bool use_process_affinity_mask = false;
|
||||
bool got_process_group_affinity = false;
|
||||
|
||||
if (GetProcessGroupAffinity(GetCurrentProcess(), &group_count, nullptr) == 0) {
|
||||
DWORD last_error = GetLastError();
|
||||
if (last_error == ERROR_INSUFFICIENT_BUFFER) {
|
||||
if (group_count > 0) {
|
||||
got_process_group_affinity = true;
|
||||
|
||||
if (group_count == 1) {
|
||||
use_process_affinity_mask = true;
|
||||
}
|
||||
} else {
|
||||
warning("Unexpected group count of 0 from GetProcessGroupAffinity.");
|
||||
assert(false, "Group count must not be 0.");
|
||||
}
|
||||
} else {
|
||||
char buf[512];
|
||||
size_t buf_len = os::lasterror(buf, sizeof(buf));
|
||||
warning("Attempt to get process group affinity failed: %s", buf_len != 0 ? buf : "<unknown error>");
|
||||
}
|
||||
} else {
|
||||
warning("Unexpected GetProcessGroupAffinity success result.");
|
||||
assert(false, "Unexpected GetProcessGroupAffinity success result");
|
||||
}
|
||||
|
||||
// Fall back to SYSTEM_INFO.dwNumberOfProcessors if the process group affinity could not be determined.
|
||||
if (!got_process_group_affinity) {
|
||||
return si.dwNumberOfProcessors;
|
||||
}
|
||||
|
||||
// If the process it not in a job and the process group affinity is exactly 1 group
|
||||
// then get the number of available logical processors from the process affinity mask
|
||||
if (use_process_affinity_mask) {
|
||||
DWORD_PTR lpProcessAffinityMask = 0;
|
||||
DWORD_PTR lpSystemAffinityMask = 0;
|
||||
if (GetProcessAffinityMask(GetCurrentProcess(), &lpProcessAffinityMask, &lpSystemAffinityMask) != 0) {
|
||||
// Number of active processors is number of bits in process affinity mask
|
||||
logical_processors = population_count(lpProcessAffinityMask);
|
||||
|
||||
if (logical_processors > 0) {
|
||||
return logical_processors;
|
||||
} else {
|
||||
// We only check the process affinity mask if GetProcessGroupAffinity determined that there was
|
||||
// only 1 active group. In this case, GetProcessAffinityMask will not set the affinity mask to 0.
|
||||
warning("Unexpected process affinity mask of 0 from GetProcessAffinityMask.");
|
||||
assert(false, "Found unexpected process affinity mask: 0");
|
||||
}
|
||||
} else {
|
||||
char buf[512];
|
||||
size_t buf_len = os::lasterror(buf, sizeof(buf));
|
||||
warning("Attempt to get the process affinity mask failed: %s", buf_len != 0 ? buf : "<unknown error>");
|
||||
}
|
||||
|
||||
// Fall back to SYSTEM_INFO.dwNumberOfProcessors if the process affinity mask could not be determined.
|
||||
return si.dwNumberOfProcessors;
|
||||
}
|
||||
|
||||
if (UseAllWindowsProcessorGroups) {
|
||||
// There are no processor affinity restrictions at this point so we can return
|
||||
// the overall processor count if the OS automatically schedules threads across
|
||||
// all processors on the system. Note that older operating systems can
|
||||
// correctly report processor count but will not schedule threads across
|
||||
// processor groups unless the application explicitly uses group affinity APIs
|
||||
// to assign threads to processor groups. On these older operating systems, we
|
||||
// will continue to use the dwNumberOfProcessors field.
|
||||
if (schedules_all_processor_groups) {
|
||||
logical_processors = processor_count();
|
||||
}
|
||||
}
|
||||
|
||||
return logical_processors == 0 ? si.dwNumberOfProcessors : logical_processors;
|
||||
}
|
||||
|
||||
uint os::processor_id() {
|
||||
@@ -1976,52 +2075,13 @@ void os::print_os_info(outputStream* st) {
|
||||
}
|
||||
|
||||
void os::win32::print_windows_version(outputStream* st) {
|
||||
VS_FIXEDFILEINFO *file_info;
|
||||
TCHAR kernel32_path[MAX_PATH];
|
||||
UINT len, ret;
|
||||
|
||||
bool is_workstation = !IsWindowsServer();
|
||||
|
||||
// Get the full path to \Windows\System32\kernel32.dll and use that for
|
||||
// determining what version of Windows we're running on.
|
||||
len = MAX_PATH - (UINT)strlen("\\kernel32.dll") - 1;
|
||||
ret = GetSystemDirectory(kernel32_path, len);
|
||||
if (ret == 0 || ret > len) {
|
||||
st->print_cr("Call to GetSystemDirectory failed");
|
||||
return;
|
||||
}
|
||||
strncat(kernel32_path, "\\kernel32.dll", MAX_PATH - ret);
|
||||
|
||||
DWORD version_size = GetFileVersionInfoSize(kernel32_path, nullptr);
|
||||
if (version_size == 0) {
|
||||
st->print_cr("Call to GetFileVersionInfoSize failed");
|
||||
return;
|
||||
}
|
||||
|
||||
LPTSTR version_info = (LPTSTR)os::malloc(version_size, mtInternal);
|
||||
if (version_info == nullptr) {
|
||||
st->print_cr("Failed to allocate version_info");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetFileVersionInfo(kernel32_path, 0, version_size, version_info)) {
|
||||
os::free(version_info);
|
||||
st->print_cr("Call to GetFileVersionInfo failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!VerQueryValue(version_info, TEXT("\\"), (LPVOID*)&file_info, &len)) {
|
||||
os::free(version_info);
|
||||
st->print_cr("Call to VerQueryValue failed");
|
||||
return;
|
||||
}
|
||||
|
||||
int major_version = HIWORD(file_info->dwProductVersionMS);
|
||||
int minor_version = LOWORD(file_info->dwProductVersionMS);
|
||||
int build_number = HIWORD(file_info->dwProductVersionLS);
|
||||
int build_minor = LOWORD(file_info->dwProductVersionLS);
|
||||
int major_version = windows_major_version();
|
||||
int minor_version = windows_minor_version();
|
||||
int build_number = windows_build_number();
|
||||
int build_minor = windows_build_minor();
|
||||
int os_vers = major_version * 1000 + minor_version;
|
||||
os::free(version_info);
|
||||
|
||||
st->print(" Windows ");
|
||||
switch (os_vers) {
|
||||
@@ -2117,6 +2177,12 @@ void os::pd_print_cpu_info(outputStream* st, char* buf, size_t buflen) {
|
||||
if (proc_count < 1) {
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
|
||||
// This is the number of logical processors in the current processor group only and is therefore
|
||||
// at most 64. The GetLogicalProcessorInformation function is used to compute the total number
|
||||
// of processors. However, it requires memory to be allocated for the processor information buffer.
|
||||
// Since this method is used in paths where memory allocation should not be done (i.e. after a crash),
|
||||
// only the number of processors in the current group will be returned.
|
||||
proc_count = si.dwNumberOfProcessors;
|
||||
}
|
||||
|
||||
@@ -2146,7 +2212,7 @@ void os::pd_print_cpu_info(outputStream* st, char* buf, size_t buflen) {
|
||||
}
|
||||
|
||||
if (same_vals_for_all_cpus && max_mhz != -1) {
|
||||
st->print_cr("Processor Information for all %d processors :", proc_count);
|
||||
st->print_cr("Processor Information for the first %d processors :", proc_count);
|
||||
st->print_cr(" Max Mhz: %d, Current Mhz: %d, Mhz Limit: %d", max_mhz, current_mhz, mhz_limit);
|
||||
return;
|
||||
}
|
||||
@@ -4189,6 +4255,161 @@ bool os::win32::_is_windows_server = false;
|
||||
// including the latest one (as of this writing - Windows Server 2012 R2)
|
||||
bool os::win32::_has_exit_bug = true;
|
||||
|
||||
int os::win32::_major_version = 0;
|
||||
int os::win32::_minor_version = 0;
|
||||
int os::win32::_build_number = 0;
|
||||
int os::win32::_build_minor = 0;
|
||||
|
||||
bool os::win32::_processor_group_warning_displayed = false;
|
||||
bool os::win32::_job_object_processor_group_warning_displayed = false;
|
||||
|
||||
void os::win32::initialize_windows_version() {
|
||||
assert(_major_version == 0, "windows version already initialized.");
|
||||
|
||||
VS_FIXEDFILEINFO *file_info;
|
||||
TCHAR kernel32_path[MAX_PATH];
|
||||
UINT len, ret;
|
||||
char error_msg_buffer[512];
|
||||
|
||||
// Get the full path to \Windows\System32\kernel32.dll and use that for
|
||||
// determining what version of Windows we're running on.
|
||||
len = MAX_PATH - (UINT)strlen("\\kernel32.dll") - 1;
|
||||
ret = GetSystemDirectory(kernel32_path, len);
|
||||
if (ret == 0 || ret > len) {
|
||||
size_t buf_len = os::lasterror(error_msg_buffer, sizeof(error_msg_buffer));
|
||||
warning("Attempt to determine system directory failed: %s", buf_len != 0 ? error_msg_buffer : "<unknown error>");
|
||||
return;
|
||||
}
|
||||
strncat(kernel32_path, "\\kernel32.dll", MAX_PATH - ret);
|
||||
|
||||
DWORD version_size = GetFileVersionInfoSize(kernel32_path, nullptr);
|
||||
if (version_size == 0) {
|
||||
size_t buf_len = os::lasterror(error_msg_buffer, sizeof(error_msg_buffer));
|
||||
warning("Failed to determine whether the OS can retrieve version information from kernel32.dll: %s", buf_len != 0 ? error_msg_buffer : "<unknown error>");
|
||||
return;
|
||||
}
|
||||
|
||||
LPTSTR version_info = (LPTSTR)os::malloc(version_size, mtInternal);
|
||||
if (version_info == nullptr) {
|
||||
warning("os::malloc() failed to allocate %ld bytes for GetFileVersionInfo buffer", version_size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetFileVersionInfo(kernel32_path, 0, version_size, version_info) == 0) {
|
||||
os::free(version_info);
|
||||
size_t buf_len = os::lasterror(error_msg_buffer, sizeof(error_msg_buffer));
|
||||
warning("Attempt to retrieve version information from kernel32.dll failed: %s", buf_len != 0 ? error_msg_buffer : "<unknown error>");
|
||||
return;
|
||||
}
|
||||
|
||||
if (VerQueryValue(version_info, TEXT("\\"), (LPVOID*)&file_info, &len) == 0) {
|
||||
os::free(version_info);
|
||||
size_t buf_len = os::lasterror(error_msg_buffer, sizeof(error_msg_buffer));
|
||||
warning("Attempt to determine Windows version from kernel32.dll failed: %s", buf_len != 0 ? error_msg_buffer : "<unknown error>");
|
||||
return;
|
||||
}
|
||||
|
||||
_major_version = HIWORD(file_info->dwProductVersionMS);
|
||||
_minor_version = LOWORD(file_info->dwProductVersionMS);
|
||||
_build_number = HIWORD(file_info->dwProductVersionLS);
|
||||
_build_minor = LOWORD(file_info->dwProductVersionLS);
|
||||
|
||||
os::free(version_info);
|
||||
}
|
||||
|
||||
bool os::win32::is_windows_11_or_greater() {
|
||||
if (IsWindowsServer()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Windows 11 starts at build 22000 (Version 21H2)
|
||||
return (windows_major_version() == 10 && windows_build_number() >= 22000) || (windows_major_version() > 10);
|
||||
}
|
||||
|
||||
bool os::win32::is_windows_server_2022_or_greater() {
|
||||
if (!IsWindowsServer()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Windows Server 2022 starts at build 20348.169
|
||||
return (windows_major_version() == 10 && windows_build_number() >= 20348) || (windows_major_version() > 10);
|
||||
}
|
||||
|
||||
DWORD os::win32::active_processors_in_job_object(DWORD* active_processor_groups) {
|
||||
if (active_processor_groups != nullptr) {
|
||||
*active_processor_groups = 0;
|
||||
}
|
||||
BOOL is_in_job_object = false;
|
||||
if (IsProcessInJob(GetCurrentProcess(), nullptr, &is_in_job_object) == 0) {
|
||||
char buf[512];
|
||||
size_t buf_len = os::lasterror(buf, sizeof(buf));
|
||||
warning("Attempt to determine whether the process is running in a job failed: %s", buf_len != 0 ? buf : "<unknown error>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!is_in_job_object) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD processors = 0;
|
||||
|
||||
LPVOID job_object_information = nullptr;
|
||||
DWORD job_object_information_length = 0;
|
||||
|
||||
if (QueryInformationJobObject(nullptr, JobObjectGroupInformationEx, nullptr, 0, &job_object_information_length) != 0) {
|
||||
warning("Unexpected QueryInformationJobObject success result.");
|
||||
assert(false, "Unexpected QueryInformationJobObject success result");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD last_error = GetLastError();
|
||||
if (last_error == ERROR_INSUFFICIENT_BUFFER) {
|
||||
DWORD group_count = job_object_information_length / sizeof(GROUP_AFFINITY);
|
||||
|
||||
job_object_information = os::malloc(job_object_information_length, mtInternal);
|
||||
if (job_object_information != nullptr) {
|
||||
if (QueryInformationJobObject(nullptr, JobObjectGroupInformationEx, job_object_information, job_object_information_length, &job_object_information_length) != 0) {
|
||||
DWORD groups_found = job_object_information_length / sizeof(GROUP_AFFINITY);
|
||||
if (groups_found != group_count) {
|
||||
warning("Unexpected processor group count: %ld. Expected %ld processor groups.", groups_found, group_count);
|
||||
assert(false, "Unexpected group count");
|
||||
}
|
||||
|
||||
GROUP_AFFINITY* group_affinity_data = ((GROUP_AFFINITY*)job_object_information);
|
||||
for (DWORD i = 0; i < groups_found; i++, group_affinity_data++) {
|
||||
DWORD processors_in_group = population_count(group_affinity_data->Mask);
|
||||
processors += processors_in_group;
|
||||
if (active_processor_groups != nullptr && processors_in_group > 0) {
|
||||
(*active_processor_groups)++;
|
||||
}
|
||||
}
|
||||
|
||||
if (processors == 0) {
|
||||
warning("Could not determine processor count from the job object.");
|
||||
assert(false, "Must find at least 1 logical processor");
|
||||
}
|
||||
} else {
|
||||
char buf[512];
|
||||
size_t buf_len = os::lasterror(buf, sizeof(buf));
|
||||
warning("Attempt to query job object information failed: %s", buf_len != 0 ? buf : "<unknown error>");
|
||||
}
|
||||
|
||||
os::free(job_object_information);
|
||||
} else {
|
||||
warning("os::malloc() failed to allocate %ld bytes for QueryInformationJobObject", job_object_information_length);
|
||||
}
|
||||
} else {
|
||||
char buf[512];
|
||||
size_t buf_len = os::lasterror(buf, sizeof(buf));
|
||||
warning("Attempt to query job object information failed: %s", buf_len != 0 ? buf : "<unknown error>");
|
||||
assert(false, "Unexpected QueryInformationJobObject error code");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug(os)("Process is running in a job with %d active processors.", processors);
|
||||
return processors;
|
||||
}
|
||||
|
||||
void os::win32::initialize_system_info() {
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
@@ -4196,7 +4417,20 @@ void os::win32::initialize_system_info() {
|
||||
OSInfo::set_vm_allocation_granularity(si.dwAllocationGranularity);
|
||||
_processor_type = si.dwProcessorType;
|
||||
_processor_level = si.wProcessorLevel;
|
||||
set_processor_count(si.dwNumberOfProcessors);
|
||||
|
||||
DWORD processors = 0;
|
||||
bool schedules_all_processor_groups = win32::is_windows_11_or_greater() || win32::is_windows_server_2022_or_greater();
|
||||
if (schedules_all_processor_groups) {
|
||||
processors = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
|
||||
if (processors == 0) {
|
||||
char buf[512];
|
||||
size_t buf_len = os::lasterror(buf, sizeof(buf));
|
||||
warning("Attempt to determine the processor count from GetActiveProcessorCount() failed: %s", buf_len != 0 ? buf : "<unknown error>");
|
||||
assert(false, "Must find at least 1 logical processor");
|
||||
}
|
||||
}
|
||||
|
||||
set_processor_count(processors > 0 ? processors : si.dwNumberOfProcessors);
|
||||
|
||||
MEMORYSTATUSEX ms;
|
||||
ms.dwLength = sizeof(ms);
|
||||
@@ -4505,6 +4739,7 @@ void nx_check_protection() {
|
||||
void os::init(void) {
|
||||
_initial_pid = _getpid();
|
||||
|
||||
win32::initialize_windows_version();
|
||||
win32::initialize_system_info();
|
||||
win32::setmode_streams();
|
||||
_page_sizes.add(os::vm_page_size());
|
||||
@@ -4551,6 +4786,12 @@ size_t os::_os_min_stack_allowed = 64 * K;
|
||||
|
||||
// this is called _after_ the global arguments have been parsed
|
||||
jint os::init_2(void) {
|
||||
const char* auto_schedules_message = "Host Windows OS automatically schedules threads across all processor groups.";
|
||||
const char* no_auto_schedules_message = "Host Windows OS does not automatically schedule threads across all processor groups.";
|
||||
|
||||
bool schedules_all_processor_groups = win32::is_windows_11_or_greater() || win32::is_windows_server_2022_or_greater();
|
||||
log_debug(os)(schedules_all_processor_groups ? auto_schedules_message : no_auto_schedules_message);
|
||||
log_debug(os)("%d logical processors found.", processor_count());
|
||||
|
||||
// This could be set any time but all platforms
|
||||
// have to set it the same so we have to mirror Solaris.
|
||||
|
||||
@@ -43,6 +43,13 @@ class os::win32 {
|
||||
static julong _physical_memory;
|
||||
static bool _is_windows_server;
|
||||
static bool _has_exit_bug;
|
||||
static bool _processor_group_warning_displayed;
|
||||
static bool _job_object_processor_group_warning_displayed;
|
||||
|
||||
static int _major_version;
|
||||
static int _minor_version;
|
||||
static int _build_number;
|
||||
static int _build_minor;
|
||||
|
||||
static void print_windows_version(outputStream* st);
|
||||
static void print_uptime_info(outputStream* st);
|
||||
@@ -56,6 +63,37 @@ class os::win32 {
|
||||
// Windows-specific interface:
|
||||
static void initialize_system_info();
|
||||
static void setmode_streams();
|
||||
static bool is_windows_11_or_greater();
|
||||
static bool is_windows_server_2022_or_greater();
|
||||
static int windows_major_version() {
|
||||
assert(_major_version > 0, "windows version not initialized.");
|
||||
return _major_version;
|
||||
}
|
||||
static int windows_minor_version() {
|
||||
assert(_major_version > 0, "windows version not initialized.");
|
||||
return _minor_version;
|
||||
}
|
||||
static int windows_build_number() {
|
||||
assert(_major_version > 0, "windows version not initialized.");
|
||||
return _build_number;
|
||||
}
|
||||
static int windows_build_minor() {
|
||||
assert(_major_version > 0, "windows version not initialized.");
|
||||
return _build_minor;
|
||||
}
|
||||
|
||||
static void set_processor_group_warning_displayed(bool displayed) {
|
||||
_processor_group_warning_displayed = displayed;
|
||||
}
|
||||
static bool processor_group_warning_displayed() {
|
||||
return _processor_group_warning_displayed;
|
||||
}
|
||||
static void set_job_object_processor_group_warning_displayed(bool displayed) {
|
||||
_job_object_processor_group_warning_displayed = displayed;
|
||||
}
|
||||
static bool job_object_processor_group_warning_displayed() {
|
||||
return _job_object_processor_group_warning_displayed;
|
||||
}
|
||||
|
||||
// Processor info as provided by NT
|
||||
static int processor_type() { return _processor_type; }
|
||||
@@ -79,6 +117,8 @@ class os::win32 {
|
||||
static int exit_process_or_thread(Ept what, int exit_code);
|
||||
|
||||
static void initialize_performance_counter();
|
||||
static void initialize_windows_version();
|
||||
static DWORD active_processors_in_job_object(DWORD* active_processor_groups = nullptr);
|
||||
|
||||
public:
|
||||
// Generic interface:
|
||||
|
||||
@@ -703,6 +703,25 @@ TEST_VM(os_windows, reserve_memory_special) {
|
||||
TestReserveMemorySpecial_test();
|
||||
}
|
||||
|
||||
TEST_VM(os_windows, processor_count) {
|
||||
JVMFlag* flag = JVMFlag::find_flag("UseAllWindowsProcessorGroups");
|
||||
EXPECT_NE(flag, nullptr) << "Expected UseAllWindowsProcessorGroups product flag to be available";
|
||||
|
||||
int processors = os::processor_count();
|
||||
EXPECT_GT(processors, 0) << "Expected at least 1 processor";
|
||||
|
||||
int active_processors = os::active_processor_count();
|
||||
EXPECT_GT(active_processors, 0) << "Expected at least 1 active processor";
|
||||
|
||||
bool schedules_all_processor_groups = os::win32::is_windows_11_or_greater() || os::win32::is_windows_server_2022_or_greater();
|
||||
if (schedules_all_processor_groups && UseAllWindowsProcessorGroups) {
|
||||
EXPECT_EQ(active_processors, processors) << "Expected all processors to be active";
|
||||
} else {
|
||||
// active_processors should be at most the number of processors in 1 Windows processor group.
|
||||
EXPECT_LE(active_processors, processors) << "Expected active processors to not exceed available processors";
|
||||
}
|
||||
}
|
||||
|
||||
class ReserveMemorySpecialRunnable : public TestRunnable {
|
||||
public:
|
||||
void runUnitTest() const {
|
||||
|
||||
@@ -152,6 +152,7 @@ tier1_common = \
|
||||
gtest/MetaspaceGtests.java \
|
||||
gtest/LargePageGtests.java \
|
||||
gtest/NMTGtests.java \
|
||||
gtest/WindowsProcessorGroups.java
|
||||
|
||||
tier1_compiler = \
|
||||
:tier1_compiler_1 \
|
||||
|
||||
34
test/hotspot/jtreg/gtest/WindowsProcessorGroups.java
Normal file
34
test/hotspot/jtreg/gtest/WindowsProcessorGroups.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This runs the os related gtests on Windows with all processor groups enabled.
|
||||
*/
|
||||
|
||||
/* @test id=use-all-windows-processor-groups
|
||||
* @summary Run gtests with all Windows processor groups enabled
|
||||
* @library /test/lib
|
||||
* @requires os.family == "windows"
|
||||
* @run main/native GTestWrapper --gtest_filter=os* -XX:+UseAllWindowsProcessorGroups
|
||||
*/
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
public class GetAvailableProcessors {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Runtime.availableProcessors: " + Runtime.getRuntime().availableProcessors());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6942632
|
||||
* @requires os.family == "windows"
|
||||
* @summary This test verifies that OpenJDK can use all available
|
||||
* processors on Windows 11/Windows Server 2022 and later.
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @compile GetAvailableProcessors.java
|
||||
* @run testng TestAvailableProcessors
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class TestAvailableProcessors {
|
||||
|
||||
private static final String totalProcessorCountMessage = "Active processor count across all processor groups: ";
|
||||
private static final String processorCountPerGroupMessage = "Active processors per group: ";
|
||||
private static final String isWindowsServerMessage = "IsWindowsServer: ";
|
||||
|
||||
private static final String runtimeAvailableProcessorsMessage = "Runtime.availableProcessors: ";
|
||||
private static final String osVersionMessage = "OS Version: ";
|
||||
private static final String unsupportedPlatformMessage = "The UseAllWindowsProcessorGroups flag is not supported on this Windows version and will be ignored.";
|
||||
|
||||
private static String getWindowsVersion() throws IOException {
|
||||
String systeminfoPath = "systeminfo.exe";
|
||||
|
||||
var processBuilder = new ProcessBuilder(systeminfoPath);
|
||||
OutputAnalyzer outputAnalyzer = new OutputAnalyzer(processBuilder.start());
|
||||
outputAnalyzer.shouldHaveExitValue(0);
|
||||
outputAnalyzer.shouldContain(osVersionMessage);
|
||||
List<String> lines = outputAnalyzer.stdoutAsLines();
|
||||
|
||||
String osVersion = null;
|
||||
for (var line: lines) {
|
||||
if (line.startsWith(osVersionMessage)) {
|
||||
osVersion = line.substring(osVersionMessage.length()).trim();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Found OS version: " + osVersion);
|
||||
return osVersion;
|
||||
}
|
||||
|
||||
private static boolean getSchedulesAllProcessorGroups(boolean isWindowsServer) throws IOException {
|
||||
String windowsVer = getWindowsVersion();
|
||||
String[] parts = windowsVer.split(" ");
|
||||
String[] versionParts = parts[0].split("\\.");
|
||||
|
||||
if (versionParts.length != 3) {
|
||||
throw new RuntimeException("Unexpected Windows version format.");
|
||||
}
|
||||
|
||||
int major = Integer.parseInt(versionParts[0]);
|
||||
int minor = Integer.parseInt(versionParts[1]);
|
||||
int build = Integer.parseInt(versionParts[2]);
|
||||
|
||||
if (major > 10) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (major < 10) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minor > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isWindowsServer) {
|
||||
return build >= 20348;
|
||||
} else {
|
||||
return build >= 22000;
|
||||
}
|
||||
}
|
||||
|
||||
private static OutputAnalyzer getAvailableProcessorsOutput(boolean productFlagEnabled) throws IOException {
|
||||
String productFlag = productFlagEnabled ? "-XX:+UseAllWindowsProcessorGroups" : "-XX:-UseAllWindowsProcessorGroups";
|
||||
|
||||
ProcessBuilder processBuilder = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
new String[] {productFlag, "GetAvailableProcessors"}
|
||||
);
|
||||
|
||||
var output = new OutputAnalyzer(processBuilder.start());
|
||||
output.shouldHaveExitValue(0);
|
||||
output.shouldContain(runtimeAvailableProcessorsMessage);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private static int getAvailableProcessors(OutputAnalyzer outputAnalyzer) {
|
||||
int runtimeAvailableProcs = 0;
|
||||
List<String> output = outputAnalyzer.stdoutAsLines();
|
||||
|
||||
for (var line: output) {
|
||||
if (line.startsWith(runtimeAvailableProcessorsMessage)) {
|
||||
String runtimeAvailableProcsStr = line.substring(runtimeAvailableProcessorsMessage.length());
|
||||
runtimeAvailableProcs = Integer.parseInt(runtimeAvailableProcsStr);
|
||||
}
|
||||
}
|
||||
|
||||
return runtimeAvailableProcs;
|
||||
}
|
||||
|
||||
private static int getAvailableProcessors(boolean productFlagEnabled) throws IOException {
|
||||
OutputAnalyzer outputAnalyzer = getAvailableProcessorsOutput(productFlagEnabled);
|
||||
return getAvailableProcessors(outputAnalyzer);
|
||||
}
|
||||
|
||||
private static void verifyAvailableProcessorsWithDisabledProductFlag(Set<Integer> processorGroupSizes) throws IOException {
|
||||
boolean productFlagEnabled = false;
|
||||
int runtimeAvailableProcs = getAvailableProcessors(productFlagEnabled);
|
||||
|
||||
String error = String.format("Runtime.availableProcessors (%d) is not a valid processor group size on this machine.", runtimeAvailableProcs);
|
||||
Assert.assertTrue(processorGroupSizes.contains(runtimeAvailableProcs), error);
|
||||
}
|
||||
|
||||
private static void verifyAvailableProcessorsWithEnabledProductFlag(boolean schedulesAllProcessorGroups, int totalProcessorCount, Set<Integer> processorGroupSizes) throws IOException {
|
||||
boolean productFlagEnabled = true;
|
||||
|
||||
OutputAnalyzer outputAnalyzer = getAvailableProcessorsOutput(productFlagEnabled);
|
||||
int runtimeAvailableProcs = getAvailableProcessors(outputAnalyzer);
|
||||
|
||||
if (schedulesAllProcessorGroups) {
|
||||
String error = String.format("Runtime.availableProcessors (%d) is not equal to the expected total processor count (%d)", runtimeAvailableProcs, totalProcessorCount);
|
||||
Assert.assertEquals(runtimeAvailableProcs, totalProcessorCount, error);
|
||||
} else {
|
||||
outputAnalyzer.shouldContain(unsupportedPlatformMessage);
|
||||
|
||||
String error = String.format("Runtime.availableProcessors (%d) is not a valid processor group size on this machine.", runtimeAvailableProcs);
|
||||
Assert.assertTrue(processorGroupSizes.contains(runtimeAvailableProcs), error);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
private static void testProcessorAvailability() throws IOException {
|
||||
// Launch GetProcessorInfo.exe to gather processor counts
|
||||
Path nativeGetProcessorInfo = Paths.get(Utils.TEST_NATIVE_PATH)
|
||||
.resolve("GetProcessorInfo.exe")
|
||||
.toAbsolutePath();
|
||||
|
||||
var processBuilder = new ProcessBuilder(nativeGetProcessorInfo.toString());
|
||||
var outputAnalyzer= new OutputAnalyzer(processBuilder.start());
|
||||
outputAnalyzer.shouldHaveExitValue(0);
|
||||
outputAnalyzer.shouldContain(totalProcessorCountMessage);
|
||||
outputAnalyzer.shouldContain(processorCountPerGroupMessage);
|
||||
outputAnalyzer.shouldContain(isWindowsServerMessage);
|
||||
|
||||
int totalProcessorCount = 0;
|
||||
boolean isWindowsServer = false;
|
||||
var processorGroupSizes = new HashSet<Integer>();
|
||||
|
||||
List<String> lines = outputAnalyzer.stdoutAsLines();
|
||||
|
||||
for (var line: lines) {
|
||||
if (line.startsWith(totalProcessorCountMessage)) {
|
||||
String totalProcessorCountStr = line.substring(totalProcessorCountMessage.length());
|
||||
totalProcessorCount = Integer.parseInt(totalProcessorCountStr);
|
||||
} else if (line.startsWith(processorCountPerGroupMessage)) {
|
||||
String processorCountPerGroupStr = line.substring(processorCountPerGroupMessage.length());
|
||||
String[] processorCountsPerGroup = processorCountPerGroupStr.split(",");
|
||||
|
||||
for (var processorCountStr: processorCountsPerGroup) {
|
||||
int processorCount = Integer.parseInt(processorCountStr);
|
||||
processorGroupSizes.add(processorCount);
|
||||
}
|
||||
} else if (line.startsWith(isWindowsServerMessage)) {
|
||||
String isWindowsServerStr = line.substring(isWindowsServerMessage.length());
|
||||
isWindowsServer = Integer.parseInt(isWindowsServerStr) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Launch java without the start command and with the product flag disabled
|
||||
verifyAvailableProcessorsWithDisabledProductFlag(processorGroupSizes);
|
||||
|
||||
// Launch java without the start command and with the product flag enabled
|
||||
boolean schedulesAllProcessorGroups = getSchedulesAllProcessorGroups(isWindowsServer);
|
||||
verifyAvailableProcessorsWithEnabledProductFlag(schedulesAllProcessorGroups, totalProcessorCount, processorGroupSizes);
|
||||
}
|
||||
}
|
||||
77
test/hotspot/jtreg/runtime/os/windows/exeGetProcessorInfo.c
Normal file
77
test/hotspot/jtreg/runtime/os/windows/exeGetProcessorInfo.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <Windows.h>
|
||||
#include <malloc.h>
|
||||
#include <versionhelpers.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
DWORD active_processor_count = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
|
||||
if (active_processor_count == 0) {
|
||||
printf("GetActiveProcessorCount failed with error: %x\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("IsWindowsServer: %d\n", IsWindowsServer() ? 1 : 0);
|
||||
printf("Active processor count across all processor groups: %d\n", active_processor_count);
|
||||
|
||||
USHORT group_count = 0;
|
||||
|
||||
if (GetProcessGroupAffinity(GetCurrentProcess(), &group_count, NULL) == 0) {
|
||||
DWORD last_error = GetLastError();
|
||||
if (last_error == ERROR_INSUFFICIENT_BUFFER) {
|
||||
if (group_count == 0) {
|
||||
printf("Unexpected group count of 0 from GetProcessGroupAffinity.\n");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
printf("GetActiveProcessorCount failed with error: %x\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
printf("Unexpected GetProcessGroupAffinity success result.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
PUSHORT group_array = (PUSHORT)malloc(group_count * sizeof(USHORT));
|
||||
if (group_array == NULL) {
|
||||
printf("malloc failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Active processors per group: ");
|
||||
for (USHORT i=0; i < group_count; i++) {
|
||||
DWORD active_processors_in_group = GetActiveProcessorCount(i);
|
||||
if (active_processors_in_group == 0) {
|
||||
printf("GetActiveProcessorCount(%d) failed with error: %x\n", i, GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("%d,", active_processors_in_group);
|
||||
}
|
||||
|
||||
free(group_array);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user