jdk-24/test/hotspot/gtest/runtime/test_os_windows.cpp
2024-10-04 04:50:54 +00:00

847 lines
33 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2018, 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 "precompiled.hpp"
#ifdef _WINDOWS
#include "logging/log.hpp"
#include "runtime/flags/flagSetting.hpp"
#include "runtime/globals_extension.hpp"
#include "runtime/os.hpp"
#include "concurrentTestRunner.inline.hpp"
#include "unittest.hpp"
namespace {
class MemoryReleaser {
char* const _ptr;
const size_t _size;
public:
MemoryReleaser(char* ptr, size_t size) : _ptr(ptr), _size(size) { }
~MemoryReleaser() {
if (_ptr != nullptr) {
os::release_memory_special(_ptr, _size);
}
}
};
}
// test tries to allocate memory in a single contiguous memory block at a particular address.
// The test first tries to find a good approximate address to allocate at by using the same
// method to allocate some memory at any address. The test then tries to allocate memory in
// the vicinity (not directly after it to avoid possible by-chance use of that location)
// This is of course only some dodgy assumption, there is no guarantee that the vicinity of
// the previously allocated memory is available for allocation. The only actual failure
// that is reported is when the test tries to allocate at a particular location but gets a
// different valid one. A nullptr return value at this point is not considered an error but may
// be legitimate.
void TestReserveMemorySpecial_test() {
if (!UseLargePages) {
return;
}
// set globals to make sure we hit the correct code path
AutoSaveRestore<bool> FLAG_GUARD(UseLargePagesIndividualAllocation);
AutoSaveRestore<bool> FLAG_GUARD(UseNUMAInterleaving);
FLAG_SET_CMDLINE(UseLargePagesIndividualAllocation, false);
FLAG_SET_CMDLINE(UseNUMAInterleaving, false);
const size_t large_allocation_size = os::large_page_size() * 4;
char* result = os::reserve_memory_special(large_allocation_size, os::large_page_size(), os::large_page_size(), nullptr, false);
if (result == nullptr) {
// failed to allocate memory, skipping the test
return;
}
MemoryReleaser m1(result, large_allocation_size);
// Reserve another page within the recently allocated memory area. This should fail
const size_t expected_allocation_size = os::large_page_size();
char* expected_location = result + os::large_page_size();
char* actual_location = os::reserve_memory_special(expected_allocation_size, os::large_page_size(), os::large_page_size(), expected_location, false);
EXPECT_TRUE(actual_location == nullptr) << "Should not be allowed to reserve within present reservation";
// Instead try reserving after the first reservation.
expected_location = result + large_allocation_size;
actual_location = os::reserve_memory_special(expected_allocation_size, os::large_page_size(), os::large_page_size(), expected_location, false);
EXPECT_TRUE(actual_location != nullptr) << "Unexpected reservation failure, cant verify correct location";
EXPECT_TRUE(actual_location == expected_location) << "Reservation must be at requested location";
MemoryReleaser m2(actual_location, os::large_page_size());
// Now try to do a reservation with a larger alignment.
const size_t alignment = os::large_page_size() * 2;
const size_t new_large_size = alignment * 4;
char* aligned_request = os::reserve_memory_special(new_large_size, alignment, os::large_page_size(), nullptr, false);
EXPECT_TRUE(aligned_request != nullptr) << "Unexpected reservation failure, cant verify correct alignment";
EXPECT_TRUE(is_aligned(aligned_request, alignment)) << "Returned address must be aligned";
MemoryReleaser m3(aligned_request, new_large_size);
}
// The types of path modifications we randomly apply to a path. They should not change the file designated by the path.
enum ModsFilter {
Allow_None = 0, // No modifications
Allow_Sep_Mods = 1, // Replace '\\' by any sequence of '/' or '\\' or at least length 1.
Allow_Dot_Path = 2, // Add /. segments at random positions
Allow_Dot_Dot_Path = 4, // Add /../<correct-dir> segments at random positions.
Allow_All = Allow_Sep_Mods | Allow_Dot_Path | Allow_Dot_Dot_Path
};
// The mode in which to run.
enum Mode {
TEST, // Runs the test. This is the normal modus.
EXAMPLES, // Runs example which document the behaviour of the Windows system calls.
BENCH // Runs a small benchmark which tries to show the costs of using the *W variants/_wfullpath.
};
// Parameters of the test.
static ModsFilter mods_filter = Allow_All;
static int mods_per_path = 50; // The number of variants of a path we try.
static Mode mode = TEST;
// Utility methods
static void get_current_dir_w(wchar_t* path, size_t size) {
DWORD count = GetCurrentDirectoryW((DWORD) size, path);
EXPECT_GT((int) count, 0) << "Failed to get current directory: " << GetLastError();
EXPECT_LT((size_t) count, size) << "Buffer too small for current directory: " << size;
}
#define WITH_ABS_PATH(path) \
wchar_t abs_path[JVM_MAXPATHLEN]; \
wchar_t cwd[JVM_MAXPATHLEN]; \
get_current_dir_w(cwd, JVM_MAXPATHLEN); \
wsprintfW(abs_path, L"\\\\?\\%ls\\%ls", cwd, (path))
static bool file_exists_w(const wchar_t* path) {
WIN32_FILE_ATTRIBUTE_DATA file_data;
return ::GetFileAttributesExW(path, GetFileExInfoStandard, &file_data);
}
static void create_rel_directory_w(const wchar_t* path) {
WITH_ABS_PATH(path);
EXPECT_FALSE(file_exists_w(abs_path)) << "Can't create directory: \"" << path << "\" already exists";
BOOL result = CreateDirectoryW(abs_path, nullptr);
EXPECT_TRUE(result) << "Failed to create directory \"" << path << "\" " << GetLastError();
}
static void delete_empty_rel_directory_w(const wchar_t* path) {
WITH_ABS_PATH(path);
EXPECT_TRUE(file_exists_w(abs_path)) << "Can't delete directory: \"" << path << "\" does not exists";
const int retry_count = 20;
// If the directory cannot be deleted directly, a file in it might be kept
// open by a virus scanner. Try a few times, since this should be temporary.
for (int i = 0; i <= retry_count; ++i) {
BOOL result = RemoveDirectoryW(abs_path);
if (!result && (i < retry_count)) {
Sleep(1);
} else {
EXPECT_TRUE(result) << "Failed to delete directory \"" << path << "\": " << GetLastError();
return;
}
}
}
static void create_rel_file_w(const wchar_t* path) {
WITH_ABS_PATH(path);
EXPECT_FALSE(file_exists_w(abs_path)) << "Can't create file: \"" << path << "\" already exists";
HANDLE h = CreateFileW(abs_path, 0, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
EXPECT_NE(h, INVALID_HANDLE_VALUE) << "Failed to create file \"" << path << "\": " << GetLastError();
CloseHandle(h);
}
static void delete_rel_file_w(const wchar_t* path) {
WITH_ABS_PATH(path);
EXPECT_TRUE(file_exists_w(abs_path)) << "Can't delete file: \"" << path << "\" does not exists";
BOOL result = DeleteFileW(abs_path);
EXPECT_TRUE(result) << "Failed to delete file \"" << path << "\": " << GetLastError();
}
static bool convert_to_cstring(char* c_str, size_t size, const wchar_t* w_str) {
size_t converted;
errno_t err = wcstombs_s(&converted, c_str, size, w_str, size - 1);
EXPECT_EQ(err, ERROR_SUCCESS) << "Could not convert \"" << w_str << "\" to c-string";
return err == ERROR_SUCCESS;
}
static wchar_t* my_wcscpy_s(wchar_t* dest, size_t size, wchar_t* start, const wchar_t* to_copy) {
size_t already_used = dest - start;
size_t len = wcslen(to_copy);
if (already_used + len < size) {
wcscpy_s(dest, size - already_used, to_copy);
}
return dest + wcslen(to_copy);
}
// The currently finite list of seperator sequences we might use instead of '\\'.
static const wchar_t* sep_replacements[] = {
L"\\", L"\\/", L"/", L"//", L"\\\\/\\", L"//\\/"
};
// Takes a path and modifies it in a way that it should still designate the same file.
static bool unnormalize_path(wchar_t* result, size_t size, bool is_dir, const wchar_t* path) {
wchar_t* dest = result;
const wchar_t* src = path;
const wchar_t* path_start;
if (wcsncmp(src, L"\\\\?\\UNC\\", 8) == 0) {
path_start = src + 8;
} else if (wcsncmp(src, L"\\\\?\\", 4) == 0) {
if (src[5] == L':') {
path_start = src + 6;
} else {
path_start = wcschr(src + 4, L'\\');
}
} else if (wcsncmp(src, L"\\\\", 2) == 0) {
path_start = wcschr(src + 2, L'?');
if (path_start == nullptr) {
path_start = wcschr(src + 2, L'\\');
} else {
path_start = wcschr(path_start, L'\\');
}
} else {
path_start = wcschr(src + 1, L'\\');
}
bool allow_sep_change = (mods_filter & Allow_Sep_Mods) && (os::random() & 1) == 0;
bool allow_dot_change = (mods_filter & Allow_Dot_Path) && (os::random() & 1) == 0;
bool allow_dotdot_change = (mods_filter & Allow_Dot_Dot_Path) && (os::random() & 1) == 0;
while ((*src != L'\0') && (result + size > dest)) {
wchar_t c = *src;
*dest = c;
++src;
++dest;
if (c == L'\\') {
if (allow_sep_change && (os::random() & 3) == 3) {
int i = os::random() % (sizeof(sep_replacements) / sizeof(sep_replacements[0]));
if (i >= 0) {
const wchar_t* replacement = sep_replacements[i];
dest = my_wcscpy_s(dest - 1, size, result, replacement);
}
} else if (path_start != nullptr) {
if (allow_dotdot_change && (src > path_start + 1) && ((os::random() & 7) == 7)) {
wchar_t const* last_sep = src - 2;
while (last_sep[0] != L'\\') {
--last_sep;
}
if (last_sep > path_start) {
dest = my_wcscpy_s(dest, size, result, L"../");
src = last_sep + 1;
}
} else if (allow_dot_change && (src > path_start + 1) && ((os::random() & 7) == 7)) {
dest = my_wcscpy_s(dest, size, result, L"./");
}
}
}
}
while (is_dir && ((os::random() & 15) == 1)) {
dest = my_wcscpy_s(dest, size, result, L"/");
}
if (result + size > dest) {
*dest = L'\0';
}
// Use this modification only if not too close to the max size.
return result + size - 10 > dest;
}
static void check_dir_impl(wchar_t* path, bool should_be_empty) {
char buf[JVM_MAXPATHLEN];
if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
struct stat st;
EXPECT_EQ(os::stat(buf, &st), 0) << "os::stat failed for \"" << path << "\"";
EXPECT_EQ(st.st_mode & S_IFMT, S_IFDIR) << "\"" << path << "\" is not a directory according to os::stat";
errno = ERROR_SUCCESS;
bool is_empty = os::dir_is_empty(buf);
errno_t err = errno;
EXPECT_EQ(is_empty, should_be_empty) << "os::dir_is_empty assumed \"" << path << "\" is "
<< (should_be_empty ? "not ": "") << "empty";
EXPECT_EQ(err, ERROR_SUCCESS) << "os::dir_is_empty failed for \"" << path << "\"with errno " << err;
}
}
static void check_file_impl(wchar_t* path) {
char buf[JVM_MAXPATHLEN];
if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
struct stat st;
EXPECT_EQ(os::stat(buf, &st), 0) << "os::stat failed for \"" << path << "\"";
EXPECT_EQ(st.st_mode & S_IFMT, S_IFREG) << "\"" << path << "\" is not a regular file according to os::stat";
int fd = os::open(buf, O_RDONLY, 0);
EXPECT_NE(fd, -1) << "os::open failed for \"" << path << "\" with errno " << errno;
if (fd >= 0) {
::close(fd);
}
}
}
static void check_file_not_present_impl(const wchar_t* path) {
char buf[JVM_MAXPATHLEN];
if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
struct stat st;
int stat_ret;
EXPECT_EQ(stat_ret = os::stat(buf, &st), -1) << "os::stat did not fail for \"" << path << "\"";
if (stat_ret != -1) {
// Only check open if stat not already failed.
int fd = os::open(buf, O_RDONLY, 0);
EXPECT_EQ(fd, -1) << "os::open did not fail for \"" << path << "\"";
if (fd >= 0) {
::close(fd);
}
}
}
}
static void check_dir(wchar_t* path, bool should_be_empty) {
check_dir_impl(path, should_be_empty);
for (int i = 0; mods_filter != Allow_None && i < mods_per_path; ++i) {
wchar_t tmp[JVM_MAXPATHLEN];
if (unnormalize_path(tmp, JVM_MAXPATHLEN, true, path)) {
check_dir_impl(tmp, should_be_empty);
}
}
}
static void check_file(wchar_t* path) {
check_file_impl(path);
// Check os::same_files at least somewhat.
char buf[JVM_MAXPATHLEN];
if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
wchar_t mod[JVM_MAXPATHLEN];
if (unnormalize_path(mod, JVM_MAXPATHLEN, false, path)) {
char mod_c[JVM_MAXPATHLEN];
if (convert_to_cstring(mod_c, JVM_MAXPATHLEN, mod)) {
EXPECT_EQ(os::same_files(buf, mod_c), true) << "os::same files failed for \\" << path << "\" and \"" << mod_c << "\"";
}
}
}
for (int i = 0; mods_filter != Allow_None && i < mods_per_path; ++i) {
wchar_t tmp[JVM_MAXPATHLEN];
if (unnormalize_path(tmp, JVM_MAXPATHLEN, false, path)) {
check_file_impl(tmp);
}
}
}
static void check_file_not_present(const wchar_t* path) {
check_file_not_present_impl(path);
for (int i = 0; mods_filter != Allow_None && i < mods_per_path; ++i) {
wchar_t tmp[JVM_MAXPATHLEN];
if (unnormalize_path(tmp, JVM_MAXPATHLEN, false, path)) {
check_file_not_present_impl(tmp);
}
}
}
static void record_path(char const* name, char const* len_name, wchar_t* path) {
char buf[JVM_MAXPATHLEN];
if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
::testing::Test::RecordProperty(name, buf);
os::snprintf(buf, JVM_MAXPATHLEN, "%d", (int) wcslen(path));
::testing::Test::RecordProperty(len_name, buf);
}
}
static void bench_path(wchar_t* path) {
char buf[JVM_MAXPATHLEN];
int reps = 100000;
if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
jlong wtime[2];
for (int t = 0; t < 2; ++t) {
wtime[t] = os::javaTimeNanos();
for (int i = 0; i < reps; ++i) {
bool succ = false;
size_t buf_len = strlen(buf);
wchar_t* w_path = (wchar_t*) os::malloc(sizeof(wchar_t) * (buf_len + 1), mtInternal);
if (w_path != nullptr) {
size_t converted_chars;
if (::mbstowcs_s(&converted_chars, w_path, buf_len + 1, buf, buf_len) == ERROR_SUCCESS) {
if (t == 1) {
wchar_t* tmp = (wchar_t*) os::malloc(sizeof(wchar_t) * JVM_MAXPATHLEN, mtInternal);
if (tmp) {
if (_wfullpath(tmp, w_path, JVM_MAXPATHLEN)) {
succ = true;
}
// Note that we really don't use the full path name, but just add the cost of running _wfullpath.
os::free(tmp);
}
if (!succ) {
printf("Failed fullpathing \"%s\"\n", buf);
return;
}
succ = false;
}
HANDLE h = ::CreateFileW(w_path, 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
if (h != INVALID_HANDLE_VALUE) {
::CloseHandle(h);
succ = true;
}
}
}
os::free(w_path);
if (!succ) {
printf("Failed getting W*attr. \"%s\"\n", buf);
return;
}
}
wtime[t] = os::javaTimeNanos() - wtime[t];
}
jlong ctime = os::javaTimeNanos();
for (int i = 0; i < reps; ++i) {
HANDLE h = ::CreateFileA(buf, 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
if (h == INVALID_HANDLE_VALUE) {
return;
}
::CloseHandle(h);
}
ctime = os::javaTimeNanos() - ctime;
printf("\"%s\" %f us for *A, %f us for *W, %f us for *W with fullpath\n", buf,
0.001 * ctime / reps, 0.001 * wtime[0] / reps, 0.001 * wtime[1] / reps);
}
}
static void print_attr_result_for_path(const wchar_t* path) {
WIN32_FILE_ATTRIBUTE_DATA file_data;
struct stat st;
char buf[JVM_MAXPATHLEN];
wchar_t abs[JVM_MAXPATHLEN];
_wfullpath(abs, path, JVM_MAXPATHLEN);
printf("Checking \"%ls\" (%d chars):\n", path, (int) wcslen(path));
printf("_wfullpath %ls (%d chars)\n", abs, (int) wcslen(abs));
BOOL bret = ::GetFileAttributesExW(path, GetFileExInfoStandard, &file_data);
printf("GetFileAttributesExW() %s\n", bret ? "success" : "failed");
if (convert_to_cstring(buf, JVM_MAXPATHLEN, path)) {
bret = ::GetFileAttributesExA(buf, GetFileExInfoStandard, &file_data);
printf("GetFileAttributesExA() %s\n", bret ? "success" : "failed");
bool succ = os::stat(buf, &st) != -1;
printf("os::stat() %s\n", succ ? "success" : "failed");
}
}
static void print_attr_result(const wchar_t* format, ...) {
va_list argptr;
wchar_t buf[JVM_MAXPATHLEN];
va_start(argptr, format);
wvsprintfW(buf, format, argptr);
print_attr_result_for_path(buf);
va_end(argptr);
}
#define RECORD_PATH(name) record_path(#name, #name "Len", name)
#define NAME_PART_50 L"01234567890123456789012345678901234567890123456789"
#define NAME_PART_250 NAME_PART_50 NAME_PART_50 NAME_PART_50 NAME_PART_50 NAME_PART_50
// Test which tries to find out if the os::stat, os::open, os::same_files and os::dir_is_empty methods
// can handle long path names correctly.
TEST_VM(os_windows, handle_long_paths) {
static wchar_t cwd[JVM_MAXPATHLEN];
static wchar_t nearly_long_rel_path[JVM_MAXPATHLEN];
static wchar_t long_rel_path[JVM_MAXPATHLEN];
static wchar_t empty_dir_rel_path[JVM_MAXPATHLEN];
static wchar_t not_empty_dir_rel_path[JVM_MAXPATHLEN];
static wchar_t file_rel_path[JVM_MAXPATHLEN];
static wchar_t nearly_long_file_rel_path[JVM_MAXPATHLEN];
static wchar_t nearly_long_path[JVM_MAXPATHLEN];
static wchar_t empty_dir_path[JVM_MAXPATHLEN];
static wchar_t not_empty_dir_path[JVM_MAXPATHLEN];
static wchar_t nearly_long_file_path[JVM_MAXPATHLEN];
static wchar_t file_path[JVM_MAXPATHLEN];
static wchar_t nearly_long_unc_path[JVM_MAXPATHLEN];
static wchar_t empty_dir_unc_path[JVM_MAXPATHLEN];
static wchar_t not_empty_dir_unc_path[JVM_MAXPATHLEN];
static wchar_t nearly_long_file_unc_path[JVM_MAXPATHLEN];
static wchar_t file_unc_path[JVM_MAXPATHLEN];
static wchar_t root_dir_path[JVM_MAXPATHLEN];
static wchar_t root_rel_dir_path[JVM_MAXPATHLEN];
const wchar_t* dir_prefix = L"os_windows_long_paths_dir_";
const wchar_t* empty_dir_name = L"empty_directory_with_long_path";
const wchar_t* not_empty_dir_name = L"not_empty_directory_with_long_path";
const wchar_t* file_name = L"file";
wchar_t dir_letter;
WIN32_FILE_ATTRIBUTE_DATA file_data;
bool can_test_unc = false;
get_current_dir_w(cwd, sizeof(cwd) / sizeof(wchar_t));
dir_letter = (cwd[1] == L':' ? cwd[0] : L'\0');
int cwd_len = (int) wcslen(cwd);
int dir_prefix_len = (int) wcslen(dir_prefix);
int rel_path_len = MAX2(dir_prefix_len, 235 - cwd_len);
memcpy(nearly_long_rel_path, dir_prefix, sizeof(wchar_t) * dir_prefix_len);
for (int i = dir_prefix_len; i < rel_path_len; ++i) {
nearly_long_rel_path[i] = L'L';
}
nearly_long_rel_path[rel_path_len] = L'\0';
wsprintfW(long_rel_path, L"%ls\\%ls", nearly_long_rel_path, NAME_PART_250);
wsprintfW(empty_dir_rel_path, L"%ls\\%ls", nearly_long_rel_path, empty_dir_name);
wsprintfW(not_empty_dir_rel_path, L"%ls\\%ls", nearly_long_rel_path, not_empty_dir_name);
wsprintfW(nearly_long_file_rel_path, L"%ls\\%ls", nearly_long_rel_path, file_name);
wsprintfW(file_rel_path, L"%ls\\%ls\\%ls", nearly_long_rel_path, not_empty_dir_name, file_name);
wsprintfW(nearly_long_path, L"\\\\?\\%ls\\%ls", cwd, nearly_long_rel_path);
wsprintfW(empty_dir_path, L"%ls\\%ls", nearly_long_path, empty_dir_name);
wsprintfW(not_empty_dir_path, L"%ls\\%ls", nearly_long_path, not_empty_dir_name);
wsprintfW(nearly_long_file_path, L"%ls\\%ls", nearly_long_path, file_name);
wsprintfW(file_path, L"%ls\\%ls\\%ls", nearly_long_path, not_empty_dir_name, file_name);
wsprintfW(nearly_long_unc_path, L"\\\\localhost\\%lc$\\%s", dir_letter, nearly_long_path + 7);
wsprintfW(empty_dir_unc_path, L"%s\\%s", nearly_long_unc_path, empty_dir_name);
wsprintfW(not_empty_dir_unc_path, L"%s\\%s", nearly_long_unc_path, not_empty_dir_name);
wsprintfW(nearly_long_file_unc_path, L"%ls\\%ls", nearly_long_unc_path, file_name);
wsprintfW(file_unc_path, L"%s\\%s\\%s", nearly_long_unc_path, not_empty_dir_name, file_name);
wsprintfW(root_dir_path, L"%lc:\\", dir_letter);
wsprintfW(root_rel_dir_path, L"%lc:", dir_letter);
RECORD_PATH(long_rel_path);
RECORD_PATH(nearly_long_rel_path);
RECORD_PATH(nearly_long_path);
RECORD_PATH(nearly_long_unc_path);
RECORD_PATH(empty_dir_rel_path);
RECORD_PATH(empty_dir_path);
RECORD_PATH(empty_dir_unc_path);
RECORD_PATH(not_empty_dir_rel_path);
RECORD_PATH(not_empty_dir_path);
RECORD_PATH(not_empty_dir_unc_path);
RECORD_PATH(nearly_long_file_rel_path);
RECORD_PATH(nearly_long_file_path);
RECORD_PATH(nearly_long_file_unc_path);
RECORD_PATH(file_rel_path);
RECORD_PATH(file_path);
RECORD_PATH(file_unc_path);
create_rel_directory_w(nearly_long_rel_path);
create_rel_directory_w(long_rel_path);
create_rel_directory_w(empty_dir_rel_path);
create_rel_directory_w(not_empty_dir_rel_path);
create_rel_file_w(nearly_long_file_rel_path);
create_rel_file_w(file_rel_path);
// For UNC path test we assume that the current DRIVE has a share
// called "<DRIVELETTER>$" (so for D: we expect \\localhost\D$ to be
// the same). Since this is only an assumption, we have to skip
// the UNC tests if the share is missing.
if (dir_letter && !::GetFileAttributesExW(nearly_long_unc_path, GetFileExInfoStandard, &file_data)) {
printf("Disabled UNC path test, since %lc: is not mapped as share %lc$.\n", dir_letter, dir_letter);
} else {
can_test_unc = true;
}
if (mode == BENCH) {
bench_path(nearly_long_path + 4);
bench_path(nearly_long_rel_path);
bench_path(nearly_long_file_path + 4);
bench_path(nearly_long_file_rel_path);
} else if (mode == EXAMPLES) {
printf("Working directory: %ls", cwd);
if (dir_letter) {
static wchar_t top_buf[JVM_MAXPATHLEN];
wchar_t* top_path = wcschr(cwd + 3, L'\\');
if (top_path) {
size_t top_len = (top_path - cwd) - 3;
memcpy(top_buf, cwd + 3, top_len * 2);
top_buf[top_len] = L'\0';
top_path = top_buf;
}
print_attr_result(L"%lc:\\", dir_letter);
print_attr_result(L"%lc:\\.\\", dir_letter);
if (top_path) {
print_attr_result(L"%lc:\\%ls\\..\\%ls\\", dir_letter, top_path, top_path);
}
print_attr_result(L"%lc:", dir_letter);
print_attr_result(L"%lc:.", dir_letter);
print_attr_result(L"%lc:\\COM1", dir_letter);
print_attr_result(L"%lc:\\PRN", dir_letter);
print_attr_result(L"%lc:\\PRN\\COM1", dir_letter);
print_attr_result(L"\\\\?\\UNC\\localhost\\%lc$\\", dir_letter);
print_attr_result(L"\\\\?\\UNC\\\\localhost\\%lc$\\", dir_letter);
print_attr_result(nearly_long_unc_path);
print_attr_result(L"%ls\\.\\", nearly_long_unc_path);
print_attr_result(L"%ls\\..\\%ls", nearly_long_unc_path, nearly_long_rel_path);
print_attr_result(L"\\\\?\\UNC\\%ls", nearly_long_unc_path + 2);
print_attr_result(file_unc_path);
print_attr_result(L"%ls\\%ls\\..\\%ls\\%ls", nearly_long_unc_path, not_empty_dir_name, not_empty_dir_name, file_name);
print_attr_result(L"%ls\\%ls\\.\\%ls", nearly_long_unc_path, not_empty_dir_name, file_name);
print_attr_result(L"\\\\?\\UNC\\%ls", file_unc_path + 2);
print_attr_result(L"\\\\?\\UNC\\%ls\\%ls\\.\\%ls", nearly_long_unc_path + 2, not_empty_dir_name, file_name);
print_attr_result(L"\\\\?\\UNC\\%ls\\%ls\\..\\%ls\\%ls", nearly_long_unc_path + 2, not_empty_dir_name, not_empty_dir_name, file_name);
}
print_attr_result(nearly_long_rel_path);
print_attr_result(L"%ls\\.\\", nearly_long_rel_path);
print_attr_result(L"%ls\\..\\%ls", nearly_long_rel_path, nearly_long_rel_path);
print_attr_result(L"%\\\\?\\%ls", nearly_long_rel_path);
print_attr_result(L"\\\\?\\%ls\\.\\", nearly_long_rel_path);
print_attr_result(L"\\\\?\\%ls\\..\\%ls", nearly_long_rel_path, nearly_long_rel_path);
print_attr_result(nearly_long_path + 4);
print_attr_result(L"%ls\\.\\", nearly_long_path + 4);
print_attr_result(L"%ls\\..\\%ls", nearly_long_path + 4, nearly_long_rel_path);
print_attr_result(nearly_long_path);
print_attr_result(L"%ls\\.\\", nearly_long_path);
print_attr_result(L"%ls\\..\\%ls", nearly_long_path, nearly_long_rel_path);
} else {
check_file_not_present(L"");
// Check relative paths
check_dir(nearly_long_rel_path, false);
check_dir(long_rel_path, true);
check_dir(empty_dir_rel_path, true);
check_dir(not_empty_dir_rel_path, false);
check_file(nearly_long_file_rel_path);
check_file(file_rel_path);
// Check absolute paths
if (dir_letter) {
check_dir(root_dir_path, false);
check_dir(root_rel_dir_path, false);
}
check_dir(cwd, false);
check_dir(nearly_long_path + 4, false);
check_dir(empty_dir_path + 4, true);
check_dir(not_empty_dir_path + 4, false);
check_file(nearly_long_file_path + 4);
check_file(file_path + 4);
// Check UNC paths
if (can_test_unc) {
check_dir(nearly_long_unc_path, false);
check_dir(empty_dir_unc_path, true);
check_dir(not_empty_dir_unc_path, false);
check_file(nearly_long_file_unc_path);
check_file(file_unc_path);
}
// Check handling of <DRIVE>:/../<OTHER_DRIVE>:/path/...
// The other drive letter should not overwrite the original one.
if (dir_letter) {
static wchar_t tmp[JVM_MAXPATHLEN];
const wchar_t* other_letter = dir_letter == L'D' ? L"C" : L"D";
wsprintfW(tmp, L"%2ls\\..\\%ls:%ls", nearly_long_file_path, other_letter, nearly_long_file_path + 2);
check_file_not_present(tmp);
wsprintfW(tmp, L"%2ls\\..\\%ls:%ls", file_path, other_letter, file_path + 2);
check_file_not_present(tmp);
}
}
delete_rel_file_w(file_rel_path);
delete_rel_file_w(nearly_long_file_rel_path);
delete_empty_rel_directory_w(not_empty_dir_rel_path);
delete_empty_rel_directory_w(empty_dir_rel_path);
delete_empty_rel_directory_w(long_rel_path);
delete_empty_rel_directory_w(nearly_long_rel_path);
}
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";
}
}
TEST_VM(os_windows, large_page_init_multiple_sizes) {
// Call request_lock_memory_privilege() and check the result
if (!os::win32::request_lock_memory_privilege()) {
GTEST_SKIP() << "Skipping test because lock memory privilege is not granted.";
}
// Set globals to make sure we hit the correct code path
AutoSaveRestore<bool> guardUseLargePages(UseLargePages);
AutoSaveRestore<bool> guardEnableAllLargePageSizesForWindows(EnableAllLargePageSizesForWindows);
AutoSaveRestore<size_t> guardLargePageSizeInBytes(LargePageSizeInBytes);
FLAG_SET_CMDLINE(UseLargePages, true);
FLAG_SET_CMDLINE(EnableAllLargePageSizesForWindows, true);
// Determine the minimum page size
const size_t min_size = GetLargePageMinimum();
// End the test if GetLargePageMinimum returns 0
if (min_size == 0) {
GTEST_SKIP() << "Large pages are not supported on this system.";
return;
}
// Set LargePageSizeInBytes to 4 times the minimum page size
FLAG_SET_CMDLINE(LargePageSizeInBytes, 4 * min_size); // Set a value for multiple page sizes
// Initialize large page settings
os::large_page_init();
// Verify that large pages are enabled
EXPECT_TRUE(UseLargePages) << "UseLargePages should be true after initialization for LargePageSizeInBytes = 4 * min_size";
// Verify that decided_large_page_size is greater than the default page size
const size_t default_page_size = os::vm_page_size();
size_t decided_large_page_size = os::win32::large_page_init_decide_size();
EXPECT_GT(decided_large_page_size, default_page_size) << "Large page size should be greater than the default page size for LargePageSizeInBytes = 4 * min_size";
#if !defined(IA32)
size_t page_size_count = 0;
size_t page_size = os::page_sizes().largest();
do {
++page_size_count;
page_size = os::page_sizes().next_smaller(page_size);
} while (page_size >= os::page_sizes().smallest());
EXPECT_GT(page_size_count, 1u) << "There should be multiple large page sizes available.";
size_t large_page_size = decided_large_page_size;
for (size_t page_size = os::page_sizes().largest(); page_size >= min_size; page_size = os::page_sizes().next_smaller(page_size)) {
EXPECT_TRUE(page_size % min_size == 0) << "Each page size should be a multiple of the minimum large page size.";
EXPECT_LE(page_size, large_page_size) << "Page size should not exceed the determined large page size.";
}
#endif
}
TEST_VM(os_windows, large_page_init_decide_size) {
// Initial setup
// Call request_lock_memory_privilege() and check the result
if (!os::win32::request_lock_memory_privilege()) {
GTEST_SKIP() << "Skipping test because lock memory privilege is not granted.";
}
AutoSaveRestore<bool> guardUseLargePages(UseLargePages);
AutoSaveRestore<size_t> guardLargePageSizeInBytes(LargePageSizeInBytes);
FLAG_SET_CMDLINE(UseLargePages, true);
FLAG_SET_CMDLINE(LargePageSizeInBytes, 0); // Reset to default
// Test for large page support
size_t decided_size = os::win32::large_page_init_decide_size();
size_t min_size = GetLargePageMinimum();
if (min_size == 0) {
EXPECT_EQ(decided_size, 0) << "Expected decided size to be 0 when large page is not supported by the processor";
return;
}
// Scenario 1: Test with 2MB large page size
if (min_size == 2 * M) {
FLAG_SET_CMDLINE(LargePageSizeInBytes, 2 * M); // Set large page size to 2MB
decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size
EXPECT_EQ(decided_size, 2 * M) << "Expected decided size to be 2M when large page and OS reported size are both 2M";
}
// Scenario 2: Test with 1MB large page size
if (min_size == 2 * M) {
FLAG_SET_CMDLINE(LargePageSizeInBytes, 1 * M); // Set large page size to 1MB
decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size
EXPECT_EQ(decided_size, 2 * M) << "Expected decided size to be 2M when large page is 1M and OS reported size is 2M";
}
#if defined(IA32) || defined(AMD64)
FLAG_SET_CMDLINE(LargePageSizeInBytes, 5 * M); // Set large page size to 5MB
if (!EnableAllLargePageSizesForWindows) {
decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size
EXPECT_EQ(decided_size, 0) << "Expected decided size to be 0 for large pages bigger than 4mb on IA32 or AMD64";
}
#endif
// Additional check for non-multiple of minimum size
// Set an arbitrary large page size which is not a multiple of min_size
FLAG_SET_CMDLINE(LargePageSizeInBytes, 5 * min_size + 1);
// Recalculate decided size
decided_size = os::win32::large_page_init_decide_size();
// Assert that the decided size defaults to minimum page size when LargePageSizeInBytes
// is not a multiple of the minimum size, assuming conditions are always met
EXPECT_EQ(decided_size, 0) << "Expected decided size to default to 0 when LargePageSizeInBytes is not a multiple of minimum size";
}
class ReserveMemorySpecialRunnable : public TestRunnable {
public:
void runUnitTest() const {
TestReserveMemorySpecial_test();
}
};
TEST_VM(os_windows, reserve_memory_special_concurrent) {
ReserveMemorySpecialRunnable runnable;
ConcurrentTestRunner testRunner(&runnable, 30, 15000);
testRunner.run();
}
#endif