1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
|
/*
* 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, can’t 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, can’t 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
|